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>
This commit is contained in:
tetektoza
2025-12-07 11:43:09 +01:00
committed by GitHub
parent 6ec9870d54
commit 915b6f9901
2 changed files with 43 additions and 44 deletions

View File

@@ -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<QGroupBox*>(parent);
auto* groupBox = qobject_cast<QGroupBox*>(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<QLabel*>(widget)) {
return QStringLiteral("QLabel { ") + baseStyle + QStringLiteral(" padding: 2px; }");
}
else if (qobject_cast<QCheckBox*>(widget)) {
if (qobject_cast<QCheckBox*>(widget)) {
return QStringLiteral("QCheckBox { ") + baseStyle + QStringLiteral(" padding: 2px; }");
}
else if (qobject_cast<QRadioButton*>(widget)) {
if (qobject_cast<QRadioButton*>(widget)) {
return QStringLiteral("QRadioButton { ") + baseStyle + QStringLiteral(" padding: 2px; }");
}
else if (qobject_cast<QGroupBox*>(widget)) {
if (qobject_cast<QGroupBox*>(widget)) {
return QStringLiteral("QGroupBox::title { ") + baseStyle + QStringLiteral(" padding: 2px; }");
}
else if (qobject_cast<QPushButton*>(widget)) {
if (qobject_cast<QPushButton*>(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<QKeyEvent*>(event);
auto* keyEvent = static_cast<QKeyEvent*>(event);
return m_searchController->handleSearchBoxKeyPress(keyEvent);
}
// Handle popup key presses
if (obj == m_searchController->getSearchResultsList() && event->type() == QEvent::KeyPress) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
auto* keyEvent = static_cast<QKeyEvent*>(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<QFocusEvent*>(event);
auto* focusEvent = static_cast<QFocusEvent*>(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<QMouseEvent*>(event);
QWidget* widget = qobject_cast<QWidget*>(obj);
auto* mouseEvent = static_cast<QMouseEvent*>(event);
auto widget = qobject_cast<QWidget*>(obj);
// Check if click is outside search area
if (m_searchController->isPopupVisible() && obj != m_searchController->getSearchResultsList()

View File

@@ -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;