1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2021 Felix Ernst <fe.a.ernst@gmail.com>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "khamburgermenuhelpers_p.h"
9
10#include "khamburgermenu.h"
11
12#include <QEvent>
13#include <QGuiApplication>
14#include <QMenu>
15#include <QMenuBar>
16#include <QToolBar>
17#include <QToolButton>
18#include <QWidget>
19#include <QWindow>
20
21ListenerContainer::ListenerContainer(KHamburgerMenuPrivate *hamburgerMenuPrivate)
22 : QObject{hamburgerMenuPrivate}
23 , m_listeners{std::vector<std::unique_ptr<QObject>>(4)}
24{
25}
26
27ListenerContainer::~ListenerContainer()
28{
29}
30
31bool AddOrRemoveActionListener::eventFilter(QObject * /*watched*/, QEvent *event)
32{
33 if (event->type() == QEvent::ActionAdded || event->type() == QEvent::ActionRemoved) {
34 static_cast<KHamburgerMenuPrivate *>(parent())->notifyMenuResetNeeded();
35 }
36 return false;
37}
38
39void ButtonPressListener::prepareHamburgerButtonForPress(QObject *button)
40{
41 Q_ASSERT(qobject_cast<QToolButton *>(button));
42
43 auto hamburgerMenuPrivate = static_cast<KHamburgerMenuPrivate *>(parent());
44 auto q = static_cast<KHamburgerMenu *>(hamburgerMenuPrivate->q_ptr);
45 Q_EMIT q->aboutToShowMenu();
46 hamburgerMenuPrivate->resetMenu(); // This menu never has a parent which can be
47 // problematic because it can lead to situations in which the QMenu itself is
48 // treated like its own window.
49 // To avoid this we set a sane transientParent() now even if it already has one
50 // because the menu might be opened from another window this time.
51 const auto watchedButton = static_cast<QToolButton *>(button);
52 auto menu = watchedButton->menu();
53 if (!menu) {
54 return;
55 }
56 prepareParentlessMenuForShowing(menu, surrogateParent: watchedButton);
57}
58
59bool ButtonPressListener::eventFilter(QObject *watched, QEvent *event)
60{
61 if (event->type() == QEvent::KeyPress || event->type() == QEvent::MouseButtonPress) {
62 prepareHamburgerButtonForPress(button: watched);
63 }
64 return false;
65}
66
67bool VisibleActionsChangeListener::eventFilter(QObject *watched, QEvent *event)
68{
69 if (event->type() == QEvent::Show || event->type() == QEvent::Hide) {
70 if (!event->spontaneous()) {
71 static_cast<KHamburgerMenuPrivate *>(parent())->notifyMenuResetNeeded();
72 }
73 } else if (event->type() == QEvent::ActionAdded || event->type() == QEvent::ActionRemoved) {
74 Q_ASSERT_X(qobject_cast<QWidget *>(watched), "VisibileActionsChangeListener", "The watched QObject is expected to be a QWidget.");
75 if (static_cast<QWidget *>(watched)->isVisible()) {
76 static_cast<KHamburgerMenuPrivate *>(parent())->notifyMenuResetNeeded();
77 }
78 }
79 return false;
80}
81
82bool VisibilityChangesListener::eventFilter(QObject * /*watched*/, QEvent *event)
83{
84 if (event->type() == QEvent::Show || event->type() == QEvent::Hide) {
85 if (!event->spontaneous()) {
86 static_cast<KHamburgerMenuPrivate *>(parent())->updateVisibility();
87 }
88 }
89 return false;
90}
91
92bool isMenuBarVisible(const QMenuBar *menuBar)
93{
94 return menuBar && (menuBar->isVisible() && !menuBar->isNativeMenuBar());
95}
96
97bool isWidgetActuallyVisible(const QWidget *widget)
98{
99 Q_CHECK_PTR(widget);
100 if (widget->width() < 1 || widget->height() < 1) {
101 return false;
102 }
103
104 bool actuallyVisible = widget->isVisible();
105 const QWidget *ancestorWidget = widget->parentWidget();
106 while (actuallyVisible && ancestorWidget) {
107 actuallyVisible = ancestorWidget->isVisible();
108 ancestorWidget = ancestorWidget->parentWidget();
109 }
110 return actuallyVisible;
111}
112
113void prepareParentlessMenuForShowing(QMenu *menu, const QWidget *surrogateParent)
114{
115 Q_CHECK_PTR(menu);
116 // ensure polished so the style can change the surfaceformat of the window which is
117 // not possible once the window has been created
118 menu->ensurePolished();
119 menu->winId(); // trigger being a native widget already, to ensure windowHandle created
120 // generic code if not known if the available parent widget is a native widget or not
121
122 if (surrogateParent) {
123 auto parentWindowHandle = surrogateParent->windowHandle();
124 if (!parentWindowHandle) {
125 parentWindowHandle = surrogateParent->nativeParentWidget()->windowHandle();
126 }
127 menu->windowHandle()->setTransientParent(parentWindowHandle);
128 return;
129 }
130
131 menu->windowHandle()->setTransientParent(qGuiApp->focusWindow());
132 // Worst case: The menu's transientParent is now still nullptr in which case it might open as
133 // its own window.
134}
135
136void setToolButtonVisible(QWidget *toolButton, bool visible)
137{
138 toolButton->setVisible(visible);
139 // setVisible() unfortunately has no effect for QWidgetActions on toolbars,
140 // so we work around this by using setMaximumSize().
141 if (qobject_cast<QToolBar *>(object: toolButton->parent())) {
142 if (visible) {
143 toolButton->setMaximumSize(QSize(9999999, 9999999));
144 toolButton->setFocusPolicy(Qt::TabFocus);
145 } else {
146 toolButton->setMaximumSize(QSize(0, 0));
147 toolButton->setFocusPolicy(Qt::NoFocus); // We don't want focus on invisible items.
148 }
149 }
150}
151
152bool listContainsWidget(const std::forward_list<QPointer<const QWidget>> &list, const QWidget *widget)
153{
154 for (const auto &item : list) {
155 if (widget == item) {
156 return true;
157 }
158 }
159 return false;
160}
161
162#include "moc_khamburgermenuhelpers_p.cpp"
163

source code of kconfigwidgets/src/khamburgermenuhelpers.cpp