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 | #ifndef KHAMBURGERMENUHELPERS_P_H |
9 | #define |
10 | |
11 | #include "khamburgermenu_p.h" |
12 | |
13 | #include <QObject> |
14 | |
15 | #include <memory> |
16 | #include <vector> |
17 | |
18 | class QFont; |
19 | class ; |
20 | class QWidget; |
21 | |
22 | /** |
23 | * @brief Makes sure there are no redundant event listeners. |
24 | * |
25 | * Functionally identical event listeners are needed throughout khamburgermenu.cpp. |
26 | * This class makes sure only one of each is created when needed and then reused. |
27 | * This also simplifies the removal of event listeners. |
28 | * \internal |
29 | */ |
30 | class ListenerContainer : private QObject |
31 | { |
32 | public: |
33 | explicit (KHamburgerMenuPrivate *); |
34 | ~ListenerContainer() override; |
35 | |
36 | /** |
37 | * @return an object of class @p Listener with the same parent as ListenerContainer. |
38 | */ |
39 | template<class Listener> |
40 | Listener *get() |
41 | { |
42 | for (auto &i : m_listeners) { |
43 | if (auto existingListener = qobject_cast<Listener *>(i.get())) { |
44 | return existingListener; |
45 | } |
46 | } |
47 | |
48 | KHamburgerMenuPrivate * = static_cast<KHamburgerMenuPrivate *>(parent()); |
49 | m_listeners.push_back(x: std::unique_ptr<QObject>(new Listener(hamburgerMenuPrivate))); |
50 | return static_cast<Listener *>(m_listeners.back().get()); |
51 | } |
52 | |
53 | protected: |
54 | std::vector<std::unique_ptr<QObject>> m_listeners; |
55 | }; |
56 | |
57 | /** |
58 | * When an action is added or removed, calls KHamburgerMenuPrivate::notifyMenuResetNeeded(). |
59 | * \internal |
60 | */ |
61 | class AddOrRemoveActionListener : public QObject |
62 | { |
63 | Q_OBJECT |
64 | |
65 | protected: |
66 | inline AddOrRemoveActionListener(QObject *parent) |
67 | : QObject{parent} {}; |
68 | |
69 | bool eventFilter(QObject * /*watched*/, QEvent *event) override; |
70 | |
71 | friend class ListenerContainer; |
72 | }; |
73 | |
74 | /** |
75 | * When the button is pressed, emits KHamburgerMenu::aboutToShowMenu(), then calls |
76 | * KHamburgerMenuPrivate::resetMenu() (which will only actually reset the menu if |
77 | * a menu reset is needed). |
78 | * \internal |
79 | */ |
80 | class ButtonPressListener : public QObject |
81 | { |
82 | Q_OBJECT |
83 | |
84 | public: |
85 | /** |
86 | * Makes sure the button can show the expected menu in the expected way when pressed. |
87 | * A button that hasn't been prepared yet will have no menu at all because the menu is only |
88 | * created when it is needed. |
89 | */ |
90 | void prepareHamburgerButtonForPress(QObject *button); |
91 | |
92 | protected: |
93 | inline ButtonPressListener(QObject *parent) |
94 | : QObject{parent} {}; |
95 | |
96 | /** Calls prepareButtonForPress() when an event that presses the button is detected. */ |
97 | bool eventFilter(QObject *watched, QEvent *event) override; |
98 | |
99 | friend class ListenerContainer; |
100 | }; |
101 | |
102 | /** |
103 | * When either |
104 | * - the visibility of the widget changes or |
105 | * - actions are added or removed from the widget while it isVisible() |
106 | * calls KHamburgerMenuPrivate::notifyMenuResetNeeded(). |
107 | * \internal |
108 | */ |
109 | class VisibleActionsChangeListener : public QObject |
110 | { |
111 | Q_OBJECT |
112 | |
113 | protected: |
114 | inline VisibleActionsChangeListener(QObject *parent) |
115 | : QObject{parent} {}; |
116 | |
117 | /** |
118 | * Listen for events that potentially lead to a change in user-visible actions. |
119 | * Examples: Adding an action or hiding a toolbar. |
120 | */ |
121 | bool eventFilter(QObject *watched, QEvent *event) override; |
122 | |
123 | friend class ListenerContainer; |
124 | }; |
125 | |
126 | /** |
127 | * When the visibility of the widget changes calls KHamburgerMenuPrivate::updateVisibility(). |
128 | * \internal |
129 | */ |
130 | class VisibilityChangesListener : public QObject |
131 | { |
132 | Q_OBJECT |
133 | |
134 | protected: |
135 | inline VisibilityChangesListener(QObject *parent) |
136 | : QObject{parent} {}; |
137 | |
138 | bool eventFilter(QObject * /*watched*/, QEvent *event) override; |
139 | |
140 | friend class ListenerContainer; |
141 | }; |
142 | |
143 | /* |
144 | * We only consider a visible m_menuBar as actually visible if it is not a native |
145 | * menu bar because native menu bars can come in many shapes and sizes which don't necessarily |
146 | * have the same usability benefits as a traditional in-window menu bar. |
147 | */ |
148 | bool (const QMenuBar *); |
149 | |
150 | /** |
151 | * Is the widget and all of its ancestors visible? |
152 | */ |
153 | bool isWidgetActuallyVisible(const QWidget *widget); |
154 | |
155 | /* |
156 | * Call this on menus that don't have a parent or don't want to belong to a singular parent() so |
157 | * those menus won't be treated like their own separate windows. |
158 | * @param menu Any menu. Though calling this doesn't make sense if the menu has a parent(). |
159 | * @param surrogateParent The widget that is logically closest to be considered a parent at this |
160 | * point in time. Pass nullptr if this function is supposed to guess. |
161 | */ |
162 | void (QMenu *, const QWidget *surrogateParent); |
163 | |
164 | /** |
165 | * Use this instead of QWidget::isVisible() to work around a peculiarity of QToolBar/QToolButton. |
166 | */ |
167 | void setToolButtonVisible(QWidget *toolButton, bool visible); |
168 | |
169 | /** |
170 | * Does the @p list contain the @p widget? |
171 | */ |
172 | bool listContainsWidget(const std::forward_list<QPointer<const QWidget>> &list, const QWidget *widget); |
173 | |
174 | #endif // KHAMBURGERMENUHELPERS_P_H |
175 | |