File indexing completed on 2024-04-21 11:26:56

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_H
0009 #define KHamburgerMenu_H
0010 
0011 #include <kconfigwidgets_export.h>
0012 
0013 #include <QWidgetAction>
0014 
0015 #include <memory>
0016 
0017 class KHamburgerMenuPrivate;
0018 
0019 class QMenuBar;
0020 
0021 /**
0022  * @class KHamburgerMenu khamburgermenu.h KHamburgerMenu
0023  *
0024  * @short A menu that substitutes a menu bar when necessary
0025  *
0026  * Allowing users to toggle the visibility of the menu bar and/or toolbars,
0027  * while pretty/"simple by default", can lead to various grave usability issues.
0028  * This class makes it easy to prevent all of them.
0029  *
0030  * Simply add a KHamburgerMenu to your UI (typically to a QToolBar) and make
0031  * it aware of a QMenuBar like this:
0032  *
0033  * \code
0034  * auto hamburgerMenu = KStandardAction::hamburgerMenu(nullptr, nullptr, actionCollection());
0035  * toolBar()->addAction(hamburgerMenu);
0036  * hamburgerMenu->hideActionsOf(toolBar());
0037  * // after the QMenuBar has been initialised
0038  * hamburgerMenu->setMenuBar(menuBar()); // line not needed if there is no QMenuBar
0039  * \endcode
0040  *
0041  * The added menu button will only be visible when the QMenuBar is hidden.
0042  * With this minimal initialisation it will contain the contents of the menu bar.
0043  * If a user (also) hides the container the KHamburgerMenu was added to they
0044  * might find themselves without a way to get a menu back. To prevent this, it is
0045  * recommended to add the hamburgerMenu to prominent context menus like the one
0046  * of your central widget preferably at the first position. Simply write:
0047  *
0048  * \code
0049  * hamburgerMenu->addActionToMenu(contextMenu);
0050  * \endcode
0051  *
0052  * The added menu will only be visible if the QMenuBar is hidden and the
0053  * hamburgerMenu->createdWidgets() are all invisible to the user.
0054  *
0055  * **Populating the KHamburgerMenu**
0056  *
0057  * This is easy:
0058  *
0059  * \code
0060  * auto menu = new QMenu(this);
0061  * menu->addAction(action);
0062  * // Add actions, separators, etc. like usual.
0063  * hamburgerMenu->setMenu(menu);
0064  * \endcode
0065  *
0066  * You probably do not want this to happen on startup. Therefore KHamburgerMenu
0067  * provides the signal aboutToShowMenu that you can connect to a function containing
0068  * the previous statements.
0069  *
0070  * \code
0071  * connect(hamburgerMenu, &KHamburgerMenu::aboutToShowMenu,
0072  *         this, &MainWindow::updateHamburgerMenu);
0073  * // You might want to disconnect the signal after initial creation if the contents never change.
0074  * \endcode
0075  *
0076  * **Deciding what to put on the hamburger menu**
0077  *
0078  * 1. Be sure to add all of the most important actions. Actions which are already
0079  *    visible on QToolBars, etc. will not show up in the hamburgerMenu. To manage
0080  *    which containers KHamburgerMenu should watch for redundancy use
0081  *    hideActionsOf(QWidget *) and showActionsOf(QWidget *).
0082  *    When a KHamburgerMenu is added to a widget, hideActionsOf(that widget)
0083  *    will automatically be called.
0084  * 2. Do not worry about adding all actions the application has to offer.
0085  *    The KHamburgerMenu will automatically have a section advertising excluded
0086  *    actions which can be found in the QMenuBar. There will also be the
0087  *    showMenuBarAction if you set it with setShowMenuBarAction().
0088  * 3. Do not worry about the help menu. KHamburgerMenu will automatically contain
0089  *    a help menu as the second to last item (if you set a QMenuBar which is
0090  *    expected to have the help menu as the last action).
0091  *
0092  * @since 5.81
0093  */
0094 class KCONFIGWIDGETS_EXPORT KHamburgerMenu : public QWidgetAction
0095 {
0096     Q_OBJECT
0097     Q_DECLARE_PRIVATE(KHamburgerMenu)
0098 
0099 public:
0100     explicit KHamburgerMenu(QObject *parent);
0101 
0102     ~KHamburgerMenu() override;
0103 
0104     /**
0105      * Associates this KHamburgerMenu with @p menuBar. The KHamburgerMenu will from now
0106      * on only be visible when @p menuBar is hidden.
0107      * (Menu bars with QMenuBar::isNativeMenuBar() == true are considered hidden.)
0108      *
0109      * Furthermore the KHamburgerMenu will have the help menu from the @p menuBar added
0110      * at the end. There will also be a special sub-menu advertising actions which are
0111      * only available in the menu bar unless advertiseMenuBar(false) was called.
0112      *
0113      * @param menuBar   The QMenuBar the KHamburgerMenu should be associated with.
0114      *                  This can be set to nullptr.
0115      */
0116     void setMenuBar(QMenuBar *menuBar);
0117 
0118     /** @see setMenuBar() */
0119     QMenuBar *menuBar() const;
0120 
0121     /**
0122      * By default the KHamburgerMenu contains a special sub-menu that advertises actions
0123      * of the menu bar which would otherwise not be visible or discoverable for the user.
0124      * This method removes or re-adds that sub-menu.
0125      *
0126      * @param advertise sets whether the special sub-menu that advertises menu bar only
0127      *                  actions should exist.
0128      */
0129     void setMenuBarAdvertised(bool advertise);
0130 
0131     /** @see setMenuBarAdvertised() */
0132     bool menuBarAdvertised() const;
0133 
0134     /**
0135      * Adds the @p showMenuBarAction as the first item of the sub-menu which advertises actions
0136      * from the menu bar.
0137      * @see setMenuBarAdvertised()
0138      */
0139     void setShowMenuBarAction(QAction *showMenuBarAction);
0140 
0141     /**
0142      * Adds this KHamburgerMenu to @p menu.
0143      * It will only be visible in the menu if both the menu bar and all of this
0144      * QWidgetAction's createdWidgets() are invisible.
0145      * If it is visible in the menu, then opening the menu emits the aboutToShowMenu
0146      * signal.
0147      *
0148      * @param menu The menu this KHamburgerMenu is supposed to appear in.
0149      */
0150     void addToMenu(QMenu *menu);
0151 
0152     /**
0153      * Inserts this KHamburgerMenu to @p menu's list of actions, before the action @p before.
0154      * It will only be visible in the menu if both the menu bar and all of this
0155      * QWidgetAction's createdWidgets() are invisible.
0156      * If it is visible in the menu, then opening the menu emits the aboutToShowMenu
0157      * signal.
0158      *
0159      * @param before The action before which KHamburgerMenu should be inserted.
0160      * @param menu The menu this KHamburgerMenu is supposed to appear in.
0161      *
0162      * @see QWidget::insertAction(), QMenu::insertMenu()
0163      *
0164      * @since 5.99
0165      */
0166     void insertIntoMenuBefore(QMenu *menu, QAction *before);
0167 
0168     /**
0169      * Adds @p widget to a list of widgets that should be monitored for their actions().
0170      * If the widget is a QMenu, its actions will be treated as known to the user.
0171      * If the widget isn't a QMenu, its actions will only be treated as known to the user
0172      * when the widget is actually visible.
0173      * @param widget A widget that contains actions which should not show up in the
0174      *               KHamburgerMenu redundantly.
0175      */
0176     void hideActionsOf(QWidget *widget);
0177 
0178     /**
0179      * Reverses a hideActionsOf(widget) method call.
0180      * @see hideActionsOf()
0181      */
0182     void showActionsOf(QWidget *widget);
0183 
0184 Q_SIGNALS:
0185     /**
0186      * This signal is emitted when a hamburger menu button is about to be pressed down.
0187      * It is also emitted when a QMenu that contains a visible KHamburgerMenu emits
0188      * QMenu::aboutToShow.
0189      */
0190     void aboutToShowMenu();
0191 
0192 protected:
0193     /**
0194      * @see QWidgetAction::createWidget
0195      */
0196     virtual QWidget *createWidget(QWidget *parent) override;
0197 
0198 private:
0199     std::unique_ptr<KHamburgerMenuPrivate> const d_ptr;
0200 };
0201 
0202 #endif // KHamburgerMenu_H