summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVolodymyr Zibarov <[email protected]>2024-05-03 15:58:50 +0300
committerVolodymyr Zibarov <[email protected]>2024-05-13 22:32:34 +0000
commit4585cacaa9f9329a10aaf13a449151f5e9bc7a2c (patch)
treef51b362b3308966dd7843fe153b7d35f98af57b3
parent3d59a0f7c7321b611fd08a9fed94819c43e5dd79 (diff)
Add way to override when to show context menu
Add Qt::ContextMenuTrigger enum used with QStyleHints::setContextMenuTrigger() to override default platform behavior when to trigger context menu event. The default is to show context menu on mouse press on UNIX systems and on mouse release on Windows. Give developer a possibility to override platform default behavior to make cross platform application that behaves the same way on all platforms Task-number: QTBUG-93486 Change-Id: Ic832d3d8a7c355a8adb46868fff9cfd19988cf3c Reviewed-by: Tor Arne Vestbø <[email protected]>
-rw-r--r--src/corelib/global/qnamespace.h6
-rw-r--r--src/corelib/global/qnamespace.qdoc12
-rw-r--r--src/gui/kernel/qguiapplication.cpp9
-rw-r--r--src/gui/kernel/qguiapplication_p.h2
-rw-r--r--src/gui/kernel/qstylehints.cpp34
-rw-r--r--src/gui/kernel/qstylehints.h5
-rw-r--r--src/gui/kernel/qstylehints_p.h1
-rw-r--r--src/gui/kernel/qwindow.cpp8
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp9
-rw-r--r--tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp40
10 files changed, 115 insertions, 11 deletions
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h
index 2398c0a1a49..1569577b129 100644
--- a/src/corelib/global/qnamespace.h
+++ b/src/corelib/global/qnamespace.h
@@ -1356,6 +1356,11 @@ namespace Qt {
PreventContextMenu
};
+ enum class ContextMenuTrigger {
+ Press,
+ Release,
+ };
+
enum InputMethodQuery {
ImEnabled = 0x1,
ImCursorRectangle = 0x2,
@@ -1731,6 +1736,7 @@ namespace Qt {
Q_ENUM_NS(ScrollBarPolicy)
Q_ENUM_NS(FocusPolicy)
Q_ENUM_NS(ContextMenuPolicy)
+ Q_ENUM_NS(ContextMenuTrigger)
Q_ENUM_NS(ArrowType)
Q_ENUM_NS(ToolButtonStyle)
Q_ENUM_NS(PenStyle)
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc
index ddfade675a3..b2ec64f435c 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -2087,6 +2087,18 @@
*/
/*!
+ \enum Qt::ContextMenuTrigger
+ \since 6.8
+
+ This enum type defines the mouse event used to trigger a context menu event.
+
+ \value Press context menu on mouse press event, default on UNIX systems.
+ \value Release context menu on mouse release event, default on Windows.
+
+ \sa QStyleHints::contextMenuTrigger
+*/
+
+/*!
\enum Qt::FocusPolicy
This enum type defines the various policies a widget can have with
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index c97374e9757..f2b914f203d 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -3447,6 +3447,15 @@ void QGuiApplicationPrivate::updatePalette()
}
}
+QEvent::Type QGuiApplicationPrivate::contextMenuEventType()
+{
+ switch (QGuiApplication::styleHints()->contextMenuTrigger()) {
+ case Qt::ContextMenuTrigger::Press: return QEvent::MouseButtonPress;
+ case Qt::ContextMenuTrigger::Release: return QEvent::MouseButtonRelease;
+ }
+ return QEvent::None;
+}
+
void QGuiApplicationPrivate::clearPalette()
{
delete app_pal;
diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h
index cca79534fc7..39c490c5813 100644
--- a/src/gui/kernel/qguiapplication_p.h
+++ b/src/gui/kernel/qguiapplication_p.h
@@ -323,6 +323,8 @@ public:
static void updatePalette();
+ static QEvent::Type contextMenuEventType();
+
protected:
virtual void handleThemeChanged();
diff --git a/src/gui/kernel/qstylehints.cpp b/src/gui/kernel/qstylehints.cpp
index 0d15d114ec5..73c61997330 100644
--- a/src/gui/kernel/qstylehints.cpp
+++ b/src/gui/kernel/qstylehints.cpp
@@ -443,6 +443,40 @@ void QStyleHints::setShowShortcutsInContextMenus(bool s)
}
/*!
+ \property QStyleHints::contextMenuTrigger
+ \since 6.8
+ \brief mouse event used to trigger a context menu event.
+
+ The default on UNIX systems is to show context menu on mouse button press event, while on
+ Windows it is the mouse button release event. This property can be used to override the default
+ platform behavior.
+
+ \note Developers must use this property with great care, as it changes the default interaction
+ mode that their users will expect on the platform that they are running on.
+
+ \sa Qt::ContextMenuTrigger
+*/
+Qt::ContextMenuTrigger QStyleHints::contextMenuTrigger() const
+{
+ Q_D(const QStyleHints);
+ if (d->m_contextMenuTrigger == -1) {
+ return themeableHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool()
+ ? Qt::ContextMenuTrigger::Release
+ : Qt::ContextMenuTrigger::Press;
+ }
+ return Qt::ContextMenuTrigger(d->m_contextMenuTrigger);
+}
+
+void QStyleHints::setContextMenuTrigger(Qt::ContextMenuTrigger contextMenuTrigger)
+{
+ Q_D(QStyleHints);
+ const Qt::ContextMenuTrigger currentTrigger = this->contextMenuTrigger();
+ d->m_contextMenuTrigger = int(contextMenuTrigger);
+ if (currentTrigger != contextMenuTrigger)
+ emit contextMenuTriggerChanged(contextMenuTrigger);
+}
+
+/*!
\property QStyleHints::passwordMaskDelay
\brief the time, in milliseconds, a typed letter is displayed unshrouded
in a text input field in password mode.
diff --git a/src/gui/kernel/qstylehints.h b/src/gui/kernel/qstylehints.h
index 89813830603..97ef59f3cf5 100644
--- a/src/gui/kernel/qstylehints.h
+++ b/src/gui/kernel/qstylehints.h
@@ -36,6 +36,8 @@ class Q_GUI_EXPORT QStyleHints : public QObject
Q_PROPERTY(bool showIsMaximized READ showIsMaximized STORED false CONSTANT FINAL)
Q_PROPERTY(bool showShortcutsInContextMenus READ showShortcutsInContextMenus
WRITE setShowShortcutsInContextMenus NOTIFY showShortcutsInContextMenusChanged FINAL)
+ Q_PROPERTY(Qt::ContextMenuTrigger contextMenuTrigger READ contextMenuTrigger WRITE
+ setContextMenuTrigger NOTIFY contextMenuTriggerChanged FINAL)
Q_PROPERTY(int startDragDistance READ startDragDistance NOTIFY startDragDistanceChanged FINAL)
Q_PROPERTY(int startDragTime READ startDragTime NOTIFY startDragTimeChanged FINAL)
Q_PROPERTY(int startDragVelocity READ startDragVelocity STORED false CONSTANT FINAL)
@@ -80,6 +82,8 @@ public:
bool showIsMaximized() const;
bool showShortcutsInContextMenus() const;
void setShowShortcutsInContextMenus(bool showShortcutsInContextMenus);
+ Qt::ContextMenuTrigger contextMenuTrigger() const;
+ void setContextMenuTrigger(Qt::ContextMenuTrigger contextMenuTrigger);
int passwordMaskDelay() const;
QChar passwordMaskCharacter() const;
qreal fontSmoothingGamma() const;
@@ -108,6 +112,7 @@ Q_SIGNALS:
void tabFocusBehaviorChanged(Qt::TabFocusBehavior tabFocusBehavior);
void useHoverEffectsChanged(bool useHoverEffects);
void showShortcutsInContextMenusChanged(bool);
+ void contextMenuTriggerChanged(Qt::ContextMenuTrigger contextMenuTrigger);
void wheelScrollLinesChanged(int scrollLines);
void mouseQuickSelectionThresholdChanged(int threshold);
void colorSchemeChanged(Qt::ColorScheme colorScheme);
diff --git a/src/gui/kernel/qstylehints_p.h b/src/gui/kernel/qstylehints_p.h
index 2b3979512a1..497bf95cbf4 100644
--- a/src/gui/kernel/qstylehints_p.h
+++ b/src/gui/kernel/qstylehints_p.h
@@ -35,6 +35,7 @@ public:
int m_tabFocusBehavior = -1;
int m_uiEffects = -1;
int m_showShortcutsInContextMenus = -1;
+ int m_contextMenuTrigger = -1;
int m_wheelScrollLines = -1;
int m_mouseQuickSelectionThreshold = -1;
int m_mouseDoubleClickDistance = -1;
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index b40fd7e8e84..7c885032c76 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -2654,16 +2654,14 @@ bool QWindow::event(QEvent *ev)
This logic could be simplified by always synthesizing events in
QGuiApplicationPrivate, or perhaps even in each QPA plugin. See QTBUG-93486.
*/
- static const QEvent::Type contextMenuTrigger =
- QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool() ?
- QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
auto asMouseEvent = [](QEvent *ev) {
const auto t = ev->type();
return t == QEvent::MouseButtonPress || t == QEvent::MouseButtonRelease
? static_cast<QMouseEvent *>(ev) : nullptr ;
};
- if (QMouseEvent *me = asMouseEvent(ev); me &&
- ev->type() == contextMenuTrigger && me->button() == Qt::RightButton) {
+ if (QMouseEvent *me = asMouseEvent(ev);
+ me && ev->type() == QGuiApplicationPrivate::contextMenuEventType()
+ && me->button() == Qt::RightButton) {
QContextMenuEvent e(QContextMenuEvent::Mouse, me->position().toPoint(),
me->globalPosition().toPoint(), me->modifiers());
QGuiApplication::sendEvent(this, &e);
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index e7f0a840041..03dde9ca69e 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -505,9 +505,6 @@ void QWidgetWindow::handleNonClientAreaMouseEvent(QMouseEvent *e)
void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
{
- static const QEvent::Type contextMenuTrigger =
- QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool() ?
- QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
if (QApplicationPrivate::inPopupMode()) {
QPointer<QWidget> activePopupWidget = QApplication::activePopupWidget();
QPointF mapped = event->position();
@@ -624,7 +621,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
}
qt_replay_popup_mouse_event = false;
#ifndef QT_NO_CONTEXTMENU
- } else if (event->type() == contextMenuTrigger
+ } else if (event->type() == QGuiApplicationPrivate::contextMenuEventType()
&& event->button() == Qt::RightButton
&& (openPopupCount == oldOpenPopupCount)) {
QWidget *receiver = activePopupWidget;
@@ -637,7 +634,6 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
QApplication::forwardEvent(receiver, &e, event);
}
#else
- Q_UNUSED(contextMenuTrigger);
Q_UNUSED(oldOpenPopupCount);
}
#endif
@@ -684,7 +680,8 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
event->setAccepted(translated.isAccepted());
}
#ifndef QT_NO_CONTEXTMENU
- if (event->type() == contextMenuTrigger && event->button() == Qt::RightButton
+ if (event->type() == QGuiApplicationPrivate::contextMenuEventType()
+ && event->button() == Qt::RightButton
&& m_widget->rect().contains(event->position().toPoint())) {
QContextMenuEvent e(QContextMenuEvent::Mouse, mapped, event->globalPosition().toPoint(), event->modifiers());
QGuiApplication::forwardEvent(receiver, &e, event);
diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
index 3447c9af282..c68bb74f6e6 100644
--- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
+++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
@@ -477,6 +477,10 @@ private slots:
void reparentWindowHandles_data();
void reparentWindowHandles();
+#ifndef QT_NO_CONTEXTMENU
+ void contextMenuTrigger();
+#endif
+
private:
const QString m_platform;
QSize m_testWidgetSize;
@@ -14040,5 +14044,41 @@ void tst_QWidget::reparentWindowHandles()
}
}
+#ifndef QT_NO_CONTEXTMENU
+void tst_QWidget::contextMenuTrigger()
+{
+ class ContextMenuWidget : public QWidget
+ {
+ public:
+ int events = 0;
+
+ protected:
+ void contextMenuEvent(QContextMenuEvent *) override { ++events; }
+ };
+
+ const Qt::ContextMenuTrigger wasTrigger = QGuiApplication::styleHints()->contextMenuTrigger();
+ auto restoreTriggerGuard = qScopeGuard([wasTrigger]{
+ QGuiApplication::styleHints()->setContextMenuTrigger(wasTrigger);
+ });
+
+ ContextMenuWidget widget;
+ widget.show();
+ QVERIFY(!qApp->topLevelWindows().empty());
+ auto *window = qApp->topLevelWindows()[0];
+ QVERIFY(window);
+ QCOMPARE(widget.events, 0);
+ QGuiApplication::styleHints()->setContextMenuTrigger(Qt::ContextMenuTrigger::Press);
+ QTest::mousePress(window, Qt::RightButton);
+ QCOMPARE(widget.events, 1);
+ QTest::mouseRelease(window, Qt::RightButton);
+ QCOMPARE(widget.events, 1);
+ QGuiApplication::styleHints()->setContextMenuTrigger(Qt::ContextMenuTrigger::Release);
+ QTest::mousePress(window, Qt::RightButton);
+ QCOMPARE(widget.events, 1);
+ QTest::mouseRelease(window, Qt::RightButton);
+ QCOMPARE(widget.events, 2);
+}
+#endif
+
QTEST_MAIN(tst_QWidget)
#include "tst_qwidget.moc"