| 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 list contain the widget? |
| 171 | */ |
| 172 | bool listContainsWidget(const std::forward_list<QPointer<const QWidget>> &list, const QWidget *widget); |
| 173 | |
| 174 | #endif // KHAMBURGERMENUHELPERS_P_H |
| 175 | |