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:
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user