File indexing completed on 2024-12-01 03:39:09

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 KHamburgerMenu_P_H
0009 #define KHamburgerMenu_P_H
0010 
0011 #include "khamburgermenu.h"
0012 
0013 #include <QWidgetAction>
0014 
0015 #include <QPointer>
0016 #include <QScopedPointer>
0017 
0018 #include <forward_list>
0019 #include <unordered_set>
0020 
0021 class ListenerContainer;
0022 
0023 class QMenu;
0024 class QMenuBar;
0025 class QToolButton;
0026 
0027 /**
0028  * The private class of KHamburgerMenu used for the PIMPL idiom.
0029  * \internal
0030  */
0031 class KHamburgerMenuPrivate : public QObject
0032 {
0033     Q_OBJECT
0034     Q_DECLARE_PUBLIC(KHamburgerMenu)
0035 
0036 public:
0037     explicit KHamburgerMenuPrivate(KHamburgerMenu *qq);
0038 
0039     ~KHamburgerMenuPrivate() override;
0040 
0041     /** @see KHamburgerMenu::setMenuBar() */
0042     void setMenuBar(QMenuBar *menuBar);
0043 
0044     /** @see KHamburgerMenu::setMenuBar() */
0045     QMenuBar *menuBar() const;
0046 
0047     /** @see KHamburgerMenu::setMenuBarAdvertised() */
0048     void setMenuBarAdvertised(bool advertise);
0049 
0050     /** @see KHamburgerMenu::setMenuBarAdvertised() */
0051     bool menuBarAdvertised() const;
0052 
0053     /** @see KHamburgerMenu::setShowMenuBarAction() */
0054     void setShowMenuBarAction(QAction *showMenuBarAction);
0055 
0056     /** @see KHamburgerMenu::insertIntoMenuBefore() */
0057     void insertIntoMenuBefore(QMenu *menu, QAction *before);
0058 
0059     /** @see KHamburgerMenu::hideActionsOf() */
0060     void hideActionsOf(QWidget *widget);
0061 
0062     /** @see KHamburgerMenu::showActionsOf() */
0063     void showActionsOf(QWidget *widget);
0064 
0065     /** @see KHamburgerMenu::createWidget() */
0066     QWidget *createWidget(QWidget *parent);
0067 
0068     /**
0069      * This method only returns exclusive actions. The idea is to remove any @p nonExclusives
0070      * from @p from and all of its sub-menus. This means a copy of @p from is created when
0071      * necessary that has a menu() that only contains the exclusive actions from @p from.
0072      * @param from          the action this method extracts the exclusive actions from.
0073      * @param parent        the widget that is to become the parent if a copy of @p from needs
0074      *                      to be created.
0075      * @param nonExclusives the actions which will not be anywhere within the returned action.
0076      * @return either nullptr, @p from unchanged or a copy of @p from without the @p nonExclusives.
0077      *         In the last case, the caller gets ownership of this new copy with parent @p parent.
0078      */
0079     QAction *actionWithExclusivesFrom(QAction *from, QWidget *parent, std::unordered_set<const QAction *> &nonExclusives) const;
0080 
0081     /**
0082      * @return a new menu with all actions from KHamburgerMenu::menu() which aren't
0083      * exempted from being displayed (@see hideActionsOf()).
0084      * Next adds the help menu.
0085      * At last adds a special sub-menu by calling newMenuBarAdvertisementMenu() if this step
0086      * was not explicitly set to be skipped (@see KHamburgerMenu::setMenuBarAdvertised()).
0087      */
0088     std::unique_ptr<QMenu> newMenu();
0089 
0090     /**
0091      * @return a special sub-menu that advertises actions of the menu bar which would otherwise
0092      * not be visible or discoverable for the user
0093      * @see KHamburgerMenu::setMenuBarAdvertised()
0094      */
0095     std::unique_ptr<QMenu> newMenuBarAdvertisementMenu(std::unordered_set<const QAction *> &visibleActions);
0096 
0097     /** @see resetMenu() */
0098     inline void notifyMenuResetNeeded()
0099     {
0100         m_menuResetNeeded = true;
0101     }
0102 
0103     /**
0104      * Does nothing if m_menuResetNeeded is false.
0105      * Otherwise deletes m_actualMenu and creates a newMenu() in its place. This new menu
0106      * is then set to be used whenever the hamburger menu is opened.
0107      * @see newMenu()
0108      */
0109     void resetMenu();
0110 
0111     /**
0112      * Sets the correct visibility for KHamburgerMenu buttons based on the visibility of the
0113      * menu bar (@see setMenuBar()).
0114      * Also sets the correct visibility of the menu item (@see addToMenu()) based on the visibility
0115      * of the menu bar and of the KHamburgerMenu buttons.
0116      */
0117     void updateVisibility();
0118 
0119 protected:
0120     /**
0121      * Makes the KHamburgerMenu buttons change style just like other toolbuttons would
0122      * when their associated action changes.
0123      */
0124     void slotActionChanged();
0125 
0126     /**
0127      * When m_menuBar->isVisible(): Opens the first menu of the menu bar.
0128      * Otherwise it acts the same way as clicking on a visible KHamburgerMenu button.
0129      * If no KHamburgerMenu button is visible, open its menu anyway.
0130      *
0131      * @note The action triggered signal is not emitted when a normal button that contains a menu
0132      *       is pressed. So this slot will effectively only be called by keyboard shortcut.
0133      */
0134     void slotActionTriggered();
0135 
0136     /**
0137      * Updates the style of @p hamburgerMenuButton based on its parent's style and q->priority().
0138      */
0139     void updateButtonStyle(QToolButton *hamburgerMenuButton) const;
0140 
0141 public:
0142     KHamburgerMenu *const q_ptr;
0143 
0144 protected:
0145     /** @see newMenu(). Do not confuse this menu with QAction::menu(). */
0146     std::unique_ptr<QMenu> m_actualMenu;
0147     /** @see KHamburgerMenu::setMenuBarAdvertised() */
0148     bool m_advertiseMenuBar = true;
0149     /** @see newMenuBarAdvertisementMenu() */
0150     std::unique_ptr<QMenu> m_menuBarAdvertisementMenu;
0151     /** @see KHamburgerMenu::hideActionsOf() */
0152     std::forward_list<QPointer<const QWidget>> m_widgetsWithActionsToBeHidden;
0153     /** The menu that was used as a base when newMenu() was last called. With this we
0154      * make sure to reset the m_actualMenu if the q->menu() has been changed or replaced. */
0155     QPointer<QMenu> m_lastUsedMenu;
0156     /** Makes sure there are no redundant event listeners of the same class. */
0157     std::unique_ptr<ListenerContainer> m_listeners;
0158     /** The action that is put into QMenus to represent the KHamburgerMenu.
0159      * @see KHamburgerMenu::addToMenu() */
0160     QPointer<QAction> m_menuAction;
0161     /** @see KHamburgerMenu::setMenuBar() */
0162     QPointer<QMenuBar> m_menuBar;
0163     /** @see resetMenu() */
0164     bool m_menuResetNeeded = false;
0165     /** @see KHamburgerMenu::setShowMenuBarAction */
0166     QPointer<QAction> m_showMenuBarAction;
0167     /** Keeps track of changes to the "Show Menubar" button text. */
0168     QString m_showMenuBarText;
0169     QString m_showMenuBarWithAllActionsText;
0170     /** Identifies if the application set an icon for "Help" menu. */
0171     bool m_helpIconIsSet = false;
0172 };
0173 
0174 #endif // KHamburgerMenu_P_H