From b572a85b68aeea8bc1b755135039bb32a9bbed95 Mon Sep 17 00:00:00 2001 From: Axel Spoerl Date: Wed, 10 Jul 2024 10:59:12 +0200 Subject: [PATCH] Remove GTK3 native menu QGtk3Menu relied on gtk_menu_popup, which is deprecated and no longer supported from GTK 3.22 onward. This renders GTK applications inconsistent, if GTK >= 3.22 is used. GTK native menus aren't special. They do not blend in with e.g. a main window or QML ApplicationWindow drawn by Qt itself. There is neither a performance, nor a visual disadvantage of drawing own menus by Qt. Remove support for native windows in the GTK3 platform theme. [ChangeLog][Platform Specific Changes][Linux] Due to deprecation of the gtk_menu_popup() function, we no longer use GTK menus on Gnome: they are now rendered by Qt. Fixes: QTBUG-124561 Fixes: QTBUG-126598 Pick-to: 6.7 6.5 Change-Id: I031751fb6e070444e99ab2bcac4c622ac509b4c9 Reviewed-by: Oliver Eftevaag Reviewed-by: Shawn Rutledge Reviewed-by: Volker Hilsheimer (cherry picked from commit 7ffb4c16955a60ffc0f32d990c70476c49ed0b0b) Reviewed-by: Qt Cherry-pick Bot --- .../platformthemes/gtk3/CMakeLists.txt | 2 +- src/plugins/platformthemes/gtk3/qgtk3menu.cpp | 452 ------------------ src/plugins/platformthemes/gtk3/qgtk3menu.h | 128 ----- .../platformthemes/gtk3/qgtk3theme.cpp | 11 - src/plugins/platformthemes/gtk3/qgtk3theme.h | 3 - 5 files changed, 1 insertion(+), 595 deletions(-) delete mode 100644 src/plugins/platformthemes/gtk3/qgtk3menu.cpp delete mode 100644 src/plugins/platformthemes/gtk3/qgtk3menu.h diff --git a/src/plugins/platformthemes/gtk3/CMakeLists.txt b/src/plugins/platformthemes/gtk3/CMakeLists.txt index 20487a6babe..40c425d367a 100644 --- a/src/plugins/platformthemes/gtk3/CMakeLists.txt +++ b/src/plugins/platformthemes/gtk3/CMakeLists.txt @@ -19,7 +19,7 @@ qt_internal_add_plugin(QGtk3ThemePlugin SOURCES main.cpp qgtk3dialoghelpers.cpp qgtk3dialoghelpers.h - qgtk3menu.cpp qgtk3menu.h + qgtk3theme.cpp qgtk3theme.h qgtk3interface.cpp qgtk3interface_p.h qgtk3storage.cpp qgtk3storage_p.h diff --git a/src/plugins/platformthemes/gtk3/qgtk3menu.cpp b/src/plugins/platformthemes/gtk3/qgtk3menu.cpp deleted file mode 100644 index c4ea0e5e33d..00000000000 --- a/src/plugins/platformthemes/gtk3/qgtk3menu.cpp +++ /dev/null @@ -1,452 +0,0 @@ -// Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#include "qgtk3menu.h" - -#include -#include -#include - -#undef signals -#include - -QT_BEGIN_NAMESPACE - -#if QT_CONFIG(shortcut) -static guint qt_gdkKey(const QKeySequence &shortcut) -{ - if (shortcut.isEmpty()) - return 0; - - // TODO: proper mapping - Qt::KeyboardModifiers mods = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier; - return (shortcut[0].toCombined() ^ mods) & shortcut[0].toCombined(); -} - -static GdkModifierType qt_gdkModifiers(const QKeySequence &shortcut) -{ - if (shortcut.isEmpty()) - return GdkModifierType(0); - - guint mods = 0; - Qt::KeyboardModifiers m = shortcut[0].keyboardModifiers(); - if (m & Qt::ShiftModifier) - mods |= GDK_SHIFT_MASK; - if (m & Qt::ControlModifier) - mods |= GDK_CONTROL_MASK; - if (m & Qt::AltModifier) - mods |= GDK_MOD1_MASK; - if (m & Qt::MetaModifier) - mods |= GDK_META_MASK; - - return static_cast(mods); -} -#endif - -QGtk3MenuItem::QGtk3MenuItem() - : m_visible(true), - m_separator(false), - m_checkable(false), - m_checked(false), - m_enabled(true), - m_exclusive(false), - m_underline(false), - m_invalid(true), - m_menu(nullptr), - m_item(nullptr) -{ -} - -QGtk3MenuItem::~QGtk3MenuItem() -{ -} - -bool QGtk3MenuItem::isInvalid() const -{ - return m_invalid; -} - -GtkWidget *QGtk3MenuItem::create() -{ - if (m_invalid) { - if (m_item) { - gtk_widget_destroy(m_item); - m_item = nullptr; - } - m_invalid = false; - } - - if (!m_item) { - if (m_separator) { - m_item = gtk_separator_menu_item_new(); - } else { - if (m_checkable) { - m_item = gtk_check_menu_item_new(); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(m_item), m_checked); - g_signal_connect(m_item, "toggled", G_CALLBACK(onToggle), this); - } else { - m_item = gtk_menu_item_new(); - g_signal_connect(m_item, "activate", G_CALLBACK(onActivate), this); - } - gtk_menu_item_set_label(GTK_MENU_ITEM(m_item), m_text.toUtf8()); - gtk_menu_item_set_use_underline(GTK_MENU_ITEM(m_item), m_underline); - if (m_menu) - gtk_menu_item_set_submenu(GTK_MENU_ITEM(m_item), m_menu->handle()); - g_signal_connect(m_item, "select", G_CALLBACK(onSelect), this); -#if QT_CONFIG(shortcut) - if (!m_shortcut.isEmpty()) { - GtkWidget *label = gtk_bin_get_child(GTK_BIN(m_item)); - gtk_accel_label_set_accel(GTK_ACCEL_LABEL(label), qt_gdkKey(m_shortcut), qt_gdkModifiers(m_shortcut)); - } -#endif - } - gtk_widget_set_sensitive(m_item, m_enabled); - gtk_widget_set_visible(m_item, m_visible); - if (GTK_IS_CHECK_MENU_ITEM(m_item)) - g_object_set(m_item, "draw-as-radio", m_exclusive, NULL); - } - - return m_item; -} - -GtkWidget *QGtk3MenuItem::handle() const -{ - return m_item; -} - -QString QGtk3MenuItem::text() const -{ - return m_text; -} - -static QString convertMnemonics(QString text, bool *found) -{ - *found = false; - - qsizetype i = text.size() - 1; - while (i >= 0) { - const QChar c = text.at(i); - if (c == u'&') { - if (i == 0 || text.at(i - 1) != u'&') { - // convert Qt to GTK mnemonic - if (i < text.size() - 1 && !text.at(i + 1).isSpace()) { - text.replace(i, 1, u'_'); - *found = true; - } - } else if (text.at(i - 1) == u'&') { - // unescape ampersand - text.replace(--i, 2, u'&'); - } - } else if (c == u'_') { - // escape GTK mnemonic - text.insert(i, u'_'); - } - --i; - } - - return text; -} - -void QGtk3MenuItem::setText(const QString &text) -{ - m_text = convertMnemonics(text, &m_underline); - if (GTK_IS_MENU_ITEM(m_item)) { - gtk_menu_item_set_label(GTK_MENU_ITEM(m_item), m_text.toUtf8()); - gtk_menu_item_set_use_underline(GTK_MENU_ITEM(m_item), m_underline); - } -} - -QGtk3Menu *QGtk3MenuItem::menu() const -{ - return m_menu; -} - -void QGtk3MenuItem::setMenu(QPlatformMenu *menu) -{ - m_menu = qobject_cast(menu); - if (GTK_IS_MENU_ITEM(m_item)) - gtk_menu_item_set_submenu(GTK_MENU_ITEM(m_item), m_menu ? m_menu->handle() : nullptr); -} - -bool QGtk3MenuItem::isVisible() const -{ - return m_visible; -} - -void QGtk3MenuItem::setVisible(bool visible) -{ - if (m_visible == visible) - return; - - m_visible = visible; - if (GTK_IS_MENU_ITEM(m_item)) - gtk_widget_set_visible(m_item, visible); -} - -bool QGtk3MenuItem::isSeparator() const -{ - return m_separator; -} - -void QGtk3MenuItem::setIsSeparator(bool separator) -{ - if (m_separator == separator) - return; - - m_invalid = true; - m_separator = separator; -} - -bool QGtk3MenuItem::isCheckable() const -{ - return m_checkable; -} - -void QGtk3MenuItem::setCheckable(bool checkable) -{ - if (m_checkable == checkable) - return; - - m_invalid = true; - m_checkable = checkable; -} - -bool QGtk3MenuItem::isChecked() const -{ - return m_checked; -} - -void QGtk3MenuItem::setChecked(bool checked) -{ - if (m_checked == checked) - return; - - m_checked = checked; - if (GTK_IS_CHECK_MENU_ITEM(m_item)) - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(m_item), checked); -} - -#if QT_CONFIG(shortcut) -QKeySequence QGtk3MenuItem::shortcut() const -{ - return m_shortcut; -} - -void QGtk3MenuItem::setShortcut(const QKeySequence& shortcut) -{ - if (m_shortcut == shortcut) - return; - - m_shortcut = shortcut; - if (GTK_IS_MENU_ITEM(m_item)) { - GtkWidget *label = gtk_bin_get_child(GTK_BIN(m_item)); - gtk_accel_label_set_accel(GTK_ACCEL_LABEL(label), qt_gdkKey(m_shortcut), qt_gdkModifiers(m_shortcut)); - } -} -#endif - -bool QGtk3MenuItem::isEnabled() const -{ - return m_enabled; -} - -void QGtk3MenuItem::setEnabled(bool enabled) -{ - if (m_enabled == enabled) - return; - - m_enabled = enabled; - if (m_item) - gtk_widget_set_sensitive(m_item, enabled); -} - -bool QGtk3MenuItem::hasExclusiveGroup() const -{ - return m_exclusive; -} - -void QGtk3MenuItem::setHasExclusiveGroup(bool exclusive) -{ - if (m_exclusive == exclusive) - return; - - m_exclusive = exclusive; - if (GTK_IS_CHECK_MENU_ITEM(m_item)) - g_object_set(m_item, "draw-as-radio", exclusive, NULL); -} - -void QGtk3MenuItem::onSelect(GtkMenuItem *, void *data) -{ - QGtk3MenuItem *item = static_cast(data); - if (item) - emit item->hovered(); -} - -void QGtk3MenuItem::onActivate(GtkMenuItem *, void *data) -{ - QGtk3MenuItem *item = static_cast(data); - if (item) - emit item->activated(); -} - -void QGtk3MenuItem::onToggle(GtkCheckMenuItem *check, void *data) -{ - QGtk3MenuItem *item = static_cast(data); - if (item) { - bool active = gtk_check_menu_item_get_active(check); - if (active != item->isChecked()) { - item->setChecked(active); - emit item->activated(); - } - } -} - -QGtk3Menu::QGtk3Menu() -{ - m_menu = gtk_menu_new(); - - g_signal_connect(m_menu, "show", G_CALLBACK(onShow), this); - g_signal_connect(m_menu, "hide", G_CALLBACK(onHide), this); -} - -QGtk3Menu::~QGtk3Menu() -{ - if (GTK_IS_WIDGET(m_menu)) - gtk_widget_destroy(m_menu); -} - -GtkWidget *QGtk3Menu::handle() const -{ - return m_menu; -} - -void QGtk3Menu::insertMenuItem(QPlatformMenuItem *item, QPlatformMenuItem *before) -{ - QGtk3MenuItem *gitem = static_cast(item); - if (!gitem || m_items.contains(gitem)) - return; - - GtkWidget *handle = gitem->create(); - int index = m_items.indexOf(static_cast(before)); - if (index < 0) - index = m_items.size(); - m_items.insert(index, gitem); - gtk_menu_shell_insert(GTK_MENU_SHELL(m_menu), handle, index); -} - -void QGtk3Menu::removeMenuItem(QPlatformMenuItem *item) -{ - QGtk3MenuItem *gitem = static_cast(item); - if (!gitem || !m_items.removeOne(gitem)) - return; - - GtkWidget *handle = gitem->handle(); - if (handle) - gtk_container_remove(GTK_CONTAINER(m_menu), handle); -} - -void QGtk3Menu::syncMenuItem(QPlatformMenuItem *item) -{ - QGtk3MenuItem *gitem = static_cast(item); - int index = m_items.indexOf(gitem); - if (index == -1 || !gitem->isInvalid()) - return; - - GtkWidget *handle = gitem->create(); - if (handle) - gtk_menu_shell_insert(GTK_MENU_SHELL(m_menu), handle, index); -} - -void QGtk3Menu::syncSeparatorsCollapsible(bool enable) -{ - Q_UNUSED(enable); -} - -void QGtk3Menu::setEnabled(bool enabled) -{ - gtk_widget_set_sensitive(m_menu, enabled); -} - -void QGtk3Menu::setVisible(bool visible) -{ - gtk_widget_set_visible(m_menu, visible); -} - -static void qt_gtk_menu_position_func(GtkMenu *, gint *x, gint *y, gboolean *push_in, gpointer data) -{ - QGtk3Menu *menu = static_cast(data); - QPoint targetPos = menu->targetPos(); -#if GTK_CHECK_VERSION(3, 10, 0) - targetPos /= gtk_widget_get_scale_factor(menu->handle()); -#endif - *x = targetPos.x(); - *y = targetPos.y(); - *push_in = true; -} - -QPoint QGtk3Menu::targetPos() const -{ - return m_targetPos; -} - -void QGtk3Menu::showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) -{ - const QGtk3MenuItem *menuItem = static_cast(item); - if (menuItem) - gtk_menu_shell_select_item(GTK_MENU_SHELL(m_menu), menuItem->handle()); - - m_targetPos = QPoint(targetRect.x(), targetRect.y() + targetRect.height()); - - QPlatformWindow *pw = parentWindow ? parentWindow->handle() : nullptr; - if (pw) - m_targetPos = pw->mapToGlobal(m_targetPos); - - gtk_menu_popup(GTK_MENU(m_menu), nullptr, nullptr, qt_gtk_menu_position_func, this, 0, gtk_get_current_event_time()); -} - -void QGtk3Menu::dismiss() -{ - gtk_menu_popdown(GTK_MENU(m_menu)); -} - -QPlatformMenuItem *QGtk3Menu::menuItemAt(int position) const -{ - return m_items.value(position); -} - -QPlatformMenuItem *QGtk3Menu::menuItemForTag(quintptr tag) const -{ - for (QGtk3MenuItem *item : m_items) { - if (item->tag() == tag) - return item; - } - return nullptr; -} - -QPlatformMenuItem *QGtk3Menu::createMenuItem() const -{ - return new QGtk3MenuItem; -} - -QPlatformMenu *QGtk3Menu::createSubMenu() const -{ - return new QGtk3Menu; -} - -void QGtk3Menu::onShow(GtkWidget *, void *data) -{ - QGtk3Menu *menu = static_cast(data); - if (menu) - emit menu->aboutToShow(); -} - -void QGtk3Menu::onHide(GtkWidget *, void *data) -{ - QGtk3Menu *menu = static_cast(data); - if (menu) - emit menu->aboutToHide(); -} - -QT_END_NAMESPACE - -#include "moc_qgtk3menu.cpp" diff --git a/src/plugins/platformthemes/gtk3/qgtk3menu.h b/src/plugins/platformthemes/gtk3/qgtk3menu.h deleted file mode 100644 index 4eb432c69b2..00000000000 --- a/src/plugins/platformthemes/gtk3/qgtk3menu.h +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#ifndef QGTK3MENU_H -#define QGTK3MENU_H - -#include - -typedef struct _GtkWidget GtkWidget; -typedef struct _GtkMenuItem GtkMenuItem; -typedef struct _GtkCheckMenuItem GtkCheckMenuItem; - -QT_BEGIN_NAMESPACE - -class QGtk3Menu; - -class QGtk3MenuItem: public QPlatformMenuItem -{ -public: - QGtk3MenuItem(); - ~QGtk3MenuItem(); - - bool isInvalid() const; - - GtkWidget *create(); - GtkWidget *handle() const; - - QString text() const; - void setText(const QString &text) override; - - QGtk3Menu *menu() const; - void setMenu(QPlatformMenu *menu) override; - - bool isVisible() const; - void setVisible(bool visible) override; - - bool isSeparator() const; - void setIsSeparator(bool separator) override; - - bool isCheckable() const; - void setCheckable(bool checkable) override; - - bool isChecked() const; - void setChecked(bool checked) override; - -#if QT_CONFIG(shortcut) - QKeySequence shortcut() const; - void setShortcut(const QKeySequence &shortcut) override; -#endif - - bool isEnabled() const; - void setEnabled(bool enabled) override; - - bool hasExclusiveGroup() const; - void setHasExclusiveGroup(bool exclusive) override; - - void setRole(MenuRole role) override { Q_UNUSED(role); } - void setFont(const QFont &font) override { Q_UNUSED(font); } - void setIcon(const QIcon &icon) override { Q_UNUSED(icon); } - void setIconSize(int size) override { Q_UNUSED(size); } - -protected: - static void onSelect(GtkMenuItem *item, void *data); - static void onActivate(GtkMenuItem *item, void *data); - static void onToggle(GtkCheckMenuItem *item, void *data); - -private: - bool m_visible; - bool m_separator; - bool m_checkable; - bool m_checked; - bool m_enabled; - bool m_exclusive; - bool m_underline; - bool m_invalid; - QGtk3Menu *m_menu; - GtkWidget *m_item; - QString m_text; -#if QT_CONFIG(shortcut) - QKeySequence m_shortcut; -#endif -}; - -class QGtk3Menu : public QPlatformMenu -{ - Q_OBJECT - -public: - QGtk3Menu(); - ~QGtk3Menu(); - - GtkWidget *handle() const; - - void insertMenuItem(QPlatformMenuItem *item, QPlatformMenuItem *before) override; - void removeMenuItem(QPlatformMenuItem *item) override; - void syncMenuItem(QPlatformMenuItem *item) override; - void syncSeparatorsCollapsible(bool enable) override; - - void setEnabled(bool enabled) override; - void setVisible(bool visible) override; - - void setIcon(const QIcon &icon) override { Q_UNUSED(icon); } - void setText(const QString &text) override { Q_UNUSED(text); } - - QPoint targetPos() const; - - void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) override; - void dismiss() override; - - QPlatformMenuItem *menuItemAt(int position) const override; - QPlatformMenuItem *menuItemForTag(quintptr tag) const override; - - QPlatformMenuItem *createMenuItem() const override; - QPlatformMenu *createSubMenu() const override; - -protected: - static void onShow(GtkWidget *menu, void *data); - static void onHide(GtkWidget *menu, void *data); - -private: - GtkWidget *m_menu; - QPoint m_targetPos; - QList m_items; -}; - -QT_END_NAMESPACE - -#endif // QGTK3MENU_H diff --git a/src/plugins/platformthemes/gtk3/qgtk3theme.cpp b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp index 9d23ba7e48a..b21c45da7a0 100644 --- a/src/plugins/platformthemes/gtk3/qgtk3theme.cpp +++ b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp @@ -3,7 +3,6 @@ #include "qgtk3theme.h" #include "qgtk3dialoghelpers.h" -#include "qgtk3menu.h" #include #include #include @@ -196,16 +195,6 @@ QPlatformDialogHelper *QGtk3Theme::createPlatformDialogHelper(DialogType type) c } } -QPlatformMenu* QGtk3Theme::createPlatformMenu() const -{ - return new QGtk3Menu; -} - -QPlatformMenuItem* QGtk3Theme::createPlatformMenuItem() const -{ - return new QGtk3MenuItem; -} - bool QGtk3Theme::useNativeFileDialog() { /* Require GTK3 >= 3.15.5 to avoid running into this bug: diff --git a/src/plugins/platformthemes/gtk3/qgtk3theme.h b/src/plugins/platformthemes/gtk3/qgtk3theme.h index 2828cc56e63..5b67ca07658 100644 --- a/src/plugins/platformthemes/gtk3/qgtk3theme.h +++ b/src/plugins/platformthemes/gtk3/qgtk3theme.h @@ -23,9 +23,6 @@ public: bool usePlatformNativeDialog(DialogType type) const override; QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override; - QPlatformMenu* createPlatformMenu() const override; - QPlatformMenuItem* createPlatformMenuItem() const override; - const QPalette *palette(Palette type = SystemPalette) const override; const QFont *font(Font type = SystemFont) const override; QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const override;