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 KHamburgerMenu_H
9#define KHamburgerMenu_H
10
11#include <kconfigwidgets_export.h>
12
13#include <QWidgetAction>
14
15#include <memory>
16
17class KHamburgerMenuPrivate;
18
19class QMenuBar;
20
21/*!
22 * \class KHamburgerMenu
23 * \inmodule KConfigWidgets
24 *
25 * \brief A menu that substitutes a menu bar when necessary.
26 *
27 * Allowing users to toggle the visibility of the menu bar and/or toolbars,
28 * while pretty/"simple by default", can lead to various grave usability issues.
29 * This class makes it easy to prevent all of them.
30 *
31 * Simply add a KHamburgerMenu to your UI (typically to a QToolBar) and make
32 * it aware of a QMenuBar like this:
33 *
34 * \code
35 * auto hamburgerMenu = KStandardAction::hamburgerMenu(nullptr, nullptr, actionCollection());
36 * toolBar()->addAction(hamburgerMenu);
37 * hamburgerMenu->hideActionsOf(toolBar());
38 * // after the QMenuBar has been initialised
39 * hamburgerMenu->setMenuBar(menuBar()); // line not needed if there is no QMenuBar
40 * \endcode
41 *
42 * The added menu button will only be visible when the QMenuBar is hidden.
43 * With this minimal initialisation it will contain the contents of the menu bar.
44 * If a user (also) hides the container the KHamburgerMenu was added to they
45 * might find themselves without a way to get a menu back. To prevent this, it is
46 * recommended to add the hamburgerMenu to prominent context menus like the one
47 * of your central widget preferably at the first position. Simply write:
48 *
49 * \code
50 * hamburgerMenu->addActionToMenu(contextMenu);
51 * \endcode
52 *
53 * The added menu will only be visible if the QMenuBar is hidden and the
54 * hamburgerMenu->createdWidgets() are all invisible to the user.
55 *
56 * \section1 Populating the KHamburgerMenu
57 *
58 * This is easy:
59 *
60 * \code
61 * auto menu = new QMenu(this);
62 * menu->addAction(action);
63 * // Add actions, separators, etc. like usual.
64 * hamburgerMenu->setMenu(menu);
65 * \endcode
66 *
67 * You probably do not want this to happen on startup. Therefore KHamburgerMenu
68 * provides the signal aboutToShowMenu that you can connect to a function containing
69 * the previous statements.
70 *
71 * \code
72 * connect(hamburgerMenu, &KHamburgerMenu::aboutToShowMenu,
73 * this, &MainWindow::updateHamburgerMenu);
74 * // You might want to disconnect the signal after initial creation if the contents never change.
75 * \endcode
76 *
77 * \section1 Deciding what to put on the hamburger menu
78 *
79 * 1. Be sure to add all of the most important actions. Actions which are already
80 * visible on QToolBars, etc. will not show up in the hamburgerMenu. To manage
81 * which containers KHamburgerMenu should watch for redundancy use
82 * hideActionsOf(QWidget *) and showActionsOf(QWidget *).
83 * When a KHamburgerMenu is added to a widget, hideActionsOf(that widget)
84 * will automatically be called.
85 *
86 * 2. Do not worry about adding all actions the application has to offer.
87 * The KHamburgerMenu will automatically have a section advertising excluded
88 * actions which can be found in the QMenuBar. There will also be the
89 * showMenuBarAction if you set it with setShowMenuBarAction().
90 *
91 * 3. Do not worry about the help menu. KHamburgerMenu will automatically contain
92 * a help menu as the second to last item (if you set a QMenuBar which is
93 * expected to have the help menu as the last action).
94 *
95 * \section1 Open menu by shortcut
96 *
97 * For visually impaired users it is important to have a consistent way to open a general-purpose
98 * menu. Triggering the keyboard shortcut bound to KHamburgerMenu will always open a menu.
99 * \list
100 * \li If setMenuBar() was called and that menu bar is visible, the shortcut will open the first menu
101 * of that menu bar.
102 * \li Otherwise, if there is a visible KHamburgerMenu button in the user interface, that menu will
103 * open.
104 * \li Otherwise, KHamburgerMenu's menu will open at the mouse cursor position.
105 * \endlist
106 *
107 * \since 5.81
108 */
109class KCONFIGWIDGETS_EXPORT KHamburgerMenu : public QWidgetAction
110{
111 Q_OBJECT
112 Q_DECLARE_PRIVATE(KHamburgerMenu)
113
114public:
115 /*!
116 *
117 */
118 explicit KHamburgerMenu(QObject *parent);
119
120 ~KHamburgerMenu() override;
121
122 /*!
123 * Associates this KHamburgerMenu with \a menuBar. The KHamburgerMenu will from now
124 * on only be visible when \a menuBar is hidden.
125 * (Menu bars with QMenuBar::isNativeMenuBar() == true are considered hidden.)
126 *
127 * Furthermore the KHamburgerMenu will have the help menu from the \a menuBar added
128 * at the end. There will also be a special sub-menu advertising actions which are
129 * only available in the menu bar unless advertiseMenuBar(false) was called.
130 *
131 * \a menuBar The QMenuBar the KHamburgerMenu should be associated with.
132 * This can be set to nullptr.
133 */
134 void setMenuBar(QMenuBar *menuBar);
135
136 /*! \sa setMenuBar() */
137 QMenuBar *menuBar() const;
138
139 /*!
140 * By default the KHamburgerMenu contains a special sub-menu that advertises actions
141 * of the menu bar which would otherwise not be visible or discoverable for the user.
142 * This method removes or re-adds that sub-menu.
143 *
144 * \a advertise sets whether the special sub-menu that advertises menu bar only
145 * actions should exist.
146 */
147 void setMenuBarAdvertised(bool advertise);
148
149 /*! \a setMenuBarAdvertised() */
150 bool menuBarAdvertised() const;
151
152 /*!
153 * Adds the \a showMenuBarAction as the first item of the sub-menu which advertises actions
154 * from the menu bar.
155 * \a setMenuBarAdvertised()
156 */
157 void setShowMenuBarAction(QAction *showMenuBarAction);
158
159 /*!
160 * Adds this KHamburgerMenu to \a menu.
161 * It will only be visible in the menu if both the menu bar and all of this
162 * QWidgetAction's createdWidgets() are invisible.
163 * If it is visible in the menu, then opening the menu emits the aboutToShowMenu
164 * signal.
165 *
166 * \a menu The menu this KHamburgerMenu is supposed to appear in.
167 */
168 void addToMenu(QMenu *menu);
169
170 /*!
171 * Inserts this KHamburgerMenu to \a menu's list of actions, before the action \a before.
172 * It will only be visible in the menu if both the menu bar and all of this
173 * QWidgetAction's createdWidgets() are invisible.
174 * If it is visible in the menu, then opening the menu emits the aboutToShowMenu
175 * signal.
176 *
177 * \a before The action before which KHamburgerMenu should be inserted.
178 *
179 * \a menu The menu this KHamburgerMenu is supposed to appear in.
180 *
181 * \a QWidget::insertAction(), QMenu::insertMenu()
182 *
183 * \since 5.99
184 */
185 void insertIntoMenuBefore(QMenu *menu, QAction *before);
186
187 /*!
188 * Adds \a widget to a list of widgets that should be monitored for their actions().
189 * If the widget is a QMenu, its actions will be treated as known to the user.
190 * If the widget isn't a QMenu, its actions will only be treated as known to the user
191 * when the widget is actually visible.
192 * \a widget A widget that contains actions which should not show up in the
193 * KHamburgerMenu redundantly.
194 */
195 void hideActionsOf(QWidget *widget);
196
197 /*!
198 * Reverses a hideActionsOf(widget) method call.
199 *
200 * \a hideActionsOf()
201 */
202 void showActionsOf(QWidget *widget);
203
204Q_SIGNALS:
205 /*!
206 * This signal is emitted when a hamburger menu button is about to be pressed down.
207 * It is also emitted when a QMenu that contains a visible KHamburgerMenu emits
208 * QMenu::aboutToShow.
209 */
210 void aboutToShowMenu();
211
212protected:
213 /*!
214 * \a QWidgetAction::createWidget
215 */
216 QWidget *createWidget(QWidget *parent) override;
217
218private:
219 std::unique_ptr<KHamburgerMenuPrivate> const d_ptr;
220};
221
222#endif // KHamburgerMenu_H
223

source code of kconfigwidgets/src/khamburgermenu.h