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

source code of kconfigwidgets/src/khamburgermenu.h