summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2017-03-28 15:03:50 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2017-04-27 09:30:34 +0000
commit7849aa6e96aa923fca5523afc8cf88edcc0bcf90 (patch)
tree0aa4e3fec94df70483fe5a25f9d5a82fdc01bfe8
parent121a30ccef3b6306c1da4f415fe1305dbf2dd901 (diff)
Windows QPA: Add native menus
Add simple Win32-API based menus (not owner-drawn). Native menus are implemented using Win32 API and are simpler than QMenu-based menus in for example that they do allow for placing widgets on them or changing properties like fonts and do not provide hover signals. They are mainly intended for Qt Quick. By default, they will be used if the application is not an instance of QApplication or for Qt Quick Controls 2 applications. In addition, the command line option -platform windows:menus=native will unconditionally activate them and -platform windows:menus=no turns them off. [ChangeLog][QtGui][Windows] Native menus have been implemented. Task-number: QTBUG-55967 Change-Id: I439a7d949745debea3eb0e5789cf42288a0d526f Reviewed-by: Maurice Kalinowski <maurice.kalinowski@qt.io>
-rw-r--r--src/gui/kernel/qguiapplication.cpp9
-rw-r--r--src/plugins/platforms/windows/qtwindowsglobal.h8
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp27
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h3
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.cpp8
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.h4
-rw-r--r--src/plugins/platforms/windows/qwindowskeymapper.cpp6
-rw-r--r--src/plugins/platforms/windows/qwindowsmenu.cpp962
-rw-r--r--src/plugins/platforms/windows/qwindowsmenu.h243
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.cpp52
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.h7
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp13
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h6
-rw-r--r--src/plugins/platforms/windows/windows.pri2
14 files changed, 1349 insertions, 1 deletions
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index 56f112c36c..23c11fe212 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -570,6 +570,15 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME
\li \c {dialogs=[xp|none]}, \c xp uses XP-style native dialogs and
\c none disables them.
\li \c {fontengine=freetype}, uses the FreeType font engine.
+ \li \c {menus=[native|none]}, controls the use of native menus.
+
+ Native menus are implemented using Win32 API and are simpler than
+ QMenu-based menus in for example that they do allow for placing
+ widgets on them or changing properties like fonts and do not
+ provide hover signals. They are mainly intended for Qt Quick.
+ By default, they will be used if the application is not an
+ instance of QApplication or for Qt Quick Controls 2
+ applications.
\endlist
For more information about the platform-specific arguments available for
diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h
index e703b5d47e..34fe114d0e 100644
--- a/src/plugins/platforms/windows/qtwindowsglobal.h
+++ b/src/plugins/platforms/windows/qtwindowsglobal.h
@@ -121,6 +121,9 @@ enum WindowsEventType // Simplify event types
EndSessionApplicationEvent = ApplicationEventFlag + 5,
AppCommandEvent = ApplicationEventFlag + 6,
DeviceChangeEvent = ApplicationEventFlag + 7,
+ MenuAboutToShowEvent = ApplicationEventFlag + 8,
+ AcceleratorCommandEvent = ApplicationEventFlag + 9,
+ MenuCommandEvent = ApplicationEventFlag + 10,
InputMethodStartCompositionEvent = InputMethodEventFlag + 1,
InputMethodCompositionEvent = InputMethodEventFlag + 2,
InputMethodEndCompositionEvent = InputMethodEventFlag + 3,
@@ -274,6 +277,11 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI
return QtWindows::GestureEvent;
case WM_DEVICECHANGE:
return QtWindows::DeviceChangeEvent;
+ case WM_INITMENU:
+ case WM_INITMENUPOPUP:
+ return QtWindows::MenuAboutToShowEvent;
+ case WM_COMMAND:
+ return HIWORD(wParamIn) ? QtWindows::AcceleratorCommandEvent : QtWindows::MenuCommandEvent;
case WM_DPICHANGED:
return QtWindows::DpiChangedEvent;
default:
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index f017929148..b6b51cd60a 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -44,6 +44,7 @@
#include "qwindowskeymapper.h"
#include "qwindowsmousehandler.h"
#include "qtwindowsglobal.h"
+#include "qwindowsmenu.h"
#include "qwindowsmime.h"
#include "qwindowsinputcontext.h"
#include "qwindowstabletsupport.h"
@@ -90,6 +91,7 @@ Q_LOGGING_CATEGORY(lcQpaGl, "qt.qpa.gl")
Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime")
Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods")
Q_LOGGING_CATEGORY(lcQpaDialogs, "qt.qpa.dialogs")
+Q_LOGGING_CATEGORY(lcQpaMenus, "qt.qpa.menus")
Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
Q_LOGGING_CATEGORY(lcQpaAccessibility, "qt.qpa.accessibility")
@@ -601,6 +603,15 @@ void QWindowsContext::removeWindow(HWND hwnd)
}
}
+QWindowsWindow *QWindowsContext::findPlatformWindow(const QWindowsMenuBar *mb) const
+{
+ for (auto it = d->m_windows.cbegin(), end = d->m_windows.cend(); it != end; ++it) {
+ if ((*it)->menuBar() == mb)
+ return *it;
+ }
+ return nullptr;
+}
+
QWindowsWindow *QWindowsContext::findPlatformWindow(HWND hwnd) const
{
return d->m_windows.value(hwnd);
@@ -994,6 +1005,22 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
case QtWindows::InputMethodKeyDownEvent:
case QtWindows::AppCommandEvent:
return sessionManagerInteractionBlocked() || d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result);
+ case QtWindows::MenuAboutToShowEvent:
+ if (sessionManagerInteractionBlocked())
+ return true;
+ if (QWindowsPopupMenu::notifyAboutToShow(reinterpret_cast<HMENU>(wParam)))
+ return true;
+ if (platformWindow == nullptr || platformWindow->menuBar() == nullptr)
+ return false;
+ return platformWindow->menuBar()->notifyAboutToShow(reinterpret_cast<HMENU>(wParam));
+ case QtWindows::MenuCommandEvent:
+ if (sessionManagerInteractionBlocked())
+ return true;
+ if (QWindowsPopupMenu::notifyTriggered(LOWORD(wParam)))
+ return true;
+ if (platformWindow == nullptr || platformWindow->menuBar() == nullptr)
+ return false;
+ return platformWindow->menuBar()->notifyTriggered(LOWORD(wParam));
case QtWindows::MoveEvent:
platformWindow->handleMoved();
return true;
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index b50010321b..7f4a76af05 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -63,11 +63,13 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaGl)
Q_DECLARE_LOGGING_CATEGORY(lcQpaMime)
Q_DECLARE_LOGGING_CATEGORY(lcQpaInputMethods)
Q_DECLARE_LOGGING_CATEGORY(lcQpaDialogs)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaMenus)
Q_DECLARE_LOGGING_CATEGORY(lcQpaTablet)
Q_DECLARE_LOGGING_CATEGORY(lcQpaAccessibility)
class QWindow;
class QPlatformScreen;
+class QWindowsMenuBar;
class QWindowsScreenManager;
class QWindowsTabletSupport;
class QWindowsWindow;
@@ -177,6 +179,7 @@ public:
QWindowsWindow *findClosestPlatformWindow(HWND) const;
QWindowsWindow *findPlatformWindow(HWND) const;
+ QWindowsWindow *findPlatformWindow(const QWindowsMenuBar *mb) const;
QWindow *findWindow(HWND) const;
QWindowsWindow *findPlatformWindowAt(HWND parent, const QPoint &screenPoint,
unsigned cwex_flags) const;
diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp
index a049b8c40c..6c8fccf350 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.cpp
+++ b/src/plugins/platforms/windows/qwindowsintegration.cpp
@@ -42,6 +42,7 @@
#include "qwindowswindow.h"
#include "qwindowscontext.h"
#include "qwin10helpers.h"
+#include "qwindowsmenu.h"
#include "qwindowsopenglcontext.h"
#include "qwindowsscreen.h"
@@ -206,6 +207,10 @@ static inline unsigned parseOptions(const QStringList &paramList,
} else if (parseIntOption(param, QLatin1String("verbose"), 0, INT_MAX, &QWindowsContext::verbose)
|| parseIntOption(param, QLatin1String("tabletabsoluterange"), 0, INT_MAX, tabletAbsoluteRange)
|| parseIntOption(param, QLatin1String("dpiawareness"), QtWindows::ProcessDpiUnaware, QtWindows::ProcessPerMonitorDpiAware, dpiAwareness)) {
+ } else if (param == QLatin1String("menus=native")) {
+ options |= QWindowsIntegration::AlwaysUseNativeMenus;
+ } else if (param == QLatin1String("menus=none")) {
+ options |= QWindowsIntegration::NoNativeMenus;
} else {
qWarning() << "Unknown option" << param;
}
@@ -334,6 +339,9 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons
QWindowsWindow *result = createPlatformWindowHelper(window, obtained);
Q_ASSERT(result);
+ if (QWindowsMenuBar *menuBarToBeInstalled = QWindowsMenuBar::menuBarOf(window))
+ menuBarToBeInstalled->install(result);
+
if (requested.flags != obtained.flags)
window->setFlags(obtained.flags);
// Trigger geometry change (unless it has a special state in which case setWindowState()
diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h
index fbdb341dab..11320332bb 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.h
+++ b/src/plugins/platforms/windows/qwindowsintegration.h
@@ -64,7 +64,9 @@ public:
DontPassOsMouseEventsSynthesizedFromTouch = 0x20, // Do not pass OS-generated mouse events from touch.
// Keep in sync with QWindowsFontDatabase::FontOptions
DontUseDirectWriteFonts = QWindowsFontDatabase::DontUseDirectWriteFonts,
- DontUseColorFonts = QWindowsFontDatabase::DontUseColorFonts
+ DontUseColorFonts = QWindowsFontDatabase::DontUseColorFonts,
+ AlwaysUseNativeMenus = 0x100,
+ NoNativeMenus = 0x200
};
explicit QWindowsIntegration(const QStringList &paramList);
diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp
index 24c2df86d4..2f459b70ec 100644
--- a/src/plugins/platforms/windows/qwindowskeymapper.cpp
+++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp
@@ -903,6 +903,12 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms
return true;
}
+ // Enable Alt accelerators ("&File") on menus
+ if (msgType == WM_SYSKEYDOWN && (nModifiers & AltAny) != 0 && GetMenu(msg.hwnd) != nullptr)
+ return false;
+ if (msgType == WM_SYSKEYUP && nModifiers == 0 && GetMenu(msg.hwnd) != nullptr)
+ return false;
+
bool result = false;
// handle Directionality changes (BiDi) with RTL extensions
if (m_useRTLExtensions) {
diff --git a/src/plugins/platforms/windows/qwindowsmenu.cpp b/src/plugins/platforms/windows/qwindowsmenu.cpp
new file mode 100644
index 0000000000..4e1997c4f8
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowsmenu.cpp
@@ -0,0 +1,962 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsmenu.h"
+#include "qwindowscontext.h"
+#include "qwindowswindow.h"
+
+#include <QtGui/qwindow.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qpointer.h>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWindowsMenuBar
+ \brief Windows native menu bar
+
+ \list
+ \li \l{https://msdn.microsoft.com/de-de/library/windows/desktop/ms647553(v=vs.85).aspx#_win32_Menu_Creation_Functions},
+ \e{About Menus}
+ \endlist
+
+ \note The destruction order of the QWindowsMenu/Item/Bar instances is
+ arbitrary depending on whether the application is Qt Quick or
+ Qt Widgets, either the containers or the items might be deleted first.
+
+ \internal
+ \ingroup qt-lighthouse-win
+*/
+
+static uint nextId = 1;
+
+// Find a QPlatformMenu[Item]* in a vector of QWindowsMenu[Item], where
+// QVector::indexOf() cannot be used since it wants a QWindowsMenu[Item]*
+template <class Derived, class Needle>
+static int indexOf(const QVector<Derived *> &v, const Needle *needle)
+{
+ for (int i = 0, size = v.size(); i < size; ++i) {
+ if (v.at(i) == needle)
+ return i;
+ }
+ return -1;
+}
+
+// Helper for inserting a QPlatformMenu[Item]* into a vector of QWindowsMenu[Item].
+template <class Derived, class Base>
+static int insertBefore(QVector<Derived *> *v, Base *newItemIn, const Base *before = nullptr)
+{
+ int index = before ? indexOf(*v, before) : -1;
+ if (index != -1) {
+ v->insert(index, static_cast<Derived *>(newItemIn));
+ } else {
+ index = v->size();
+ v->append(static_cast<Derived *>(newItemIn));
+ }
+ return index;
+}
+
+static inline const wchar_t *qStringToWChar(const QString &s)
+{
+ return reinterpret_cast<const wchar_t *>(s.utf16());
+}
+
+// Traverse menu and return the item for which predicate
+// "bool Function(QWindowsMenuItem *)" returns true
+template <class Predicate>
+static QWindowsMenuItem *traverseMenuItems(const QWindowsMenu *menu, Predicate p)
+{
+ const QWindowsMenu::MenuItems &items = menu->menuItems();
+ for (QWindowsMenuItem *item : items) {
+ if (p(item))
+ return item;
+ if (item->subMenu()) {
+ if (QWindowsMenuItem *subMenuItem = traverseMenuItems(item->subMenu(), p))
+ return subMenuItem;
+ }
+ }
+ return nullptr;
+}
+
+// Traverse menu bar return the item for which predicate
+// "bool Function(QWindowsMenuItem *)" returns true
+template <class Predicate>
+static QWindowsMenuItem *traverseMenuItems(const QWindowsMenuBar *menuBar, Predicate p)
+{
+ const QWindowsMenuBar::Menus &menus = menuBar->menus();
+ for (QWindowsMenu *menu : menus) {
+ if (QWindowsMenuItem *item = traverseMenuItems(menu, p))
+ return item;
+ }
+ return nullptr;
+}
+
+template <class Menu /* Menu[Bar] */>
+static QWindowsMenuItem *findMenuItemById(const Menu *menu, uint id)
+{
+ return traverseMenuItems(menu, [id] (const QWindowsMenuItem *i) { return i->id() == id; });
+}
+
+// Traverse menu and return the menu for which predicate
+// "bool Function(QWindowsMenu *)" returns true
+template <class Predicate>
+static QWindowsMenu *traverseMenus(const QWindowsMenu *menu, Predicate p)
+{
+ const QWindowsMenu::MenuItems &items = menu->menuItems();
+ for (QWindowsMenuItem *item : items) {
+ if (QWindowsMenu *subMenu = item->subMenu()) {
+ if (p(subMenu))
+ return subMenu;
+ if (QWindowsMenu *menu = traverseMenus(subMenu, p))
+ return menu;
+ }
+ }
+ return nullptr;
+}
+
+// Traverse menu bar return the item for which
+// function "bool Function(QWindowsMenu *)" returns true
+template <class Predicate>
+static QWindowsMenu *traverseMenus(const QWindowsMenuBar *menuBar, Predicate p)
+{
+ const QWindowsMenuBar::Menus &menus = menuBar->menus();
+ for (QWindowsMenu *menu : menus) {
+ if (p(menu))
+ return menu;
+ if (QWindowsMenu *subMenu = traverseMenus(menu, p))
+ return subMenu;
+ }
+ return nullptr;
+}
+
+template <class Menu /* Menu[Bar] */>
+static QWindowsMenu *findMenuByHandle(const Menu *menu, HMENU hMenu)
+{
+ return traverseMenus(menu, [hMenu] (const QWindowsMenu *i) { return i->menuHandle() == hMenu; });
+}
+
+template <class MenuType>
+static int findNextVisibleEntry(const QVector<MenuType *> &entries, int pos)
+{
+ for (int i = pos, size = entries.size(); i < size; ++i) {
+ if (entries.at(i)->isVisible())
+ return i;
+ }
+ return -1;
+}
+
+static inline void menuItemInfoInit(MENUITEMINFO &menuItemInfo)
+{
+ memset(&menuItemInfo, 0, sizeof(MENUITEMINFO));
+ menuItemInfo.cbSize = sizeof(MENUITEMINFO);
+}
+
+static inline void menuItemInfoSetText(MENUITEMINFO &menuItemInfo, const QString &text)
+{
+ menuItemInfoInit(menuItemInfo);
+ menuItemInfo.fMask = MIIM_STRING;
+ menuItemInfo.dwTypeData = const_cast<wchar_t *>(qStringToWChar(text));
+ menuItemInfo.cch = UINT(text.size());
+}
+
+static UINT menuItemState(HMENU hMenu, UINT uItem, BOOL fByPosition)
+{
+ MENUITEMINFO menuItemInfo;
+ menuItemInfoInit(menuItemInfo);
+ menuItemInfo.fMask = MIIM_STATE;
+ return GetMenuItemInfo(hMenu, uItem, fByPosition, &menuItemInfo) == TRUE ? menuItemInfo.fState : 0;
+}
+
+static void menuItemSetState(HMENU hMenu, UINT uItem, BOOL fByPosition, UINT flags)
+{
+ MENUITEMINFO menuItemInfo;
+ menuItemInfoInit(menuItemInfo);
+ menuItemInfo.fMask = MIIM_STATE;
+ menuItemInfo.fState = flags;
+ SetMenuItemInfo(hMenu, uItem, fByPosition, &menuItemInfo);
+}
+
+static void menuItemSetChangeState(HMENU hMenu, UINT uItem, BOOL fByPosition,
+ bool value, UINT trueState, UINT falseState)
+{
+ const UINT oldState = menuItemState(hMenu, uItem, fByPosition);
+ UINT newState = oldState;
+ if (value) {
+ newState |= trueState;
+ newState &= ~falseState;
+ } else {
+ newState &= ~trueState;
+ newState |= falseState;
+ }
+ if (oldState != newState)
+ menuItemSetState(hMenu, uItem, fByPosition, newState);
+}
+
+// ------------ QWindowsMenuItem
+QWindowsMenuItem::QWindowsMenuItem(QWindowsMenu *parentMenu)
+ : m_parentMenu(parentMenu)
+ , m_id(0)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this)
+ << "parentMenu=" << parentMenu;
+}
+
+QWindowsMenuItem::~QWindowsMenuItem()
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this);
+ removeFromMenu();
+ freeBitmap();
+}
+
+void QWindowsMenuItem::freeBitmap()
+{
+ if (m_hbitmap) {
+ DeleteObject(m_hbitmap);
+ m_hbitmap = nullptr;
+ }
+}
+
+void QWindowsMenuItem::setIcon(const QIcon &icon)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << icon << ')' << this;
+ if (m_icon.cacheKey() == icon.cacheKey())
+ return;
+ m_icon = icon;
+ if (m_parentMenu != nullptr)
+ updateBitmap();
+}
+
+Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0);
+
+void QWindowsMenuItem::updateBitmap()
+{
+ freeBitmap();
+ if (!m_icon.isNull()) {
+ const int size = m_iconSize ? m_iconSize : GetSystemMetrics(SM_CYMENUCHECK);
+ m_hbitmap = qt_pixmapToWinHBITMAP(m_icon.pixmap(QSize(size, size)), 1);
+ }
+ MENUITEMINFO itemInfo;
+ menuItemInfoInit(itemInfo);
+ itemInfo.fMask = MIIM_BITMAP;
+ itemInfo.hbmpItem = m_hbitmap;
+ SetMenuItemInfo(parentMenuHandle(), m_id, FALSE, &itemInfo);
+}
+
+void QWindowsMenuItem::setText(const QString &text)
+{
+ qCDebug(lcQpaMenus).nospace().noquote()
+ << __FUNCTION__ << "(\"" << text << "\") " << this;
+ if (m_text == text)
+ return;
+ m_text = text;
+ if (m_parentMenu != nullptr)
+ updateText();
+}
+
+void QWindowsMenuItem::updateText()
+{
+ MENUITEMINFO menuItemInfo;
+ const QString &text = nativeText();
+ menuItemInfoSetText(menuItemInfo, text);
+ SetMenuItemInfo(parentMenuHandle(), m_id, FALSE, &menuItemInfo);
+}
+
+void QWindowsMenuItem::setMenu(QPlatformMenu *menuIn)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menuIn << ')' << this;
+ if (menuIn == m_subMenu)
+ return;
+ const uint oldId = m_id;
+ if (menuIn != nullptr) { // Set submenu
+ m_subMenu = static_cast<QWindowsMenu *>(menuIn);
+ m_subMenu->setAsItemSubMenu(this);
+ m_id = m_subMenu->id();
+ if (m_parentMenu != nullptr) {
+ ModifyMenu(m_parentMenu->menuHandle(), oldId, MF_BYCOMMAND | MF_POPUP,
+ m_id, qStringToWChar(m_text));
+ }
+ return;
+ }
+ // Clear submenu
+ m_subMenu = nullptr;
+ if (m_parentMenu != nullptr) {
+ m_id = nextId++;
+ ModifyMenu(m_parentMenu->menuHandle(), oldId, MF_BYCOMMAND,
+ m_id, qStringToWChar(m_text));
+ } else {
+ m_id = 0;
+ }
+}
+
+void QWindowsMenuItem::setVisible(bool isVisible)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << isVisible << ')' << this;
+ if (m_visible == isVisible)
+ return;
+ m_visible = isVisible;
+ if (m_parentMenu == nullptr)
+ return;
+ // Windows menu items do not implement settable visibility, we need to work
+ // around by removing the item from the menu. It will be kept in the list.
+ if (isVisible)
+ insertIntoMenuHelper(m_parentMenu, false, m_parentMenu->menuItems().indexOf(this));
+ else
+ RemoveMenu(parentMenuHandle(), m_id, MF_BYCOMMAND);
+}
+
+void QWindowsMenuItem::setIsSeparator(bool isSeparator)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << isSeparator << ')' << this;
+ if (m_separator == isSeparator)
+ return;
+ m_separator = isSeparator;
+}
+
+void QWindowsMenuItem::setCheckable(bool checkable)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << checkable << ')' << this;
+ if (m_checkable == checkable)
+ return;
+ m_checkable = checkable;
+ if (m_parentMenu == nullptr)
+ return;
+ UINT state = menuItemState(parentMenuHandle(), m_id, FALSE);
+ if (m_checkable)
+ state |= m_checked ? MF_CHECKED : MF_UNCHECKED;
+ else
+ state &= ~(MF_CHECKED | MF_UNCHECKED);
+ menuItemSetState(parentMenuHandle(), m_id, FALSE, state);
+}
+
+void QWindowsMenuItem::setChecked(bool isChecked)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << isChecked << ')' << this;
+ if (m_checked == isChecked)
+ return;
+ m_checked = isChecked;
+ // Convenience: Allow to set checkable by calling setChecked(true) for
+ // Quick Controls 1
+ if (isChecked)
+ m_checkable = true;
+ if (m_parentMenu == nullptr || !m_checkable)
+ return;
+ menuItemSetChangeState(parentMenuHandle(), m_id, FALSE, m_checked, MF_CHECKED, MF_UNCHECKED);
+}
+
+void QWindowsMenuItem::setShortcut(const QKeySequence &shortcut)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << shortcut << ')' << this;
+ if (m_shortcut == shortcut)
+ return;
+ m_shortcut = shortcut;
+ if (m_parentMenu != nullptr)
+ updateText();
+}
+
+void QWindowsMenuItem::setEnabled(bool enabled)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << enabled << ')' << this;
+ if (m_enabled == enabled)
+ return;
+ m_enabled = enabled;
+ if (m_parentMenu != nullptr)
+ menuItemSetChangeState(parentMenuHandle(), m_id, FALSE, m_enabled, MF_ENABLED, MF_GRAYED);
+}
+
+void QWindowsMenuItem::setIconSize(int size)
+{
+ if (m_iconSize == size)
+ return;
+ m_iconSize = size;
+ if (m_parentMenu != nullptr)
+ updateBitmap();
+}
+
+HMENU QWindowsMenuItem::parentMenuHandle() const
+{
+ return m_parentMenu ? m_parentMenu->menuHandle() : nullptr;
+}
+
+UINT QWindowsMenuItem::state() const
+{
+ if (m_separator)
+ return MF_SEPARATOR;
+ UINT result = MF_STRING | (m_enabled ? MF_ENABLED : MF_GRAYED);
+ if (m_subMenu != nullptr)
+ result |= MF_POPUP;
+ if (m_checkable)
+ result |= m_checked ? MF_CHECKED : MF_UNCHECKED;
+ if (QGuiApplication::layoutDirection() == Qt::RightToLeft)
+ result |= MFT_RIGHTORDER;
+ return result;
+}
+
+QString QWindowsMenuItem::nativeText() const
+{
+ QString result = m_text;
+ if (!m_shortcut.isEmpty()) {
+ result += QLatin1Char('\t');
+ result += m_shortcut.toString(QKeySequence::NativeText);
+ }
+ return result;
+}
+
+void QWindowsMenuItem::insertIntoMenu(QWindowsMenu *menu, bool append, int index)
+{
+ if (m_id == 0 && m_subMenu == nullptr)
+ m_id = nextId++;
+ insertIntoMenuHelper(menu, append, index);
+ m_parentMenu = menu;
+}
+
+void QWindowsMenuItem::insertIntoMenuHelper(QWindowsMenu *menu, bool append, int index)
+{
+ const QString &text = nativeText();
+
+ UINT_PTR idBefore = 0;
+ if (!append) {
+ // Skip over self (either newly inserted or when called from setVisible()
+ const int nextIndex = findNextVisibleEntry(menu->menuItems(), index + 1);
+ if (nextIndex != -1)
+ idBefore = menu->menuItems().at(nextIndex)->id();
+ }
+
+ if (idBefore)
+ InsertMenu(menu->menuHandle(), idBefore, state(), m_id, qStringToWChar(text));
+ else
+ AppendMenu(menu->menuHandle(), state(), m_id, qStringToWChar(text));
+
+ updateBitmap();
+}
+
+bool QWindowsMenuItem::removeFromMenu()
+{
+ if (QWindowsMenu *parentMenu = m_parentMenu) {
+ m_parentMenu = nullptr;
+ RemoveMenu(parentMenu->menuHandle(), m_id, MF_BYCOMMAND);
+ parentMenu->notifyRemoved(this);
+ return true;
+ }
+ return false;
+}
+
+// ------------ QWindowsMenu
+
+QWindowsMenu::QWindowsMenu() : QWindowsMenu(nullptr, CreateMenu())
+{
+}
+
+QWindowsMenu::QWindowsMenu(QWindowsMenu *parentMenu, HMENU menu)
+ : m_parentMenu(parentMenu)
+ , m_hMenu(menu)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this)
+ << "parentMenu=" << parentMenu << "HMENU=" << m_hMenu;
+}
+
+QWindowsMenu::~QWindowsMenu()
+{
+ qCDebug(lcQpaMenus).noquote().nospace() << __FUNCTION__
+ << " \"" <<m_text << "\", " << static_cast<const void *>(this);
+ for (int i = m_menuItems.size() - 1; i>= 0; --i)
+ m_menuItems.at(i)->removeFromMenu();
+ removeFromParent();
+ DestroyMenu(m_hMenu);
+}
+
+void QWindowsMenu::insertMenuItem(QPlatformMenuItem *menuItemIn, QPlatformMenuItem *before)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menuItemIn << ", before=" << before << ')' << this;
+ QWindowsMenuItem *menuItem = static_cast<QWindowsMenuItem *>(menuItemIn);
+ const int index = insertBefore(&m_menuItems, menuItemIn, before);
+ const bool append = index == m_menuItems.size() - 1;
+ menuItem->insertIntoMenu(this, append, index);
+}
+
+void QWindowsMenu::removeMenuItem(QPlatformMenuItem *menuItemIn)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menuItemIn << ')' << this;
+ static_cast<QWindowsMenuItem *>(menuItemIn)->removeFromMenu();
+}
+
+void QWindowsMenu::setText(const QString &text)
+{
+ qCDebug(lcQpaMenus).nospace().noquote()
+ << __FUNCTION__ << "(\"" << text << "\") " << this;
+ if (m_text == text)
+ return;
+ m_text = text;
+ if (!m_visible)
+ return;
+ const HMENU ph = parentHandle();
+ if (ph == nullptr)
+ return;
+ MENUITEMINFO menuItemInfo;
+ menuItemInfoSetText(menuItemInfo, m_text);
+ SetMenuItemInfo(ph, id(), FALSE, &menuItemInfo);
+}
+
+void QWindowsMenu::setIcon(const QIcon &icon)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << icon << ')' << this;
+ m_icon = icon;
+}
+
+void QWindowsMenu::setEnabled(bool enabled)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << enabled << ')' << this;
+ if (m_enabled == enabled)
+ return;
+ m_enabled = enabled;
+ if (!m_visible)
+ return;
+ if (const HMENU ph = parentHandle())
+ menuItemSetChangeState(ph, id(), FALSE, m_enabled, MF_ENABLED, MF_GRAYED);
+}
+
+QWindowsMenuItem *QWindowsMenu::itemForSubMenu(const QWindowsMenu *subMenu) const
+{
+ const auto it = std::find_if(m_menuItems.cbegin(), m_menuItems.cend(),
+ [subMenu] (const QWindowsMenuItem *i) { return i->subMenu() == subMenu; });
+ return it != m_menuItems.cend() ? *it : nullptr;
+}
+
+void QWindowsMenu::insertIntoMenuBar(QWindowsMenuBar *bar, bool append, int index)
+{
+ UINT_PTR idBefore = 0;
+ if (!append) {
+ // Skip over self (either newly inserted or when called from setVisible()
+ const int nextIndex = findNextVisibleEntry(bar->menus(), index + 1);
+ if (nextIndex != -1)
+ idBefore = bar->menus().at(nextIndex)->id();
+ }
+ m_parentMenuBar = bar;
+ m_parentMenu = nullptr;
+ if (idBefore)
+ InsertMenu(bar->menuBarHandle(), idBefore, MF_POPUP | MF_BYCOMMAND, id(), qStringToWChar(m_text));
+ else
+ AppendMenu(bar->menuBarHandle(), MF_POPUP, id(), qStringToWChar(m_text));
+}
+
+bool QWindowsMenu::removeFromParent()
+{
+ if (QWindowsMenuBar *bar = m_parentMenuBar) {
+ m_parentMenuBar = nullptr;
+ bar->notifyRemoved(this);
+ return RemoveMenu(bar->menuBarHandle(), id(), MF_BYCOMMAND) == TRUE;
+ }
+ if (QWindowsMenu *menu = m_parentMenu) {
+ m_parentMenu = nullptr;
+ QWindowsMenuItem *item = menu->itemForSubMenu(this);
+ if (item)
+ item->setMenu(nullptr);
+ return item != nullptr;
+ }
+ return false;
+}
+
+void QWindowsMenu::setVisible(bool visible)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << visible << ')' << this;
+ if (m_visible == visible)
+ return;
+ m_visible = visible;
+ const HMENU ph = parentHandle();
+ if (ph == nullptr)
+ return;
+ // Windows menus do not implement settable visibility, we need to work
+ // around by removing the menu from the parent. It will be kept in the list.
+ if (visible) {
+ if (m_parentMenuBar)
+ insertIntoMenuBar(m_parentMenuBar, false, m_parentMenuBar->menus().indexOf(this));
+ } else {
+ RemoveMenu(ph, id(), MF_BYCOMMAND);
+ }
+ if (m_parentMenuBar)
+ m_parentMenuBar->redraw();
+}
+
+QPlatformMenuItem *QWindowsMenu::menuItemAt(int position) const
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << position;
+ return position >= 0 && position < m_menuItems.size()
+ ? m_menuItems.at(position) : nullptr;
+}
+
+QPlatformMenuItem *QWindowsMenu::menuItemForTag(quintptr tag) const
+{
+ return traverseMenuItems(this, [tag] (const QPlatformMenuItem *i) { return i->tag() == tag; });
+}
+
+QPlatformMenuItem *QWindowsMenu::createMenuItem() const
+{
+ QPlatformMenuItem *result = new QWindowsMenuItem;
+ qCDebug(lcQpaMenus) << __FUNCTION__ << this << "returns" << result;
+ return result;
+}
+
+QPlatformMenu *QWindowsMenu::createSubMenu() const
+{
+ QPlatformMenu *result = new QWindowsMenu;
+ qCDebug(lcQpaMenus) << __FUNCTION__ << this << "returns" << result;
+ return result;
+}
+
+void QWindowsMenu::setAsItemSubMenu(QWindowsMenuItem *item)
+{
+ m_parentMenu = item->parentMenu();
+}
+
+HMENU QWindowsMenu::parentMenuHandle() const
+{
+ return m_parentMenu ? m_parentMenu->menuHandle() : nullptr;
+}
+
+HMENU QWindowsMenu::parentMenuBarHandle() const
+{
+ return m_parentMenuBar ? m_parentMenuBar->menuBarHandle() : nullptr;
+}
+
+HMENU QWindowsMenu::parentHandle() const
+{
+ if (m_parentMenuBar)
+ return m_parentMenuBar->menuBarHandle();
+ if (m_parentMenu)
+ return m_parentMenu->menuHandle();
+ return nullptr;
+}
+
+// --------------- QWindowsPopupMenu
+
+static QPointer<QWindowsPopupMenu> lastShownPopupMenu;
+
+QWindowsPopupMenu::QWindowsPopupMenu() : QWindowsMenu(nullptr, CreatePopupMenu())
+{
+}
+
+void QWindowsPopupMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect,
+ const QPlatformMenuItem *item)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '>' << this << parentWindow << targetRect << item;
+ const QWindowsBaseWindow *window = static_cast<const QWindowsBaseWindow *>(parentWindow->handle());
+ const QPoint globalPos = window->mapToGlobal(targetRect.topLeft());
+ trackPopupMenu(window->handle(), globalPos.x(), globalPos.y());
+}
+
+bool QWindowsPopupMenu::trackPopupMenu(HWND windowHandle, int x, int y)
+{
+ lastShownPopupMenu = this;
+ return TrackPopupMenu(menuHandle(),
+ QGuiApplication::layoutDirection() == Qt::RightToLeft ? UINT(TPM_RIGHTALIGN) : UINT(0),
+ x, y, 0, windowHandle, nullptr) == TRUE;
+}
+
+bool QWindowsPopupMenu::notifyTriggered(uint id)
+{
+ QPlatformMenuItem *result = lastShownPopupMenu.isNull()
+ ? nullptr
+ : findMenuItemById(lastShownPopupMenu.data(), id);
+ if (result != nullptr) {
+ qCDebug(lcQpaMenus) << __FUNCTION__ << "id=" << id;
+ emit result->activated();
+ }
+ lastShownPopupMenu = nullptr;
+ return result != nullptr;
+}
+
+bool QWindowsPopupMenu::notifyAboutToShow(HMENU hmenu)
+{
+ if (lastShownPopupMenu.isNull())
+ return false;
+ if (lastShownPopupMenu->menuHandle() == hmenu) {
+ emit lastShownPopupMenu->aboutToShow();
+ return true;
+ }
+ if (QWindowsMenu *menu = findMenuByHandle(lastShownPopupMenu.data(), hmenu)) {
+ emit menu->aboutToShow();
+ return true;
+ }
+ return false;
+}
+
+// --------------- QWindowsMenuBar
+
+QWindowsMenuBar::QWindowsMenuBar() : m_hMenuBar(CreateMenu())
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this);
+}
+
+QWindowsMenuBar::~QWindowsMenuBar()
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this);
+ for (int m = m_menus.size() - 1; m >= 0; --m)
+ m_menus.at(m)->removeFromParent();
+ removeFromWindow();
+ DestroyMenu(m_hMenuBar);
+}
+
+void QWindowsMenuBar::insertMenu(QPlatformMenu *menuIn, QPlatformMenu *before)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << menuIn << "before=" << before;
+ QWindowsMenu *menu = static_cast<QWindowsMenu *>(menuIn);
+ const int index = insertBefore(&m_menus, menuIn, before);
+ menu->insertIntoMenuBar(this, index == m_menus.size() - 1, index);
+}
+
+void QWindowsMenuBar::removeMenu(QPlatformMenu *menu)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menu << ')' << this;
+ const int index = indexOf(m_menus, menu);
+ if (index != -1)
+ m_menus[index]->removeFromParent();
+}
+
+// When calling handleReparent() for a QWindow instances that does not have
+// a platform window yet, set the menubar as dynamic property to be installed
+// on platform window creation.
+static const char menuBarPropertyName[] = "_q_windowsNativeMenuBar";
+
+void QWindowsMenuBar::handleReparent(QWindow *newParentWindow)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << newParentWindow << ')' << this;
+ if (newParentWindow == nullptr) {
+ removeFromWindow();
+ return; // Happens during Quick Controls 1 property setup
+ }
+ if (QPlatformWindow *platWin = newParentWindow->handle())
+ install(static_cast<QWindowsWindow *>(platWin));
+ else // Store for later creation, see menuBarOf()
+ newParentWindow->setProperty(menuBarPropertyName, qVariantFromValue<QObject *>(this));
+}
+
+QWindowsMenuBar *QWindowsMenuBar::menuBarOf(const QWindow *notYetCreatedWindow)
+{
+ const QVariant menuBarV = notYetCreatedWindow->property(menuBarPropertyName);
+ return menuBarV.canConvert<QObject *>()
+ ? qobject_cast<QWindowsMenuBar *>(menuBarV.value<QObject *>()) : nullptr;
+}
+
+static inline void forceNcCalcSize(HWND hwnd)
+{
+ // Force WM_NCCALCSIZE to adjust margin: Does not appear to work?
+ SetWindowPos(hwnd, 0, 0, 0, 0, 0,
+ SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
+}
+
+void QWindowsMenuBar::install(QWindowsWindow *window)
+{
+ const HWND hwnd = window->handle();
+ const BOOL result = SetMenu(hwnd, m_hMenuBar);
+ if (result) {
+ window->setMenuBar(this);
+ forceNcCalcSize(hwnd);
+ }
+}
+
+void QWindowsMenuBar::removeFromWindow()
+{
+ if (QWindowsWindow *window = platformWindow()) {
+ const HWND hwnd = window->handle();
+ SetMenu(hwnd, nullptr);
+ window->setMenuBar(nullptr);
+ forceNcCalcSize(hwnd);
+ }
+}
+
+QPlatformMenu *QWindowsMenuBar::menuForTag(quintptr tag) const
+{
+ return traverseMenus(this, [tag] (const QWindowsMenu *m) { return m->tag() == tag; });
+}
+
+QPlatformMenu *QWindowsMenuBar::createMenu() const
+{
+ QPlatformMenu *result = new QWindowsMenu;
+ qCDebug(lcQpaMenus) << __FUNCTION__ << this << "returns" << result;
+ return result;
+}
+
+bool QWindowsMenuBar::notifyTriggered(uint id)
+{
+ QPlatformMenuItem *result = findMenuItemById(this, id);
+ if (result != nullptr) {
+ qCDebug(lcQpaMenus) << __FUNCTION__ << "id=" << id;
+ emit result->activated();
+ }
+ return result != nullptr;
+}
+
+bool QWindowsMenuBar::notifyAboutToShow(HMENU hmenu)
+{
+ if (QWindowsMenu *menu = findMenuByHandle(this, hmenu)) {
+ emit menu->aboutToShow();
+ return true;
+ }
+ return false;
+}
+
+QWindowsWindow *QWindowsMenuBar::platformWindow() const
+{
+ if (const QWindowsContext *ctx = QWindowsContext::instance()) {
+ if (QWindowsWindow *w = ctx->findPlatformWindow(this))
+ return w;
+ }
+ return nullptr;
+}
+
+void QWindowsMenuBar::redraw() const
+{
+ if (const QWindowsWindow *window = platformWindow())
+ DrawMenuBar(window->handle());
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+template <class M> /* Menu[Item] */
+static void formatTextSequence(QDebug &d, const QVector<M *> &v)
+{
+ if (const int size = v.size()) {
+ d << '[' << size << "](";
+ for (int i = 0; i < size; ++i) {
+ if (i)
+ d << ", ";
+ if (!v.at(i)->isVisible())
+ d << "[hidden] ";
+ d << '"' << v.at(i)->text() << '"';
+ }
+ d << ')';
+ }
+}
+
+void QWindowsMenuItem::formatDebug(QDebug &d) const
+{
+ if (m_separator)
+ d << "separator, ";
+ else
+ d << '"' << m_text << "\", ";
+ d << static_cast<const void *>(this);
+ if (m_parentMenu)
+ d << ", parentMenu=" << static_cast<const void *>(m_parentMenu);
+ if (m_subMenu)
+ d << ", subMenu=" << static_cast<const void *>(m_subMenu);
+ d << ", tag=" << showbase << hex
+ << tag() << noshowbase << dec << ", id=" << m_id;
+ if (!m_shortcut.isEmpty())
+ d << ", shortcut=" << m_shortcut;
+ if (m_visible)
+ d << " [visible]";
+ if (m_enabled)
+ d << " [enabled]";
+ if (m_checkable)
+ d << ", checked=" << m_checked;
+}
+
+QDebug operator<<(QDebug d, const QPlatformMenuItem *i)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d.noquote();
+ d << "QPlatformMenuItem(";
+ if (i)
+ static_cast<const QWindowsMenuItem *>(i)->formatDebug(d);
+ else
+ d << '0';
+ d << ')';
+ return d;
+}
+
+void QWindowsMenu::formatDebug(QDebug &d) const
+{
+ d << '"' << m_text << "\", " << static_cast<const void *>(this)
+ << ", handle=" << m_hMenu;
+ if (m_parentMenuBar != nullptr)
+ d << " [on menubar]";
+ if (m_parentMenu != nullptr)
+ d << " [on menu]";
+ if (tag())
+ d << ", tag=" << showbase << hex << tag() << noshowbase << dec;
+ if (m_visible)
+ d << " [visible]";
+ if (m_enabled)
+ d << " [enabled]";
+ d <<' ';
+ formatTextSequence(d, m_menuItems);
+}
+
+void QWindowsMenuBar::formatDebug(QDebug &d) const
+{
+ d << static_cast<const void *>(this) << ' ';
+ formatTextSequence(d, m_menus);
+}
+
+QDebug operator<<(QDebug d, const QPlatformMenu *m)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d.noquote();
+ if (m) {
+ d << m->metaObject()->className() << '(';
+ static_cast<const QWindowsMenu *>(m)->formatDebug(d);
+ d << ')';
+ } else {
+ d << "QPlatformMenu(0)";
+ }
+ return d;
+}
+
+QDebug operator<<(QDebug d, const QPlatformMenuBar *mb)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d.noquote();
+ d << "QPlatformMenuBar(";
+ if (mb)
+ static_cast<const QWindowsMenuBar *>(mb)->formatDebug(d);
+ else
+ d << '0';
+ d << ')';
+ return d;
+}
+
+#endif // !QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowsmenu.h b/src/plugins/platforms/windows/qwindowsmenu.h
new file mode 100644
index 0000000000..d51a29676e
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowsmenu.h
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSMENU_H
+#define QWINDOWSMENU_H
+
+#include "qtwindowsglobal.h"
+
+#include <qpa/qplatformmenu.h>
+
+#include <QtCore/QVector>
+#include <QtCore/QPair>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+
+class QWindowsMenu;
+class QWindowsMenuBar;
+class QWindowsWindow;
+
+class QWindowsMenuItem : public QPlatformMenuItem
+{
+ Q_OBJECT
+public:
+ explicit QWindowsMenuItem(QWindowsMenu *parentMenu = nullptr);
+ ~QWindowsMenuItem();
+
+ void setText(const QString &text) override;
+ void setIcon(const QIcon &icon) override;
+ void setMenu(QPlatformMenu *menu) override;
+ void setVisible(bool isVisible) override;
+ void setIsSeparator(bool isSeparator) override;
+ void setFont(const QFont &) override {}
+ void setRole(MenuRole) override {}
+ void setCheckable(bool checkable) override;
+ void setChecked(bool isChecked) override;
+#ifndef QT_NO_SHORTCUT
+ void setShortcut(const QKeySequence& shortcut) override;
+#endif
+ void setEnabled(bool enabled) override;
+ void setIconSize(int size) override;
+
+ const QWindowsMenu *parentMenu() const { return m_parentMenu; }
+ QWindowsMenu *parentMenu() { return m_parentMenu; }
+ HMENU parentMenuHandle() const;
+ QWindowsMenu *subMenu() const { return m_subMenu; }
+ UINT_PTR id() const { return m_id; }
+ void setId(uint id) { m_id = id; }
+ UINT state() const;
+ QString text() const { return m_text; }
+ QString nativeText() const;
+ bool isVisible() const { return m_visible; }
+
+ void insertIntoMenu(QWindowsMenu *menuItem, bool append, int index);
+ bool removeFromMenu();
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+
+private:
+ void updateBitmap();
+ void freeBitmap();
+ void updateText();
+ void insertIntoMenuHelper(QWindowsMenu *menu, bool append, int index);
+
+ QWindowsMenu *m_parentMenu = nullptr;
+ QWindowsMenu *m_subMenu = nullptr;
+ UINT_PTR m_id; // Windows Id sent as wParam with WM_COMMAND or submenu handle.
+ QString m_text;
+ QIcon m_icon;
+ HBITMAP m_hbitmap = nullptr;
+ int m_iconSize = 0;
+ bool m_separator = false;
+ bool m_visible = true;
+ bool m_checkable = false;
+ bool m_checked = false;
+ bool m_enabled = true;
+ QKeySequence m_shortcut;
+};
+
+class QWindowsMenu : public QPlatformMenu
+{
+ Q_OBJECT
+public:
+ typedef QVector<QWindowsMenuItem *> MenuItems;
+
+ QWindowsMenu();
+ ~QWindowsMenu();
+
+ void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) override;
+ void removeMenuItem(QPlatformMenuItem *menuItem) override;
+ void syncMenuItem(QPlatformMenuItem *) override {}
+ void syncSeparatorsCollapsible(bool) override {}
+
+ void setText(const QString &text) override;
+ void setIcon(const QIcon &icon) override;
+ void setEnabled(bool enabled) override;
+ bool isEnabled() const override { return m_enabled; }
+ void setVisible(bool visible) override;
+
+ QPlatformMenuItem *menuItemAt(int position) const override;
+ QPlatformMenuItem *menuItemForTag(quintptr tag) const override;
+
+ QPlatformMenuItem *createMenuItem() const override;
+ QPlatformMenu *createSubMenu() const override;
+
+ HMENU menuHandle() const { return m_hMenu; }
+ UINT_PTR id() const { return reinterpret_cast<UINT_PTR>(m_hMenu); }
+ QString text() const { return m_text; }
+ const MenuItems &menuItems() const { return m_menuItems; }
+ QWindowsMenuItem *itemForSubMenu(const QWindowsMenu *subMenu) const;
+
+ const QWindowsMenuBar *parentMenuBar() const { return m_parentMenuBar; }
+ HMENU parentMenuBarHandle() const;
+ const QWindowsMenu *parentMenu() const { return m_parentMenu; }
+ void setAsItemSubMenu(QWindowsMenuItem *item);
+ void notifyRemoved(QWindowsMenuItem *item) { m_menuItems.removeOne(item); }
+ HMENU parentMenuHandle() const;
+ HMENU parentHandle() const;
+ bool isVisible() const { return m_visible; }
+ void insertIntoMenuBar(QWindowsMenuBar *bar, bool append, int index);
+ bool removeFromParent();
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+
+protected:
+ explicit QWindowsMenu(QWindowsMenu *parentMenu, HMENU menu);
+
+private:
+ QWindowsMenuBar *m_parentMenuBar = nullptr;
+ QWindowsMenu *m_parentMenu = nullptr;
+ MenuItems m_menuItems;
+ HMENU m_hMenu = nullptr;
+ QString m_text;
+ QIcon m_icon;
+ bool m_visible = true;
+ bool m_enabled = true;
+};
+
+class QWindowsPopupMenu : public QWindowsMenu
+{
+ Q_OBJECT
+public:
+ QWindowsPopupMenu();
+
+ static bool notifyTriggered(uint id);
+ static bool notifyAboutToShow(HMENU hmenu);
+
+ void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) override;
+ void dismiss() override {}
+
+ bool trackPopupMenu(HWND windowHandle, int x, int y);
+};
+
+class QWindowsMenuBar : public QPlatformMenuBar
+{
+ Q_OBJECT
+public:
+ typedef QVector<QWindowsMenu *> Menus;
+
+ QWindowsMenuBar();
+ ~QWindowsMenuBar();
+
+ void insertMenu(QPlatformMenu *menu, QPlatformMenu *before) override;
+ void removeMenu(QPlatformMenu *menu) override;
+ void syncMenu(QPlatformMenu *) override {}
+ void handleReparent(QWindow *newParentWindow) override;
+
+ QPlatformMenu *menuForTag(quintptr tag) const override;
+ QPlatformMenu *createMenu() const override;
+
+ HMENU menuBarHandle() const { return m_hMenuBar; }
+ const Menus &menus() const { return m_menus; }
+ bool notifyTriggered(uint id);
+ bool notifyAboutToShow(HMENU hmenu);
+ void notifyRemoved(QWindowsMenu *menu) { m_menus.removeOne(menu); }
+ void redraw() const;
+
+ void install(QWindowsWindow *window);
+
+ static QWindowsMenuBar *menuBarOf(const QWindow *notYetCreatedWindow);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+
+private:
+ QWindowsWindow *platformWindow() const;
+ void removeFromWindow();
+
+ Menus m_menus;
+ HMENU m_hMenuBar = nullptr;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const QPlatformMenuItem *);
+QDebug operator<<(QDebug d, const QPlatformMenu *);
+QDebug operator<<(QDebug d, const QPlatformMenuBar *);
+#endif // !QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSMENU_H
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index e1cbbc9311..5fe7c7c071 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -44,6 +44,7 @@
#endif
#include "qwindowstheme.h"
+#include "qwindowsmenu.h"
#include "qwindowsdialoghelpers.h"
#include "qwindowscontext.h"
#include "qwindowsintegration.h"
@@ -910,4 +911,55 @@ QIcon QWindowsTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOpt
return QIcon(new QWindowsFileIconEngine(fileInfo, iconOptions));
}
+static inline bool doUseNativeMenus()
+{
+ const unsigned options = QWindowsIntegration::instance()->options();
+ if ((options & QWindowsIntegration::NoNativeMenus) != 0)
+ return false;
+ if ((options & QWindowsIntegration::AlwaysUseNativeMenus) != 0)
+ return true;
+ // "Auto" mode: For non-widget or Quick Controls 2 applications
+ if (!QCoreApplication::instance()->inherits("QApplication"))
+ return true;
+ const QWindowList &topLevels = QGuiApplication::topLevelWindows();
+ for (const QWindow *t : topLevels) {
+ if (t->inherits("QQuickApplicationWindow"))
+ return true;
+ }
+ return false;
+}
+
+bool QWindowsTheme::useNativeMenus()
+{
+ static const bool result = doUseNativeMenus();
+ return result;
+}
+
+QPlatformMenuItem *QWindowsTheme::createPlatformMenuItem() const
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__;
+ return QWindowsTheme::useNativeMenus() ? new QWindowsMenuItem : nullptr;
+}
+
+QPlatformMenu *QWindowsTheme::createPlatformMenu() const
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__;
+ // We create a popup menu here, since it will likely be used as context
+ // menu. Submenus should be created the factory functions of
+ // QPlatformMenu/Bar. Note though that Quick Controls 1 will use this
+ // function for submenus as well, but this has been found to work.
+ return QWindowsTheme::useNativeMenus() ? new QWindowsPopupMenu : nullptr;
+}
+
+QPlatformMenuBar *QWindowsTheme::createPlatformMenuBar() const
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__;
+ return QWindowsTheme::useNativeMenus() ? new QWindowsMenuBar : nullptr;
+}
+
+void QWindowsTheme::showPlatformMenuBar()
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowstheme.h b/src/plugins/platforms/windows/qwindowstheme.h
index a3019ff6eb..7ce3023111 100644
--- a/src/plugins/platforms/windows/qwindowstheme.h
+++ b/src/plugins/platforms/windows/qwindowstheme.h
@@ -74,6 +74,13 @@ public:
QList<QSize> availableFileIconSizes() const { return m_fileIconSizes; }
+ QPlatformMenuItem *createPlatformMenuItem() const override;
+ QPlatformMenu *createPlatformMenu() const override;
+ QPlatformMenuBar *createPlatformMenuBar() const override;
+ void showPlatformMenuBar() override;
+
+ static bool useNativeMenus();
+
static const char *name;
private:
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index a3337509a7..79ae4c004b 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -42,6 +42,7 @@
#include "qwindowsdrag.h"
#include "qwindowsscreen.h"
#include "qwindowsintegration.h"
+#include "qwindowsmenu.h"
#include "qwindowsnativeinterface.h"
#if QT_CONFIG(dynamicgl)
# include "qwindowsglcontext.h"
@@ -1016,6 +1017,8 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w,
const QMargins effectiveMargins = margins + customMargins;
frameWidth = effectiveMargins.left() + geometry.width() + effectiveMargins.right();
frameHeight = effectiveMargins.top() + geometry.height() + effectiveMargins.bottom();
+ if (QWindowsMenuBar::menuBarOf(w) != nullptr)
+ frameHeight += GetSystemMetrics(SM_CYMENU);
const bool isDefaultPosition = !frameX && !frameY && w->isTopLevel();
if (!QWindowsGeometryHint::positionIncludesFrame(w) && !isDefaultPosition) {
frameX -= effectiveMargins.left();
@@ -2446,6 +2449,16 @@ bool QWindowsWindow::isTopLevel() const
return window()->isTopLevel() && !m_data.embedded;
}
+QWindowsMenuBar *QWindowsWindow::menuBar() const
+{
+ return m_menuBar.data();
+}
+
+void QWindowsWindow::setMenuBar(QWindowsMenuBar *mb)
+{
+ m_menuBar = mb;
+}
+
/*!
\brief Sets custom margins to be added to the default margins determined by
the windows style in the handling of the WM_NCCALCSIZE message.
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index 46236c10b5..540d0ff98c 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -41,6 +41,7 @@
#define QWINDOWSWINDOW_H
#include <QtCore/qt_windows.h>
+#include <QtCore/QPointer>
#include "qwindowscursor.h"
#include <qpa/qplatformwindow.h>
@@ -53,6 +54,7 @@
QT_BEGIN_NAMESPACE
class QWindowsOleDropTarget;
+class QWindowsMenuBar;
class QDebug;
struct QWindowsGeometryHint
@@ -270,6 +272,9 @@ public:
HWND handle() const override { return m_data.hwnd; }
bool isTopLevel() const override;
+ QWindowsMenuBar *menuBar() const;
+ void setMenuBar(QWindowsMenuBar *mb);
+
QMargins customMargins() const { return m_data.customMargins; }
void setCustomMargins(const QMargins &m);
@@ -342,6 +347,7 @@ private:
void fireExpose(const QRegion &region, bool force=false);
mutable QWindowsWindowData m_data;
+ QPointer<QWindowsMenuBar> m_menuBar;
mutable unsigned m_flags = WithinCreate;
HDC m_hdc = 0;
Qt::WindowStates m_windowState = Qt::WindowNoState;
diff --git a/src/plugins/platforms/windows/windows.pri b/src/plugins/platforms/windows/windows.pri
index 7694824d20..a7166e90e4 100644
--- a/src/plugins/platforms/windows/windows.pri
+++ b/src/plugins/platforms/windows/windows.pri
@@ -22,6 +22,7 @@ SOURCES += \
$$PWD/qwindowscursor.cpp \
$$PWD/qwindowsinputcontext.cpp \
$$PWD/qwindowstheme.cpp \
+ $$PWD/qwindowsmenu.cpp \
$$PWD/qwindowsdialoghelpers.cpp \
$$PWD/qwindowsservices.cpp \
$$PWD/qwindowsnativeinterface.cpp \
@@ -43,6 +44,7 @@ HEADERS += \
$$PWD/qwindowscursor.h \
$$PWD/qwindowsinputcontext.h \
$$PWD/qwindowstheme.h \
+ $$PWD/qwindowsmenu.h \
$$PWD/qwindowsdialoghelpers.h \
$$PWD/qwindowsservices.h \
$$PWD/qwindowsnativeinterface.h \