Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwindowsmenu.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qwindowsmenu.h"
8
9#include <QtGui/qwindow.h>
10#include <QtGui/private/qpixmap_win_p.h>
11#include <QtCore/qdebug.h>
12#include <QtCore/qvariant.h>
13#include <QtCore/qmetaobject.h>
14#include <QtCore/qpointer.h>
15
16#include <algorithm>
17
18QT_BEGIN_NAMESPACE
19
20/*!
21 \class QWindowsMenuBar
22 \brief Windows native menu bar
23
24 \list
25 \li \l{https://docs.microsoft.com/en-us/windows/win32/menurc/about-menus},
26 \e{About Menus}
27 \endlist
28
29 \note The destruction order of the QWindowsMenu/Item/Bar instances is
30 arbitrary depending on whether the application is Qt Quick or
31 Qt Widgets, either the containers or the items might be deleted first.
32
33 \internal
34*/
35
36static uint nextId = 1;
37
38// Find a QPlatformMenu[Item]* in a list of QWindowsMenu[Item], where
39// QList::indexOf() cannot be used since it wants a QWindowsMenu[Item]*
40template <class Derived, class Needle>
41static int indexOf(const QList<Derived *> &v, const Needle *needle)
42{
43 for (int i = 0, size = v.size(); i < size; ++i) {
44 if (v.at(i) == needle)
45 return i;
46 }
47 return -1;
48}
49
50// Helper for inserting a QPlatformMenu[Item]* into a list of QWindowsMenu[Item].
51template <class Derived, class Base>
52static int insertBefore(QList<Derived *> *v, Base *newItemIn, const Base *before = nullptr)
53{
54 int index = before ? indexOf(*v, before) : -1;
55 if (index != -1) {
56 v->insert(index, static_cast<Derived *>(newItemIn));
57 } else {
58 index = v->size();
59 v->append(static_cast<Derived *>(newItemIn));
60 }
61 return index;
62}
63
64static inline const wchar_t *qStringToWChar(const QString &s)
65{
66 return reinterpret_cast<const wchar_t *>(s.utf16());
67}
68
69// Traverse menu and return the item for which predicate
70// "bool Function(QWindowsMenuItem *)" returns true
71template <class Predicate>
72static QWindowsMenuItem *traverseMenuItems(const QWindowsMenu *menu, Predicate p)
73{
74 const QWindowsMenu::MenuItems &items = menu->menuItems();
75 for (QWindowsMenuItem *item : items) {
76 if (p(item))
77 return item;
78 if (item->subMenu()) {
79 if (QWindowsMenuItem *subMenuItem = traverseMenuItems(item->subMenu(), p))
80 return subMenuItem;
81 }
82 }
83 return nullptr;
84}
85
86// Traverse menu bar return the item for which predicate
87// "bool Function(QWindowsMenuItem *)" returns true
88template <class Predicate>
89static QWindowsMenuItem *traverseMenuItems(const QWindowsMenuBar *menuBar, Predicate p)
90{
91 const QWindowsMenuBar::Menus &menus = menuBar->menus();
92 for (QWindowsMenu *menu : menus) {
93 if (QWindowsMenuItem *item = traverseMenuItems(menu, p))
94 return item;
95 }
96 return nullptr;
97}
98
99template <class Menu /* Menu[Bar] */>
100static QWindowsMenuItem *findMenuItemById(const Menu *menu, uint id)
101{
102 return traverseMenuItems(menu, [id] (const QWindowsMenuItem *i) { return i->id() == id; });
103}
104
105// Traverse menu and return the menu for which predicate
106// "bool Function(QWindowsMenu *)" returns true
107template <class Predicate>
108static QWindowsMenu *traverseMenus(const QWindowsMenu *menu, Predicate p)
109{
110 const QWindowsMenu::MenuItems &items = menu->menuItems();
111 for (QWindowsMenuItem *item : items) {
112 if (QWindowsMenu *subMenu = item->subMenu()) {
113 if (p(subMenu))
114 return subMenu;
115 if (QWindowsMenu *menu = traverseMenus(subMenu, p))
116 return menu;
117 }
118 }
119 return nullptr;
120}
121
122// Traverse menu bar return the item for which
123// function "bool Function(QWindowsMenu *)" returns true
124template <class Predicate>
125static QWindowsMenu *traverseMenus(const QWindowsMenuBar *menuBar, Predicate p)
126{
127 const QWindowsMenuBar::Menus &menus = menuBar->menus();
128 for (QWindowsMenu *menu : menus) {
129 if (p(menu))
130 return menu;
131 if (QWindowsMenu *subMenu = traverseMenus(menu, p))
132 return subMenu;
133 }
134 return nullptr;
135}
136
137template <class Menu /* Menu[Bar] */>
138static QWindowsMenu *findMenuByHandle(const Menu *menu, HMENU hMenu)
139{
140 return traverseMenus(menu, [hMenu] (const QWindowsMenu *i) { return i->menuHandle() == hMenu; });
141}
142
143template <class MenuType>
144static int findNextVisibleEntry(const QList<MenuType *> &entries, int pos)
145{
146 for (int i = pos, size = entries.size(); i < size; ++i) {
147 if (entries.at(i)->isVisible())
148 return i;
149 }
150 return -1;
151}
152
153static inline void menuItemInfoInit(MENUITEMINFO &menuItemInfo)
154{
155 memset(&menuItemInfo, 0, sizeof(MENUITEMINFO));
156 menuItemInfo.cbSize = sizeof(MENUITEMINFO);
157}
158
159static inline void menuItemInfoSetText(MENUITEMINFO &menuItemInfo, const QString &text)
160{
161 menuItemInfoInit(menuItemInfo);
162 menuItemInfo.fMask = MIIM_STRING;
163 menuItemInfo.dwTypeData = const_cast<wchar_t *>(qStringToWChar(text));
164 menuItemInfo.cch = UINT(text.size());
165}
166
167static UINT menuItemState(HMENU hMenu, UINT uItem, BOOL fByPosition)
168{
169 MENUITEMINFO menuItemInfo;
170 menuItemInfoInit(menuItemInfo);
171 menuItemInfo.fMask = MIIM_STATE;
172 return GetMenuItemInfo(hMenu, uItem, fByPosition, &menuItemInfo) == TRUE ? menuItemInfo.fState : 0;
173}
174
175static void menuItemSetState(HMENU hMenu, UINT uItem, BOOL fByPosition, UINT flags)
176{
177 MENUITEMINFO menuItemInfo;
178 menuItemInfoInit(menuItemInfo);
179 menuItemInfo.fMask = MIIM_STATE;
180 menuItemInfo.fState = flags;
181 SetMenuItemInfo(hMenu, uItem, fByPosition, &menuItemInfo);
182}
183
184static void menuItemSetChangeState(HMENU hMenu, UINT uItem, BOOL fByPosition,
185 bool value, UINT trueState, UINT falseState)
186{
187 const UINT oldState = menuItemState(hMenu, uItem, fByPosition);
188 UINT newState = oldState;
189 if (value) {
190 newState |= trueState;
191 newState &= ~falseState;
192 } else {
193 newState &= ~trueState;
194 newState |= falseState;
195 }
196 if (oldState != newState)
197 menuItemSetState(hMenu, uItem, fByPosition, newState);
198}
199
200// ------------ QWindowsMenuItem
201QWindowsMenuItem::QWindowsMenuItem(QWindowsMenu *parentMenu)
202 : m_parentMenu(parentMenu)
203 , m_id(0)
204{
205 qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this)
206 << "parentMenu=" << parentMenu;
207}
208
210{
211 qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this);
213 freeBitmap();
214}
215
216void QWindowsMenuItem::freeBitmap()
217{
218 if (m_hbitmap) {
219 DeleteObject(m_hbitmap);
220 m_hbitmap = nullptr;
221 }
222}
223
224void QWindowsMenuItem::setIcon(const QIcon &icon)
225{
226 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << icon << ')' << this;
227 if (m_icon.cacheKey() == icon.cacheKey())
228 return;
229 m_icon = icon;
230 if (m_parentMenu != nullptr)
231 updateBitmap();
232}
233
234void QWindowsMenuItem::updateBitmap()
235{
236 freeBitmap();
237 if (!m_icon.isNull()) {
238 const int size = m_iconSize ? m_iconSize : GetSystemMetrics(SM_CYMENUCHECK);
239 // native Win32 menus don't support high-DPI versions of icons, so always
240 // use the pixmap for a 1.0 scale factor.
241 m_hbitmap = qt_pixmapToWinHBITMAP(m_icon.pixmap(QSize(size, size), 1.0), 1);
242 }
243 MENUITEMINFO itemInfo;
244 menuItemInfoInit(itemInfo);
245 itemInfo.fMask = MIIM_BITMAP;
246 itemInfo.hbmpItem = m_hbitmap;
247 SetMenuItemInfo(parentMenuHandle(), m_id, FALSE, &itemInfo);
248}
249
250void QWindowsMenuItem::setText(const QString &text)
251{
252 qCDebug(lcQpaMenus).nospace().noquote()
253 << __FUNCTION__ << "(\"" << text << "\") " << this;
254 if (m_text == text)
255 return;
256 m_text = text;
257 if (m_parentMenu != nullptr)
258 updateText();
259}
260
261void QWindowsMenuItem::updateText()
262{
263 MENUITEMINFO menuItemInfo;
264 const QString &text = nativeText();
265 menuItemInfoSetText(menuItemInfo, text);
266 SetMenuItemInfo(parentMenuHandle(), m_id, FALSE, &menuItemInfo);
267}
268
269void QWindowsMenuItem::setMenu(QPlatformMenu *menuIn)
270{
271 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menuIn << ')' << this;
272 if (menuIn == m_subMenu)
273 return;
274 const uint oldId = m_id;
275 if (menuIn != nullptr) { // Set submenu
276 m_subMenu = static_cast<QWindowsMenu *>(menuIn);
277 m_subMenu->setAsItemSubMenu(this);
278 m_id = m_subMenu->id();
279 if (m_parentMenu != nullptr) {
280 ModifyMenu(m_parentMenu->menuHandle(), oldId, MF_BYCOMMAND | MF_POPUP,
281 m_id, qStringToWChar(m_text));
282 }
283 return;
284 }
285 // Clear submenu
286 m_subMenu = nullptr;
287 if (m_parentMenu != nullptr) {
288 m_id = nextId++;
289 ModifyMenu(m_parentMenu->menuHandle(), oldId, MF_BYCOMMAND,
290 m_id, qStringToWChar(m_text));
291 } else {
292 m_id = 0;
293 }
294}
295
296void QWindowsMenuItem::setVisible(bool isVisible)
297{
298 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << isVisible << ')' << this;
299 if (m_visible == isVisible)
300 return;
301 m_visible = isVisible;
302 if (m_parentMenu == nullptr)
303 return;
304 // Windows menu items do not implement settable visibility, we need to work
305 // around by removing the item from the menu. It will be kept in the list.
306 if (isVisible)
307 insertIntoMenuHelper(m_parentMenu, false, m_parentMenu->menuItems().indexOf(this));
308 else
309 RemoveMenu(parentMenuHandle(), m_id, MF_BYCOMMAND);
310}
311
312void QWindowsMenuItem::setIsSeparator(bool isSeparator)
313{
314 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << isSeparator << ')' << this;
315 if (m_separator == isSeparator)
316 return;
317 m_separator = isSeparator;
318 if (m_parentMenu == nullptr)
319 return;
320 MENUITEMINFO menuItemInfo;
321 menuItemInfoInit(menuItemInfo);
322 menuItemInfo.fMask = MIIM_FTYPE;
323 menuItemInfo.fType = isSeparator ? MFT_SEPARATOR : MFT_STRING;
324 SetMenuItemInfo(parentMenuHandle(), m_id, FALSE, &menuItemInfo);
325}
326
327void QWindowsMenuItem::setCheckable(bool checkable)
328{
329 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << checkable << ')' << this;
330 if (m_checkable == checkable)
331 return;
332 m_checkable = checkable;
333 if (m_parentMenu == nullptr)
334 return;
335 UINT state = menuItemState(parentMenuHandle(), m_id, FALSE);
336 if (m_checkable)
337 state |= m_checked ? MF_CHECKED : MF_UNCHECKED;
338 else
339 state &= ~(MF_CHECKED | MF_UNCHECKED);
340 menuItemSetState(parentMenuHandle(), m_id, FALSE, state);
341}
342
343void QWindowsMenuItem::setChecked(bool isChecked)
344{
345 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << isChecked << ')' << this;
346 if (m_checked == isChecked)
347 return;
348 m_checked = isChecked;
349 // Convenience: Allow to set checkable by calling setChecked(true) for
350 // Quick Controls 1
351 if (isChecked)
352 m_checkable = true;
353 if (m_parentMenu == nullptr || !m_checkable)
354 return;
355 menuItemSetChangeState(parentMenuHandle(), m_id, FALSE, m_checked, MF_CHECKED, MF_UNCHECKED);
356}
357
358#if QT_CONFIG(shortcut)
359void QWindowsMenuItem::setShortcut(const QKeySequence &shortcut)
360{
361 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << shortcut << ')' << this;
362 if (m_shortcut == shortcut)
363 return;
364 m_shortcut = shortcut;
365 if (m_parentMenu != nullptr)
366 updateText();
367}
368#endif
369
370void QWindowsMenuItem::setEnabled(bool enabled)
371{
372 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << enabled << ')' << this;
373 if (m_enabled == enabled)
374 return;
375 m_enabled = enabled;
376 if (m_parentMenu != nullptr)
377 menuItemSetChangeState(parentMenuHandle(), m_id, FALSE, m_enabled, MF_ENABLED, MF_GRAYED);
378}
379
381{
382 if (m_iconSize == size)
383 return;
384 m_iconSize = size;
385 if (m_parentMenu != nullptr)
386 updateBitmap();
387}
388
390{
391 return m_parentMenu ? m_parentMenu->menuHandle() : nullptr;
392}
393
395{
396 if (m_separator)
397 return MF_SEPARATOR;
398 UINT result = MF_STRING | (m_enabled ? MF_ENABLED : MF_GRAYED);
399 if (m_subMenu != nullptr)
400 result |= MF_POPUP;
401 if (m_checkable)
402 result |= m_checked ? MF_CHECKED : MF_UNCHECKED;
403 if (QGuiApplication::layoutDirection() == Qt::RightToLeft)
404 result |= MFT_RIGHTORDER;
405 return result;
406}
407
409{
410 QString result = m_text;
411#if QT_CONFIG(shortcut)
412 if (!m_shortcut.isEmpty()) {
413 result += u'\t';
414 result += m_shortcut.toString(QKeySequence::NativeText);
415 }
416#endif
417 return result;
418}
419
420void QWindowsMenuItem::insertIntoMenu(QWindowsMenu *menu, bool append, int index)
421{
422 if (m_id == 0 && m_subMenu == nullptr)
423 m_id = nextId++;
424 insertIntoMenuHelper(menu, append, index);
425 m_parentMenu = menu;
426}
427
428void QWindowsMenuItem::insertIntoMenuHelper(QWindowsMenu *menu, bool append, int index)
429{
430 const QString &text = nativeText();
431
432 UINT_PTR idBefore = 0;
433 if (!append) {
434 // Skip over self (either newly inserted or when called from setVisible()
435 const int nextIndex = findNextVisibleEntry(menu->menuItems(), index + 1);
436 if (nextIndex != -1)
437 idBefore = menu->menuItems().at(nextIndex)->id();
438 }
439
440 if (idBefore)
441 InsertMenu(menu->menuHandle(), idBefore, state(), m_id, qStringToWChar(text));
442 else
443 AppendMenu(menu->menuHandle(), state(), m_id, qStringToWChar(text));
444
445 updateBitmap();
446}
447
449{
450 if (QWindowsMenu *parentMenu = m_parentMenu) {
451 m_parentMenu = nullptr;
452 RemoveMenu(parentMenu->menuHandle(), m_id, MF_BYCOMMAND);
453 parentMenu->notifyRemoved(this);
454 return true;
455 }
456 return false;
457}
458
459// ------------ QWindowsMenu
460
461QWindowsMenu::QWindowsMenu() : QWindowsMenu(nullptr, CreateMenu())
462{
463}
464
465QWindowsMenu::QWindowsMenu(QWindowsMenu *parentMenu, HMENU menu)
466 : m_parentMenu(parentMenu)
467 , m_hMenu(menu)
468{
469 qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this)
470 << "parentMenu=" << parentMenu << "HMENU=" << m_hMenu;
471}
472
474{
475 qCDebug(lcQpaMenus).noquote().nospace() << __FUNCTION__
476 << " \"" <<m_text << "\", " << static_cast<const void *>(this);
477 for (int i = m_menuItems.size() - 1; i>= 0; --i)
478 m_menuItems.at(i)->removeFromMenu();
480 DestroyMenu(m_hMenu);
481}
482
483void QWindowsMenu::insertMenuItem(QPlatformMenuItem *menuItemIn, QPlatformMenuItem *before)
484{
485 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menuItemIn << ", before=" << before << ')' << this;
486 auto *menuItem = static_cast<QWindowsMenuItem *>(menuItemIn);
487 const int index = insertBefore(&m_menuItems, menuItemIn, before);
488 const bool append = index == m_menuItems.size() - 1;
489 menuItem->insertIntoMenu(this, append, index);
490}
491
492void QWindowsMenu::removeMenuItem(QPlatformMenuItem *menuItemIn)
493{
494 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menuItemIn << ')' << this;
495 static_cast<QWindowsMenuItem *>(menuItemIn)->removeFromMenu();
496}
497
498void QWindowsMenu::setText(const QString &text)
499{
500 qCDebug(lcQpaMenus).nospace().noquote()
501 << __FUNCTION__ << "(\"" << text << "\") " << this;
502 if (m_text == text)
503 return;
504 m_text = text;
505 if (!m_visible)
506 return;
507 const HMENU ph = parentHandle();
508 if (ph == nullptr)
509 return;
510 MENUITEMINFO menuItemInfo;
511 menuItemInfoSetText(menuItemInfo, m_text);
512 SetMenuItemInfo(ph, id(), FALSE, &menuItemInfo);
513}
514
515void QWindowsMenu::setIcon(const QIcon &icon)
516{
517 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << icon << ')' << this;
518 m_icon = icon;
519}
520
521void QWindowsMenu::setEnabled(bool enabled)
522{
523 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << enabled << ')' << this;
524 if (m_enabled == enabled)
525 return;
526 m_enabled = enabled;
527 if (!m_visible)
528 return;
529 if (const HMENU ph = parentHandle())
530 menuItemSetChangeState(ph, id(), FALSE, m_enabled, MF_ENABLED, MF_GRAYED);
531}
532
534{
535 const auto it = std::find_if(m_menuItems.cbegin(), m_menuItems.cend(),
536 [subMenu] (const QWindowsMenuItem *i) { return i->subMenu() == subMenu; });
537 return it != m_menuItems.cend() ? *it : nullptr;
538}
539
540void QWindowsMenu::insertIntoMenuBar(QWindowsMenuBar *bar, bool append, int index)
541{
542 UINT_PTR idBefore = 0;
543 if (!append) {
544 // Skip over self (either newly inserted or when called from setVisible()
545 const int nextIndex = findNextVisibleEntry(bar->menus(), index + 1);
546 if (nextIndex != -1)
547 idBefore = bar->menus().at(nextIndex)->id();
548 }
549 m_parentMenuBar = bar;
550 m_parentMenu = nullptr;
551 if (idBefore)
552 InsertMenu(bar->menuBarHandle(), idBefore, MF_POPUP | MF_BYCOMMAND, id(), qStringToWChar(m_text));
553 else
554 AppendMenu(bar->menuBarHandle(), MF_POPUP, id(), qStringToWChar(m_text));
555}
556
558{
559 if (QWindowsMenuBar *bar = m_parentMenuBar) {
560 m_parentMenuBar = nullptr;
561 bar->notifyRemoved(this);
562 return RemoveMenu(bar->menuBarHandle(), id(), MF_BYCOMMAND) == TRUE;
563 }
564 if (QWindowsMenu *menu = m_parentMenu) {
565 m_parentMenu = nullptr;
566 QWindowsMenuItem *item = menu->itemForSubMenu(this);
567 if (item)
568 item->setMenu(nullptr);
569 return item != nullptr;
570 }
571 return false;
572}
573
574void QWindowsMenu::setVisible(bool visible)
575{
576 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << visible << ')' << this;
577 if (m_visible == visible)
578 return;
579 m_visible = visible;
580 const HMENU ph = parentHandle();
581 if (ph == nullptr)
582 return;
583 // Windows menus do not implement settable visibility, we need to work
584 // around by removing the menu from the parent. It will be kept in the list.
585 if (visible) {
586 if (m_parentMenuBar)
587 insertIntoMenuBar(m_parentMenuBar, false, m_parentMenuBar->menus().indexOf(this));
588 } else {
589 RemoveMenu(ph, id(), MF_BYCOMMAND);
590 }
591 if (m_parentMenuBar)
592 m_parentMenuBar->redraw();
593}
594
596{
597 qCDebug(lcQpaMenus) << __FUNCTION__ << position;
598 return position >= 0 && position < m_menuItems.size()
599 ? m_menuItems.at(position) : nullptr;
600}
601
603{
604 return traverseMenuItems(this, [tag] (const QPlatformMenuItem *i) { return i->tag() == tag; });
605}
606
608{
609 QPlatformMenuItem *result = new QWindowsMenuItem;
610 qCDebug(lcQpaMenus) << __FUNCTION__ << this << "returns" << result;
611 return result;
612}
613
615{
616 QPlatformMenu *result = new QWindowsMenu;
617 qCDebug(lcQpaMenus) << __FUNCTION__ << this << "returns" << result;
618 return result;
619}
620
622{
623 m_parentMenu = item->parentMenu();
624}
625
627{
628 return m_parentMenu ? m_parentMenu->menuHandle() : nullptr;
629}
630
632{
633 return m_parentMenuBar ? m_parentMenuBar->menuBarHandle() : nullptr;
634}
635
637{
638 if (m_parentMenuBar)
639 return m_parentMenuBar->menuBarHandle();
640 if (m_parentMenu)
641 return m_parentMenu->menuHandle();
642 return nullptr;
643}
644
645// --------------- QWindowsPopupMenu
646
648
649QWindowsPopupMenu::QWindowsPopupMenu() : QWindowsMenu(nullptr, CreatePopupMenu())
650{
651}
652
653void QWindowsPopupMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect,
654 const QPlatformMenuItem *item)
655{
656 qCDebug(lcQpaMenus) << __FUNCTION__ << '>' << this << parentWindow << targetRect << item;
657 const auto *window = static_cast<const QWindowsBaseWindow *>(parentWindow->handle());
658 const QPoint globalPos = window->mapToGlobal(targetRect.topLeft());
659 trackPopupMenu(window->handle(), globalPos.x(), globalPos.y());
660}
661
662bool QWindowsPopupMenu::trackPopupMenu(HWND windowHandle, int x, int y)
663{
664 lastShownPopupMenu = this;
665 // Emulate Show()/Hide() signals. Could be implemented by catching the
666 // WM_EXITMENULOOP, WM_ENTERMENULOOP messages; but they do not carry
667 // information telling which menu was opened.
668 emit aboutToShow();
669 const bool result =
670 TrackPopupMenu(menuHandle(),
671 QGuiApplication::layoutDirection() == Qt::RightToLeft ? UINT(TPM_RIGHTALIGN) : UINT(0),
672 x, y, 0, windowHandle, nullptr) == TRUE;
673 emit aboutToHide();
674 return result;
675}
676
677bool QWindowsPopupMenu::notifyTriggered(uint id)
678{
679 QPlatformMenuItem *result = lastShownPopupMenu.isNull()
680 ? nullptr
681 : findMenuItemById(lastShownPopupMenu.data(), id);
682 if (result != nullptr) {
683 qCDebug(lcQpaMenus) << __FUNCTION__ << "id=" << id;
684 emit result->activated();
685 }
686 lastShownPopupMenu = nullptr;
687 return result != nullptr;
688}
689
690bool QWindowsPopupMenu::notifyAboutToShow(HMENU hmenu)
691{
692 if (lastShownPopupMenu.isNull())
693 return false;
694 if (lastShownPopupMenu->menuHandle() == hmenu) {
695 emit lastShownPopupMenu->aboutToShow();
696 return true;
697 }
698 if (QWindowsMenu *menu = findMenuByHandle(lastShownPopupMenu.data(), hmenu)) {
699 emit menu->aboutToShow();
700 return true;
701 }
702 return false;
703}
704
705// --------------- QWindowsMenuBar
706
708{
709 qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this);
710}
711
713{
714 qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this);
715 for (int m = m_menus.size() - 1; m >= 0; --m)
716 m_menus.at(m)->removeFromParent();
717 removeFromWindow();
718 DestroyMenu(m_hMenuBar);
719}
720
721void QWindowsMenuBar::insertMenu(QPlatformMenu *menuIn, QPlatformMenu *before)
722{
723 qCDebug(lcQpaMenus) << __FUNCTION__ << menuIn << "before=" << before;
724 auto *menu = static_cast<QWindowsMenu *>(menuIn);
725 const int index = insertBefore(&m_menus, menuIn, before);
726 menu->insertIntoMenuBar(this, index == m_menus.size() - 1, index);
727}
728
729void QWindowsMenuBar::removeMenu(QPlatformMenu *menu)
730{
731 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menu << ')' << this;
732 const int index = indexOf(m_menus, menu);
733 if (index != -1)
734 m_menus[index]->removeFromParent();
735}
736
737// When calling handleReparent() for a QWindow instances that does not have
738// a platform window yet, set the menubar as dynamic property to be installed
739// on platform window creation.
740static const char menuBarPropertyName[] = "_q_windowsNativeMenuBar";
741
742void QWindowsMenuBar::handleReparent(QWindow *newParentWindow)
743{
744 qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << newParentWindow << ')' << this;
745 if (newParentWindow == nullptr) {
746 removeFromWindow();
747 return; // Happens during Quick Controls 1 property setup
748 }
749 if (QPlatformWindow *platWin = newParentWindow->handle())
750 install(static_cast<QWindowsWindow *>(platWin));
751 else // Store for later creation, see menuBarOf()
752 newParentWindow->setProperty(menuBarPropertyName, QVariant::fromValue<QObject *>(this));
753}
754
755QWindowsMenuBar *QWindowsMenuBar::menuBarOf(const QWindow *notYetCreatedWindow)
756{
757 const QVariant menuBarV = notYetCreatedWindow->property(menuBarPropertyName);
758 return menuBarV.canConvert<QObject *>()
759 ? qobject_cast<QWindowsMenuBar *>(menuBarV.value<QObject *>()) : nullptr;
760}
761
763{
764 const HWND hwnd = window->handle();
765 const BOOL result = SetMenu(hwnd, m_hMenuBar);
766 if (result) {
767 window->setMenuBar(this);
768 QWindowsContext::forceNcCalcSize(hwnd);
769 }
770}
771
772void QWindowsMenuBar::removeFromWindow()
773{
774 if (QWindowsWindow *window = platformWindow()) {
775 const HWND hwnd = window->handle();
776 SetMenu(hwnd, nullptr);
777 window->setMenuBar(nullptr);
778 QWindowsContext::forceNcCalcSize(hwnd);
779 }
780}
781
783{
784 return traverseMenus(this, [tag] (const QWindowsMenu *m) { return m->tag() == tag; });
785}
786
788{
789 QPlatformMenu *result = new QWindowsMenu;
790 qCDebug(lcQpaMenus) << __FUNCTION__ << this << "returns" << result;
791 return result;
792}
793
794bool QWindowsMenuBar::notifyTriggered(uint id)
795{
796 QPlatformMenuItem *result = findMenuItemById(this, id);
797 if (result != nullptr) {
798 qCDebug(lcQpaMenus) << __FUNCTION__ << "id=" << id;
799 emit result->activated();
800 }
801 return result != nullptr;
802}
803
805{
806 if (QWindowsMenu *menu = findMenuByHandle(this, hmenu)) {
807 emit menu->aboutToShow();
808 return true;
809 }
810 return false;
811}
812
813QWindowsWindow *QWindowsMenuBar::platformWindow() const
814{
817 return w;
818 }
819 return nullptr;
820}
821
823{
824 if (const QWindowsWindow *window = platformWindow())
825 DrawMenuBar(window->handle());
826}
827
828#ifndef QT_NO_DEBUG_STREAM
829
830template <class M> /* Menu[Item] */
831static void formatTextSequence(QDebug &d, const QList<M *> &v)
832{
833 if (const int size = v.size()) {
834 d << '[' << size << "](";
835 for (int i = 0; i < size; ++i) {
836 if (i)
837 d << ", ";
838 if (!v.at(i)->isVisible())
839 d << "[hidden] ";
840 d << '"' << v.at(i)->text() << '"';
841 }
842 d << ')';
843 }
844}
845
846void QWindowsMenuItem::formatDebug(QDebug &d) const
847{
848 if (m_separator)
849 d << "separator, ";
850 else
851 d << '"' << m_text << "\", ";
852 d << static_cast<const void *>(this);
853 if (m_parentMenu)
854 d << ", parentMenu=" << static_cast<const void *>(m_parentMenu);
855 if (m_subMenu)
856 d << ", subMenu=" << static_cast<const void *>(m_subMenu);
857 d << ", tag=" << Qt::showbase << Qt::hex
858 << tag() << Qt::noshowbase << Qt::dec << ", id=" << m_id;
859#if QT_CONFIG(shortcut)
860 if (!m_shortcut.isEmpty())
861 d << ", shortcut=" << m_shortcut;
862#endif
863 if (m_visible)
864 d << " [visible]";
865 if (m_enabled)
866 d << " [enabled]";
867 if (m_checkable)
868 d << ", checked=" << m_checked;
869}
870
871QDebug operator<<(QDebug d, const QPlatformMenuItem *i)
872{
873 QDebugStateSaver saver(d);
874 d.nospace();
875 d.noquote();
876 d << "QPlatformMenuItem(";
877 if (i)
878 static_cast<const QWindowsMenuItem *>(i)->formatDebug(d);
879 else
880 d << "0x0";
881 d << ')';
882 return d;
883}
884
885void QWindowsMenu::formatDebug(QDebug &d) const
886{
887 d << '"' << m_text << "\", " << static_cast<const void *>(this)
888 << ", handle=" << m_hMenu;
889 if (m_parentMenuBar != nullptr)
890 d << " [on menubar]";
891 if (m_parentMenu != nullptr)
892 d << " [on menu]";
893 if (tag())
894 d << ", tag=" << Qt::showbase << Qt::hex << tag() << Qt::noshowbase << Qt::dec;
895 if (m_visible)
896 d << " [visible]";
897 if (m_enabled)
898 d << " [enabled]";
899 d <<' ';
900 formatTextSequence(d, m_menuItems);
901}
902
903void QWindowsMenuBar::formatDebug(QDebug &d) const
904{
905 d << static_cast<const void *>(this) << ' ';
906 formatTextSequence(d, m_menus);
907}
908
909QDebug operator<<(QDebug d, const QPlatformMenu *m)
910{
911 QDebugStateSaver saver(d);
912 d.nospace();
913 d.noquote();
914 if (m) {
915 d << m->metaObject()->className() << '(';
916 static_cast<const QWindowsMenu *>(m)->formatDebug(d);
917 d << ')';
918 } else {
919 d << "QPlatformMenu(0x0)";
920 }
921 return d;
922}
923
924QDebug operator<<(QDebug d, const QPlatformMenuBar *mb)
925{
926 QDebugStateSaver saver(d);
927 d.nospace();
928 d.noquote();
929 d << "QPlatformMenuBar(";
930 if (mb)
931 static_cast<const QWindowsMenuBar *>(mb)->formatDebug(d);
932 else
933 d << "0x0";
934 d << ')';
935 return d;
936}
937
938#endif // !QT_NO_DEBUG_STREAM
939
940QT_END_NAMESPACE
\inmodule QtCore\reentrant
Definition qpoint.h:30
Base class for QWindowsForeignWindow, QWindowsWindow.
Singleton container for all relevant information.
QWindowsWindow * findPlatformWindow(const QWindowsMenuBar *mb) const
static QWindowsContext * instance()
Windows native menu bar.
void formatDebug(QDebug &d) const
void removeMenu(QPlatformMenu *menu) override
void insertMenu(QPlatformMenu *menu, QPlatformMenu *before) override
void notifyRemoved(QWindowsMenu *menu)
bool notifyAboutToShow(HMENU hmenu)
void install(QWindowsWindow *window)
~QWindowsMenuBar() override
QPlatformMenu * menuForTag(quintptr tag) const override
QPlatformMenu * createMenu() const override
void redraw() const
void setChecked(bool isChecked) override
void setCheckable(bool checkable) override
void insertIntoMenu(QWindowsMenu *menuItem, bool append, int index)
UINT state() const
void setIconSize(int size) override
void formatDebug(QDebug &d) const
void setIsSeparator(bool isSeparator) override
QString nativeText() const
~QWindowsMenuItem() override
void setIcon(const QIcon &icon) override
void setMenu(QPlatformMenu *menu) override
void setText(const QString &text) override
void setEnabled(bool enabled) override
void setVisible(bool isVisible) override
HMENU parentMenuHandle() const
QWindowsMenu * subMenu() const
QWindowsMenu * parentMenu()
HMENU parentMenuBarHandle() const
void setText(const QString &text) override
HMENU parentHandle() const
void setEnabled(bool enabled) override
void notifyRemoved(QWindowsMenuItem *item)
QPlatformMenu * createSubMenu() const override
void removeMenuItem(QPlatformMenuItem *menuItem) override
QPlatformMenuItem * createMenuItem() const override
void formatDebug(QDebug &d) const
QPlatformMenuItem * menuItemAt(int position) const override
void setVisible(bool visible) override
void setIcon(const QIcon &icon) override
HMENU parentMenuHandle() const
QPlatformMenuItem * menuItemForTag(quintptr tag) const override
void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) override
QWindowsMenuItem * itemForSubMenu(const QWindowsMenu *subMenu) const
bool removeFromParent()
void setAsItemSubMenu(QWindowsMenuItem *item)
QWindowsMenu(QWindowsMenu *parentMenu, HMENU menu)
void insertIntoMenuBar(QWindowsMenuBar *bar, bool append, int index)
bool trackPopupMenu(HWND windowHandle, int x, int y)
Raster or OpenGL Window.
void setMenuBar(QWindowsMenuBar *mb)
static void menuItemSetChangeState(HMENU hMenu, UINT uItem, BOOL fByPosition, bool value, UINT trueState, UINT falseState)
static QWindowsMenu * traverseMenus(const QWindowsMenu *menu, Predicate p)
static QWindowsMenuItem * findMenuItemById(const Menu *menu, uint id)
static int insertBefore(QList< Derived * > *v, Base *newItemIn, const Base *before=nullptr)
static QWindowsMenu * findMenuByHandle(const Menu *menu, HMENU hMenu)
static QPointer< QWindowsPopupMenu > lastShownPopupMenu
static void menuItemInfoInit(MENUITEMINFO &menuItemInfo)
static QWindowsMenuItem * traverseMenuItems(const QWindowsMenuBar *menuBar, Predicate p)
static void menuItemSetState(HMENU hMenu, UINT uItem, BOOL fByPosition, UINT flags)
static QWindowsMenu * traverseMenus(const QWindowsMenuBar *menuBar, Predicate p)
static const char menuBarPropertyName[]
static QWindowsMenuItem * traverseMenuItems(const QWindowsMenu *menu, Predicate p)
static int findNextVisibleEntry(const QList< MenuType * > &entries, int pos)
static const wchar_t * qStringToWChar(const QString &s)
static void menuItemInfoSetText(MENUITEMINFO &menuItemInfo, const QString &text)
static UINT menuItemState(HMENU hMenu, UINT uItem, BOOL fByPosition)
static void formatTextSequence(QDebug &d, const QList< M * > &v)
static int indexOf(const QList< Derived * > &v, const Needle *needle)