8#include "khamburgermenu.h"
9#include "khamburgermenu_p.h"
11#include "khamburgermenuhelpers_p.h"
13#include <KLocalizedString>
22#include <forward_list>
23#include <unordered_set>
25KHamburgerMenu::KHamburgerMenu(
QObject *parent)
27 , d_ptr{new KHamburgerMenuPrivate(this)}
33 , m_listeners{new ListenerContainer(this)}
40KHamburgerMenu::~KHamburgerMenu() =
default;
42KHamburgerMenuPrivate::~KHamburgerMenuPrivate() =
default;
50void KHamburgerMenuPrivate::setMenuBar(
QMenuBar *menuBar)
53 m_menuBar->removeEventFilter(m_listeners->get<VisibilityChangesListener>());
54 m_menuBar->removeEventFilter(m_listeners->get<AddOrRemoveActionListener>());
59 m_menuBar->installEventFilter(m_listeners->get<VisibilityChangesListener>());
60 m_menuBar->installEventFilter(m_listeners->get<AddOrRemoveActionListener>());
70QMenuBar *KHamburgerMenuPrivate::menuBar()
const
78 d->setMenuBarAdvertised(advertise);
81void KHamburgerMenuPrivate::setMenuBarAdvertised(
bool advertise)
83 m_advertiseMenuBar = advertise;
89 return d->menuBarAdvertised();
92bool KHamburgerMenuPrivate::menuBarAdvertised()
const
94 return m_advertiseMenuBar;
100 d->setShowMenuBarAction(showMenuBarAction);
103void KHamburgerMenuPrivate::setShowMenuBarAction(
QAction *showMenuBarAction)
105 m_showMenuBarAction = showMenuBarAction;
111 d->insertIntoMenuBefore(
menu,
nullptr);
117 d->insertIntoMenuBefore(
menu, before);
120void KHamburgerMenuPrivate::insertIntoMenuBefore(
QMenu *menu,
QAction *before)
125 m_menuAction =
new QAction(
this);
126 m_menuAction->setText(
i18nc(
"@action:inmenu General purpose menu",
"&Menu"));
127 m_menuAction->setIcon(q->icon());
128 m_menuAction->setMenu(m_actualMenu.get());
134 if (m_menuAction->isVisible()) {
135 Q_EMIT q->aboutToShowMenu();
145 d->hideActionsOf(widget);
148void KHamburgerMenuPrivate::hideActionsOf(
QWidget *widget)
151 m_widgetsWithActionsToBeHidden.remove(
nullptr);
152 if (listContainsWidget(m_widgetsWithActionsToBeHidden, widget)) {
156 if (
QMenu *menu = qobject_cast<QMenu *>(widget)) {
159 notifyMenuResetNeeded();
164 notifyMenuResetNeeded();
172 d->showActionsOf(widget);
175void KHamburgerMenuPrivate::showActionsOf(
QWidget *widget)
178 m_widgetsWithActionsToBeHidden.remove(widget);
181 if (isWidgetActuallyVisible(widget)) {
182 notifyMenuResetNeeded();
189 return d->createWidget(
parent);
194 if (qobject_cast<QMenu *>(parent)) {
196 "Adding a KHamburgerMenu directly to a QMenu. "
197 "This will look odd. Use addToMenu() instead.");
203 toolButton->setDefaultAction(q);
204 toolButton->setMenu(m_actualMenu.get());
205 toolButton->setAttribute(Qt::WidgetAttribute::WA_CustomWhatsThis);
207 updateButtonStyle(toolButton);
208 if (
const QToolBar *toolbar = qobject_cast<QToolBar *>(parent)) {
212 setToolButtonVisible(toolButton, !isMenuBarVisible(m_menuBar));
215 toolButton->installEventFilter(m_listeners->get<ButtonPressListener>());
217 hideActionsOf(parent);
221QAction *KHamburgerMenuPrivate::actionWithExclusivesFrom(
QAction *from,
QWidget *parent, std::unordered_set<const QAction *> &nonExclusives)
const
224 if (nonExclusives.count(from) > 0) {
230 std::unique_ptr<QAction> menuActionWithExclusives(
new QAction(from->
icon(), from->
text(), parent));
231 std::unique_ptr<QMenu> menuWithExclusives(
new QMenu(parent));
232 const auto fromMenuActions = from->
menu()->
actions();
233 for (
QAction *action : fromMenuActions) {
234 QAction *actionWithExclusives = actionWithExclusivesFrom(action, menuWithExclusives.get(), nonExclusives);
235 if (actionWithExclusives) {
236 menuWithExclusives->addAction(actionWithExclusives);
239 if (menuWithExclusives->isEmpty()) {
247 menuActionWithExclusives->setMenu(menuWithExclusives.release());
248 return menuActionWithExclusives.release();
251std::unique_ptr<QMenu> KHamburgerMenuPrivate::newMenu()
253 std::unique_ptr<QMenu> menu(
new QMenu());
257 if (q->menu() != m_lastUsedMenu) {
258 q->menu()->installEventFilter(m_listeners->get<AddOrRemoveActionListener>());
260 if (m_lastUsedMenu && !listContainsWidget(m_widgetsWithActionsToBeHidden, m_lastUsedMenu)) {
261 m_lastUsedMenu->removeEventFilter(m_listeners->get<AddOrRemoveActionListener>());
263 m_lastUsedMenu = q->menu();
266 if (!q->menu() && !m_menuBar) {
272 const auto menuBarActions = m_menuBar->actions();
273 for (
QAction *menuAction : menuBarActions) {
280 std::unordered_set<const QAction *> visibleActions;
281 m_widgetsWithActionsToBeHidden.remove(
nullptr);
282 for (
const QWidget *widget : m_widgetsWithActionsToBeHidden) {
283 if (qobject_cast<const QMenu *>(widget) || isWidgetActuallyVisible(widget)) {
285 visibleActions.reserve(visibleActions.size() + widget->
actions().
size());
286 const auto widgetActions = widget->
actions();
287 for (
QAction *action : widgetActions) {
288 visibleActions.insert(action);
293 const auto menuActions = q->menu()->actions();
294 for (
QAction *action : menuActions) {
295 if (visibleActions.count(action) == 0) {
297 visibleActions.insert(action);
303 if (m_menuBar->actions().last()->icon().isNull()) {
304 m_helpIconIsSet = false;
305 m_menuBar->actions().last()->setIcon(QIcon::fromTheme(QStringLiteral(
"help-contents")));
307 m_helpIconIsSet = true;
311 if (m_menuBar->actions().last()->icon().name() == QStringLiteral(
"help-contents") && !m_helpIconIsSet) {
312 m_menuBar->actions().last()->setIcon(QIcon());
315 menu->
addAction(m_menuBar->actions().last());
316 visibleActions.insert(m_menuBar->actions().last());
317 if (m_advertiseMenuBar) {
319 m_menuBarAdvertisementMenu = newMenuBarAdvertisementMenu(visibleActions);
320 menu->
addAction(m_menuBarAdvertisementMenu->menuAction());
326std::unique_ptr<QMenu> KHamburgerMenuPrivate::newMenuBarAdvertisementMenu(std::unordered_set<const QAction *> &visibleActions)
328 std::unique_ptr<QMenu> advertiseMenuBarMenu(
new QMenu());
329 m_showMenuBarWithAllActionsText =
i18nc(
"@action:inmenu A menu item that advertises and enables the menubar",
"Show &Menubar with All Actions");
331 if (m_showMenuBarAction) {
332 m_showMenuBarText = m_showMenuBarAction->text();
333 m_showMenuBarAction->setText(m_showMenuBarWithAllActionsText);
337 if (m_showMenuBarAction && m_showMenuBarAction->text() == m_showMenuBarWithAllActionsText) {
338 m_showMenuBarAction->setText(m_showMenuBarText);
341 if (m_showMenuBarAction) {
342 advertiseMenuBarMenu->addAction(m_showMenuBarAction);
343 visibleActions.insert(m_showMenuBarAction);
345 QAction *section = advertiseMenuBarMenu->addSeparator();
347 const auto menuBarActions = m_menuBar->actions();
348 for (
QAction *menuAction : menuBarActions) {
349 QAction *menuActionWithExclusives = actionWithExclusivesFrom(menuAction, advertiseMenuBarMenu.get(), visibleActions);
350 if (menuActionWithExclusives) {
351 advertiseMenuBarMenu->addAction(menuActionWithExclusives);
355 advertiseMenuBarMenu->setTitle(
i18nc(
"@action:inmenu A menu text advertising its contents (more features).",
"More"));
356 section->
setText(
i18nc(
"@action:inmenu A section heading advertising the contents of the menu bar",
"More Actions"));
357 return advertiseMenuBarMenu;
360void KHamburgerMenuPrivate::resetMenu()
363 if (!m_menuResetNeeded && m_actualMenu && m_lastUsedMenu == q->menu()) {
366 m_menuResetNeeded =
false;
368 m_actualMenu = newMenu();
370 const auto createdWidgets = q->createdWidgets();
371 for (
auto widget : createdWidgets) {
372 static_cast<QToolButton *
>(widget)->setMenu(m_actualMenu.get());
375 m_menuAction->setMenu(m_actualMenu.get());
379void KHamburgerMenuPrivate::updateVisibility()
387 const bool menuBarVisible = isMenuBarVisible(m_menuBar);
389 const auto createdWidgets = q->createdWidgets();
390 for (
auto widget : createdWidgets) {
391 setToolButtonVisible(widget, !menuBarVisible);
395 if (menuBarVisible && m_actualMenu) {
396 m_actualMenu.release()->deleteLater();
403 if (menuBarVisible || (m_menuBar && m_menuBar->isNativeMenuBar())
404 || std::any_of(createdWidgets.cbegin(), createdWidgets.cend(), isWidgetActuallyVisible)) {
405 m_menuAction->setVisible(
false);
408 m_menuAction->setVisible(
true);
416void KHamburgerMenuPrivate::slotActionChanged()
419 const auto createdWidgets = q->createdWidgets();
420 for (
auto widget : createdWidgets) {
421 auto toolButton =
static_cast<QToolButton *
>(widget);
422 updateButtonStyle(toolButton);
426void KHamburgerMenuPrivate::slotActionTriggered()
428 if (isMenuBarVisible(m_menuBar)) {
429 const auto menuBarActions = m_menuBar->actions();
430 for (
const auto action : menuBarActions) {
431 if (action->isEnabled() && !action->isSeparator()) {
432 m_menuBar->setActiveAction(m_menuBar->actions().constFirst());
439 const auto createdWidgets = q->createdWidgets();
440 for (
auto widget : createdWidgets) {
441 if (isWidgetActuallyVisible(widget) && widget->
isActiveWindow()) {
442 auto toolButton =
static_cast<QToolButton *
>(widget);
443 m_listeners->get<ButtonPressListener>()->prepareHamburgerButtonForPress(toolButton);
444 toolButton->pressed();
449 Q_EMIT q->aboutToShowMenu();
451 prepareParentlessMenuForShowing(m_actualMenu.get(),
nullptr);
455void KHamburgerMenuPrivate::updateButtonStyle(
QToolButton *hamburgerMenuButton)
const
459 if (
QToolBar *toolbar = qobject_cast<QToolBar *>(hamburgerMenuButton->
parent())) {
460 buttonStyle = toolbar->toolButtonStyle();
472#include "moc_khamburgermenu.cpp"
473#include "moc_khamburgermenu_p.cpp"
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setPriority(Priority priority)
void triggered(bool checked)
QIcon fromTheme(const QString &name)
qsizetype size() const const
void installEventFilter(QObject *filterObj)
QObject * parent() const const
void removeEventFilter(QObject *obj)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)