From 915b6f990167c5840346e6de6be99d88dec40ad0 Mon Sep 17 00:00:00 2001 From: tetektoza Date: Sun, 7 Dec 2025 11:43:09 +0100 Subject: [PATCH] Gui: Fix preferences search popup positioning on Wayland (#25675) * Gui: Fix preferences search popup positioning on Wayland Change window flag from `Qt::Tool` to `Qt::ToolTip` to fix popup appearing in center of screen and closing immediately on Wayland desktop envs. `Qt::ToolTip` uses xdg_popup protocol which provides proper positioning relative to parent window on Wayland. * Gui: Initialize ok check flag to default in prefs * Gui: Use ranged for loop where possible in prefs * Gui: Remove unused variable in preferences controller * Gui: Use const where possible in prefs search dialog * Gui: Use static where possible in search prefs dialog * Gui: Use braced initializer list when returning type in search prefs * Gui: Use auto in search prefs dialog where possible * Gui: Do not use else if after return in search prefs dialog * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/Gui/Dialogs/DlgPreferencesImp.cpp | 73 +++++++++++++-------------- src/Gui/Dialogs/DlgPreferencesImp.h | 14 ++--- 2 files changed, 43 insertions(+), 44 deletions(-) diff --git a/src/Gui/Dialogs/DlgPreferencesImp.cpp b/src/Gui/Dialogs/DlgPreferencesImp.cpp index 19fbc7c19e..73f1cde4e3 100644 --- a/src/Gui/Dialogs/DlgPreferencesImp.cpp +++ b/src/Gui/Dialogs/DlgPreferencesImp.cpp @@ -1276,7 +1276,7 @@ PreferencesSearchController::PreferencesSearchController(DlgPreferencesImp* pare // Create the search results popup list m_searchResultsList = new QListWidget(m_parentDialog); m_searchResultsList->setWindowFlags( - Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint + Qt::ToolTip | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint ); m_searchResultsList->setVisible(false); m_searchResultsList->setMinimumWidth(300); @@ -1435,8 +1435,7 @@ void PreferencesSearchController::performSearch(const QString& searchText) void PreferencesSearchController::clearHighlights() { // Restore original styles for all highlighted widgets - for (int i = 0; i < m_highlightedWidgets.size(); ++i) { - QWidget* widget = m_highlightedWidgets.at(i); + for (auto widget : m_highlightedWidgets) { if (widget && m_originalStyles.contains(widget)) { widget->setStyleSheet(m_originalStyles[widget]); } @@ -1458,8 +1457,6 @@ void PreferencesSearchController::collectSearchResults( return; } - const QString lowerSearchText = searchText.toLower(); - // First, check if the page display name itself matches (highest priority) int pageScore = 0; if (fuzzyMatch(searchText, pageDisplayName, pageScore)) { @@ -1528,7 +1525,7 @@ void PreferencesSearchController::navigateToCurrentSearchResult(PopupAction acti } // Get the result index directly from the item data - bool ok; + bool ok = false; int resultIndex = currentItem->data(Qt::UserRole).toInt(&ok); if (ok && resultIndex >= 0 && resultIndex < m_searchResults.size()) { @@ -1561,7 +1558,7 @@ void PreferencesSearchController::populateSearchResultsList() const SearchResult& result = m_searchResults.at(i); // Create item without setting DisplayRole - QListWidgetItem* item = new QListWidgetItem(); + auto* item = new QListWidgetItem(); // Store path and widget text in separate roles if (result.isPageLevelMatch) { @@ -1612,20 +1609,20 @@ void PreferencesSearchController::showSearchResultsList() QString PreferencesSearchController::findGroupBoxForWidget(QWidget* widget) { if (!widget) { - return QString(); + return {}; } // Walk up the parent hierarchy to find a QGroupBox QWidget* parent = widget->parentWidget(); while (parent) { - QGroupBox* groupBox = qobject_cast(parent); + auto* groupBox = qobject_cast(parent); if (groupBox) { return groupBox->title(); } parent = parent->parentWidget(); } - return QString(); + return {}; } @@ -1725,7 +1722,7 @@ void PreferencesSearchController::searchWidgetType( } } -int PreferencesSearchController::calculatePopupHeight(int popupWidth) +int PreferencesSearchController::calculatePopupHeight(int popupWidth) const { int totalHeight = 0; int itemCount = m_searchResultsList->count(); @@ -1907,28 +1904,31 @@ void PreferencesSearchController::ensureSearchBoxFocus() QString PreferencesSearchController::getHighlightStyleForWidget(QWidget* widget) { - const QString baseStyle = QStringLiteral( + const auto baseStyle = QStringLiteral( "background-color: #E3F2FD; color: #1565C0; border: 2px solid #2196F3; border-radius: 3px;" ); if (qobject_cast(widget)) { return QStringLiteral("QLabel { ") + baseStyle + QStringLiteral(" padding: 2px; }"); } - else if (qobject_cast(widget)) { + + if (qobject_cast(widget)) { return QStringLiteral("QCheckBox { ") + baseStyle + QStringLiteral(" padding: 2px; }"); } - else if (qobject_cast(widget)) { + + if (qobject_cast(widget)) { return QStringLiteral("QRadioButton { ") + baseStyle + QStringLiteral(" padding: 2px; }"); } - else if (qobject_cast(widget)) { + + if (qobject_cast(widget)) { return QStringLiteral("QGroupBox::title { ") + baseStyle + QStringLiteral(" padding: 2px; }"); } - else if (qobject_cast(widget)) { + + if (qobject_cast(widget)) { return QStringLiteral("QPushButton { ") + baseStyle + QStringLiteral(" }"); } - else { - return QStringLiteral("QWidget { ") + baseStyle + QStringLiteral(" padding: 2px; }"); - } + + return QStringLiteral("QWidget { ") + baseStyle + QStringLiteral(" padding: 2px; }"); } void PreferencesSearchController::applyHighlightToWidget(QWidget* widget) @@ -1951,12 +1951,12 @@ bool PreferencesSearchController::handleSearchBoxKeyPress(QKeyEvent* keyEvent) switch (keyEvent->key()) { case Qt::Key_Down: { // Move selection down in popup, skipping separators - int currentRow = m_searchResultsList->currentRow(); - int totalItems = m_searchResultsList->count(); + const int currentRow = m_searchResultsList->currentRow(); + const int totalItems = m_searchResultsList->count(); for (int i = 1; i < totalItems; ++i) { - int nextRow = (currentRow + i) % totalItems; - QListWidgetItem* item = m_searchResultsList->item(nextRow); - if (item && (item->flags() & Qt::ItemIsSelectable)) { + const int nextRow = (currentRow + i) % totalItems; + if (QListWidgetItem* item = m_searchResultsList->item(nextRow); + item && (item->flags() & Qt::ItemIsSelectable)) { m_searchResultsList->setCurrentRow(nextRow); break; } @@ -1965,8 +1965,8 @@ bool PreferencesSearchController::handleSearchBoxKeyPress(QKeyEvent* keyEvent) } case Qt::Key_Up: { // Move selection up in popup, skipping separators - int currentRow = m_searchResultsList->currentRow(); - int totalItems = m_searchResultsList->count(); + const int currentRow = m_searchResultsList->currentRow(); + const int totalItems = m_searchResultsList->count(); for (int i = 1; i < totalItems; ++i) { int prevRow = (currentRow - i + totalItems) % totalItems; QListWidgetItem* item = m_searchResultsList->item(prevRow); @@ -1989,7 +1989,7 @@ bool PreferencesSearchController::handleSearchBoxKeyPress(QKeyEvent* keyEvent) } } -bool PreferencesSearchController::handlePopupKeyPress(QKeyEvent* keyEvent) +bool PreferencesSearchController::handlePopupKeyPress(const QKeyEvent* keyEvent) { switch (keyEvent->key()) { case Qt::Key_Return: @@ -2005,16 +2005,15 @@ bool PreferencesSearchController::handlePopupKeyPress(QKeyEvent* keyEvent) } } -bool PreferencesSearchController::isClickOutsidePopup(QMouseEvent* mouseEvent) +bool PreferencesSearchController::isClickOutsidePopup(const QMouseEvent* mouseEvent) const { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QPoint globalPos = mouseEvent->globalPos(); #else - QPoint globalPos = mouseEvent->globalPosition().toPoint(); + const QPoint globalPos = mouseEvent->globalPosition().toPoint(); #endif - QRect searchBoxRect = QRect(m_searchBox->mapToGlobal(QPoint(0, 0)), m_searchBox->size()); - QRect popupRect - = QRect(m_searchResultsList->mapToGlobal(QPoint(0, 0)), m_searchResultsList->size()); + const auto searchBoxRect = QRect(m_searchBox->mapToGlobal(QPoint(0, 0)), m_searchBox->size()); + auto popupRect = QRect(m_searchResultsList->mapToGlobal(QPoint(0, 0)), m_searchResultsList->size()); return !searchBoxRect.contains(globalPos) && !popupRect.contains(globalPos); } @@ -2023,13 +2022,13 @@ bool DlgPreferencesImp::eventFilter(QObject* obj, QEvent* event) { // Handle search box key presses if (obj == ui->searchBox && event->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(event); + auto* keyEvent = static_cast(event); return m_searchController->handleSearchBoxKeyPress(keyEvent); } // Handle popup key presses if (obj == m_searchController->getSearchResultsList() && event->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(event); + auto* keyEvent = static_cast(event); return m_searchController->handlePopupKeyPress(keyEvent); } @@ -2041,7 +2040,7 @@ bool DlgPreferencesImp::eventFilter(QObject* obj, QEvent* event) // Handle search box focus loss if (obj == ui->searchBox && event->type() == QEvent::FocusOut) { - QFocusEvent* focusEvent = static_cast(event); + auto* focusEvent = static_cast(event); if (focusEvent->reason() != Qt::PopupFocusReason && focusEvent->reason() != Qt::MouseFocusReason) { // Only hide if focus is going somewhere else, not due to popup interaction @@ -2055,8 +2054,8 @@ bool DlgPreferencesImp::eventFilter(QObject* obj, QEvent* event) // Handle clicks outside popup if (event->type() == QEvent::MouseButtonPress) { - QMouseEvent* mouseEvent = static_cast(event); - QWidget* widget = qobject_cast(obj); + auto* mouseEvent = static_cast(event); + auto widget = qobject_cast(obj); // Check if click is outside search area if (m_searchController->isPopupVisible() && obj != m_searchController->getSearchResultsList() diff --git a/src/Gui/Dialogs/DlgPreferencesImp.h b/src/Gui/Dialogs/DlgPreferencesImp.h index 59ce7242fb..c095435c43 100644 --- a/src/Gui/Dialogs/DlgPreferencesImp.h +++ b/src/Gui/Dialogs/DlgPreferencesImp.h @@ -96,8 +96,8 @@ public: // Event handling bool handleSearchBoxKeyPress(QKeyEvent* keyEvent); - bool handlePopupKeyPress(QKeyEvent* keyEvent); - bool isClickOutsidePopup(QMouseEvent* mouseEvent); + bool handlePopupKeyPress(const QKeyEvent* keyEvent); + bool isClickOutsidePopup(const QMouseEvent* mouseEvent) const; // Focus management void ensureSearchBoxFocus(); @@ -144,18 +144,18 @@ private: // UI helpers void configurePopupSize(); - int calculatePopupHeight(int popupWidth); + int calculatePopupHeight(int popupWidth) const; void applyHighlightToWidget(QWidget* widget); - QString getHighlightStyleForWidget(QWidget* widget); + static QString getHighlightStyleForWidget(QWidget* widget); // Search result navigation void selectNextSearchResult(); void selectPreviousSearchResult(); // Utility methods - QString findGroupBoxForWidget(QWidget* widget); - bool fuzzyMatch(const QString& searchText, const QString& targetText, int& score); - bool isExactMatch(const QString& searchText, const QString& targetText); + static QString findGroupBoxForWidget(QWidget* widget); + static bool fuzzyMatch(const QString& searchText, const QString& targetText, int& score); + static bool isExactMatch(const QString& searchText, const QString& targetText); private: DlgPreferencesImp* m_parentDialog;