File indexing completed on 2024-09-29 03:35:29

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2021 Felix Ernst <fe.a.ernst@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #ifndef KHAMBURGERMENUHELPERS_P_H
0009 #define KHAMBURGERMENUHELPERS_P_H
0010 
0011 #include "khamburgermenu_p.h"
0012 
0013 #include <QObject>
0014 
0015 #include <memory>
0016 #include <vector>
0017 
0018 class QFont;
0019 class QMenuBar;
0020 class QWidget;
0021 
0022 /**
0023  * @brief Makes sure there are no redundant event listeners.
0024  *
0025  * Functionally identical event listeners are needed throughout khamburgermenu.cpp.
0026  * This class makes sure only one of each is created when needed and then reused.
0027  * This also simplifies the removal of event listeners.
0028  * \internal
0029  */
0030 class ListenerContainer : private QObject
0031 {
0032 public:
0033     explicit ListenerContainer(KHamburgerMenuPrivate *hamburgerMenuPrivate);
0034     ~ListenerContainer() override;
0035 
0036     /**
0037      * @return an object of class @p Listener with the same parent as ListenerContainer.
0038      */
0039     template<class Listener>
0040     Listener *get()
0041     {
0042         for (auto &i : m_listeners) {
0043             if (auto existingListener = qobject_cast<Listener *>(i.get())) {
0044                 return existingListener;
0045             }
0046         }
0047 
0048         KHamburgerMenuPrivate *hamburgerMenuPrivate = static_cast<KHamburgerMenuPrivate *>(parent());
0049         m_listeners.push_back(std::unique_ptr<QObject>(new Listener(hamburgerMenuPrivate)));
0050         return static_cast<Listener *>(m_listeners.back().get());
0051     }
0052 
0053 protected:
0054     std::vector<std::unique_ptr<QObject>> m_listeners;
0055 };
0056 
0057 /**
0058  * When an action is added or removed, calls KHamburgerMenuPrivate::notifyMenuResetNeeded().
0059  * \internal
0060  */
0061 class AddOrRemoveActionListener : public QObject
0062 {
0063     Q_OBJECT
0064 
0065 protected:
0066     inline AddOrRemoveActionListener(QObject *parent)
0067         : QObject{parent} {};
0068 
0069     bool eventFilter(QObject * /*watched*/, QEvent *event) override;
0070 
0071     friend class ListenerContainer;
0072 };
0073 
0074 /**
0075  * When the button is pressed, emits KHamburgerMenu::aboutToShowMenu(), then calls
0076  * KHamburgerMenuPrivate::resetMenu() (which will only actually reset the menu if
0077  * a menu reset is needed).
0078  * \internal
0079  */
0080 class ButtonPressListener : public QObject
0081 {
0082     Q_OBJECT
0083 
0084 public:
0085     /**
0086      * Makes sure the button can show the expected menu in the expected way when pressed.
0087      * A button that hasn't been prepared yet will have no menu at all because the menu is only
0088      * created when it is needed.
0089      */
0090     void prepareHamburgerButtonForPress(QObject *button);
0091 
0092 protected:
0093     inline ButtonPressListener(QObject *parent)
0094         : QObject{parent} {};
0095 
0096     /** Calls prepareButtonForPress() when an event that presses the button is detected. */
0097     bool eventFilter(QObject *watched, QEvent *event) override;
0098 
0099     friend class ListenerContainer;
0100 };
0101 
0102 /**
0103  * When either
0104  * - the visibility of the widget changes or
0105  * - actions are added or removed from the widget while it isVisible()
0106  * calls KHamburgerMenuPrivate::notifyMenuResetNeeded().
0107  * \internal
0108  */
0109 class VisibleActionsChangeListener : public QObject
0110 {
0111     Q_OBJECT
0112 
0113 protected:
0114     inline VisibleActionsChangeListener(QObject *parent)
0115         : QObject{parent} {};
0116 
0117     /**
0118      * Listen for events that potentially lead to a change in user-visible actions.
0119      * Examples: Adding an action or hiding a toolbar.
0120      */
0121     bool eventFilter(QObject *watched, QEvent *event) override;
0122 
0123     friend class ListenerContainer;
0124 };
0125 
0126 /**
0127  * When the visibility of the widget changes calls KHamburgerMenuPrivate::updateVisibility().
0128  * \internal
0129  */
0130 class VisibilityChangesListener : public QObject
0131 {
0132     Q_OBJECT
0133 
0134 protected:
0135     inline VisibilityChangesListener(QObject *parent)
0136         : QObject{parent} {};
0137 
0138     bool eventFilter(QObject * /*watched*/, QEvent *event) override;
0139 
0140     friend class ListenerContainer;
0141 };
0142 
0143 /*
0144  * We only consider a visible m_menuBar as actually visible if it is not a native
0145  * menu bar because native menu bars can come in many shapes and sizes which don't necessarily
0146  * have the same usability benefits as a traditional in-window menu bar.
0147  */
0148 bool isMenuBarVisible(const QMenuBar *menuBar);
0149 
0150 /**
0151  * Is the widget and all of its ancestors visible?
0152  */
0153 bool isWidgetActuallyVisible(const QWidget *widget);
0154 
0155 /*
0156  * Call this on menus that don't have a parent or don't want to belong to a singular parent() so
0157  * those menus won't be treated like their own separate windows.
0158  * @param menu            Any menu. Though calling this doesn't make sense if the menu has a parent().
0159  * @param surrogateParent The widget that is logically closest to be considered a parent at this
0160  *                        point in time. Pass nullptr if this function is supposed to guess.
0161  */
0162 void prepareParentlessMenuForShowing(QMenu *menu, const QWidget *surrogateParent);
0163 
0164 /**
0165  * Use this instead of QWidget::isVisible() to work around a peculiarity of QToolBar/QToolButton.
0166  */
0167 void setToolButtonVisible(QWidget *toolButton, bool visible);
0168 
0169 /**
0170  * Does the @p list contain the @p widget?
0171  */
0172 bool listContainsWidget(const std::forward_list<QPointer<const QWidget>> &list, const QWidget *widget);
0173 
0174 #endif // KHAMBURGERMENUHELPERS_P_H