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