summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2024-08-20 18:08:53 +0200
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2024-08-23 11:05:53 +0200
commit3d99f90b50ab22ef5ab4433c57f9ee584a0a7cae (patch)
tree99ee0420e4a45b184ae47b28802440c2de539a68 /src
parentbad618606d64e943e3fa78e7d1dbc8e1fab55480 (diff)
Avoid dock widget separators stealing mouse events from expanded toolbar
The way QMainWindow handles dock widget separators is a bit evil. They are real QWidgets, added as direct children of the QMainWindow, but painting and mouse interaction is handled by a hook into the QMainWindow events, via QMainWindowLayoutSeparatorHelper::windowEvent(), which is called at the start of QMainWindow::event(). For reasons unknown, we also raise() these separator widgets, which means they are always at the top of the child window stack of the main window. This is problematic for the situation when a QToolBar has an expanded overflow-menu, which is also raised(), but where we end up raising the separators on top of the toolbar again. As a result the toolbar gets leave events when we hover one of the separators, even if the toolbar menu is overlaid the dock area, resulting in the menu closing. We don't see this visually, as the separators are drawn via the hook mentioned above, which happens before QMainWindow draws any of its child widgets, like the toolbar. Fixing this requires us to selectively place the separators below any expanded toolbar each time we raise a separator, so that we respect the request of the toolbar to be on top, while still ensuring that the separators are above any of the dock widgets. Unfortunately the QMainWindowLayoutSeparatorHelper hook also processes mouse move and click events, resulting in the cursor changing shape when hovering the separator, even if the toolbar menu is over it. To fix this we amend the logic of finding a separator widget based on the mouse event position to also querying the QWidget childAt() function, and if we are over a tool bar, we bail out instead of looking for a separator on that position in the dock area layout. As the special separator logic in QMainWindow is conditioned on the style returning 1 for QStyle::PM_DockWidgetSeparatorExtent the test checks for this, and skips if another style is in use. So far only the macOS style is affected by this. See the original change in fba33a6bbc09bb278df75829d8d705863d40a6a3. Fixes: QTBUG-124733 Pick-to: 6.8 6.5 6.2 Change-Id: Ifc741336bd9725ef0fea9f0474e27b7481f3a183 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/widgets/widgets/qdockarealayout.cpp33
-rw-r--r--src/widgets/widgets/qmainwindowlayout.cpp8
-rw-r--r--src/widgets/widgets/qmainwindowlayout_p.h34
-rw-r--r--src/widgets/widgets/qtoolbarlayout_p.h2
4 files changed, 72 insertions, 5 deletions
diff --git a/src/widgets/widgets/qdockarealayout.cpp b/src/widgets/widgets/qdockarealayout.cpp
index da0e9871715..32d0f9b345a 100644
--- a/src/widgets/widgets/qdockarealayout.cpp
+++ b/src/widgets/widgets/qdockarealayout.cpp
@@ -19,6 +19,11 @@
#include "qdockwidget_p.h"
#include <private/qlayoutengine_p.h>
+#if QT_CONFIG(toolbar)
+#include "qtoolbar.h"
+#include "qtoolbarlayout_p.h"
+#endif
+
#include <qpainter.h>
#include <qstyleoption.h>
@@ -2036,6 +2041,30 @@ bool QDockAreaLayoutInfo::restoreState(QDataStream &stream, QList<QDockWidget*>
}
#if QT_CONFIG(tabbar)
+
+static void raiseSeparatorWidget(QWidget *separatorWidget)
+{
+ Q_ASSERT(separatorWidget);
+
+#if QT_CONFIG(toolbar)
+ // Raise the separator widget, but make sure it doesn't go above
+ // an expanded toolbar, as that would break mouse event hit testing.
+ Q_ASSERT(separatorWidget->parent());
+ const auto toolBars = separatorWidget->parent()->findChildren<QToolBar*>(Qt::FindDirectChildrenOnly);
+ for (auto *toolBar : toolBars) {
+ if (auto *toolBarLayout = qobject_cast<QToolBarLayout*>(toolBar->layout())) {
+ if (toolBarLayout->expanded) {
+ separatorWidget->stackUnder(toolBar);
+ return;
+ }
+ }
+ }
+#endif
+
+ separatorWidget->raise();
+}
+
+
void QDockAreaLayoutInfo::updateSeparatorWidgets() const
{
if (tabbed) {
@@ -2077,7 +2106,7 @@ void QDockAreaLayoutInfo::updateSeparatorWidgets() const
j++;
Q_ASSERT(sepWidget);
- sepWidget->raise();
+ raiseSeparatorWidget(sepWidget);
QRect sepRect = separatorRect(i).adjusted(-2, -2, 2, 2);
sepWidget->setGeometry(sepRect);
@@ -3362,7 +3391,7 @@ void QDockAreaLayout::updateSeparatorWidgets() const
j++;
Q_ASSERT(sepWidget);
- sepWidget->raise();
+ raiseSeparatorWidget(sepWidget);
QRect sepRect = separatorRect(i).adjusted(-2, -2, 2, 2);
sepWidget->setGeometry(sepRect);
diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp
index 1a365dbb6e5..3bec3a3c917 100644
--- a/src/widgets/widgets/qmainwindowlayout.cpp
+++ b/src/widgets/widgets/qmainwindowlayout.cpp
@@ -273,6 +273,14 @@ public:
QDockAreaLayoutInfo *dockAreaLayoutInfo() { return &layoutState; }
+#if QT_CONFIG(toolbar)
+ QToolBarAreaLayout *toolBarAreaLayout()
+ {
+ auto *mainWindow = static_cast<QMainWindow*>(parentWidget());
+ return qt_mainwindow_layout(mainWindow)->toolBarAreaLayout();
+ }
+#endif
+
bool nativeWindowDeco() const
{
return groupWindow()->hasNativeDecos();
diff --git a/src/widgets/widgets/qmainwindowlayout_p.h b/src/widgets/widgets/qmainwindowlayout_p.h
index 55a27e4849d..f4211046e1c 100644
--- a/src/widgets/widgets/qmainwindowlayout_p.h
+++ b/src/widgets/widgets/qmainwindowlayout_p.h
@@ -43,6 +43,7 @@ struct QDockWidgetPrivate {
#endif
#if QT_CONFIG(toolbar)
#include "qtoolbararealayout_p.h"
+#include "qtoolbar.h"
#endif
#include <QtCore/qloggingcategory.h>
@@ -92,6 +93,9 @@ public:
bool endSeparatorMove(const QPoint &pos);
bool windowEvent(QEvent *e);
+private:
+ QList<int> findSeparator(const QPoint &pos) const;
+
#endif // QT_CONFIG(dockwidget)
};
@@ -142,7 +146,7 @@ void QMainWindowLayoutSeparatorHelper<Layout>::adjustCursor(const QPoint &pos)
w->unsetCursor();
}
} else if (movingSeparator.isEmpty()) { // Don't change cursor when moving separator
- QList<int> pathToSeparator = layout()->dockAreaLayoutInfo()->findSeparator(pos);
+ QList<int> pathToSeparator = findSeparator(pos);
if (pathToSeparator != hoverSeparator) {
if (!hoverSeparator.isEmpty())
@@ -280,9 +284,34 @@ bool QMainWindowLayoutSeparatorHelper<Layout>::windowEvent(QEvent *event)
}
template <typename Layout>
+QList<int> QMainWindowLayoutSeparatorHelper<Layout>::findSeparator(const QPoint &pos) const
+{
+ Layout *layout = const_cast<Layout*>(this->layout());
+#if QT_CONFIG(toolbar)
+ QToolBarAreaLayout *toolBarAreaLayout = layout->toolBarAreaLayout();
+ if (!toolBarAreaLayout->isEmpty()) {
+ // We might have a toolbar that is currently expanded, covering
+ // parts of the dock area, in which case we don't want the dock
+ // area layout to treat mouse events for the expanded toolbar as
+ // hitting a separator.
+ const QWidget *widget = layout->window();
+ QWidget *childWidget = widget->childAt(pos);
+ while (childWidget && childWidget != widget) {
+ if (auto *toolBar = qobject_cast<QToolBar*>(childWidget)) {
+ if (!toolBarAreaLayout->indexOf(toolBar).isEmpty())
+ return {};
+ }
+ childWidget = childWidget->parentWidget();
+ }
+ }
+#endif
+ return layout->dockAreaLayoutInfo()->findSeparator(pos);
+}
+
+template <typename Layout>
bool QMainWindowLayoutSeparatorHelper<Layout>::startSeparatorMove(const QPoint &pos)
{
- movingSeparator = layout()->dockAreaLayoutInfo()->findSeparator(pos);
+ movingSeparator = findSeparator(pos);
if (movingSeparator.isEmpty())
return false;
@@ -493,6 +522,7 @@ public:
void removeToolBar(QToolBar *toolbar);
void toggleToolBarsVisible();
void moveToolBar(QToolBar *toolbar, int pos);
+ QToolBarAreaLayout *toolBarAreaLayout() { return &layoutState.toolBarAreaLayout; }
#endif
// dock widgets
diff --git a/src/widgets/widgets/qtoolbarlayout_p.h b/src/widgets/widgets/qtoolbarlayout_p.h
index a7d3810d619..7915af1e44b 100644
--- a/src/widgets/widgets/qtoolbarlayout_p.h
+++ b/src/widgets/widgets/qtoolbarlayout_p.h
@@ -38,7 +38,7 @@ public:
bool customWidget;
};
-class QToolBarLayout : public QLayout
+class Q_AUTOTEST_EXPORT QToolBarLayout : public QLayout
{
Q_OBJECT