File: khamburgermenuhelpers.cpp

package info (click to toggle)
kf6-kconfigwidgets 6.13.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 24,020 kB
  • sloc: cpp: 6,309; sh: 18; makefile: 7
file content (162 lines) | stat: -rw-r--r-- 5,552 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
    This file is part of the KDE project
    SPDX-FileCopyrightText: 2021 Felix Ernst <fe.a.ernst@gmail.com>

    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/

#include "khamburgermenuhelpers_p.h"

#include "khamburgermenu.h"

#include <QEvent>
#include <QGuiApplication>
#include <QMenu>
#include <QMenuBar>
#include <QToolBar>
#include <QToolButton>
#include <QWidget>
#include <QWindow>

ListenerContainer::ListenerContainer(KHamburgerMenuPrivate *hamburgerMenuPrivate)
    : QObject{hamburgerMenuPrivate}
    , m_listeners{std::vector<std::unique_ptr<QObject>>(4)}
{
}

ListenerContainer::~ListenerContainer()
{
}

bool AddOrRemoveActionListener::eventFilter(QObject * /*watched*/, QEvent *event)
{
    if (event->type() == QEvent::ActionAdded || event->type() == QEvent::ActionRemoved) {
        static_cast<KHamburgerMenuPrivate *>(parent())->notifyMenuResetNeeded();
    }
    return false;
}

void ButtonPressListener::prepareHamburgerButtonForPress(QObject *button)
{
    Q_ASSERT(qobject_cast<QToolButton *>(button));

    auto hamburgerMenuPrivate = static_cast<KHamburgerMenuPrivate *>(parent());
    auto q = static_cast<KHamburgerMenu *>(hamburgerMenuPrivate->q_ptr);
    Q_EMIT q->aboutToShowMenu();
    hamburgerMenuPrivate->resetMenu(); // This menu never has a parent which can be
    // problematic because it can lead to situations in which the QMenu itself is
    // treated like its own window.
    // To avoid this we set a sane transientParent() now even if it already has one
    // because the menu might be opened from another window this time.
    const auto watchedButton = static_cast<QToolButton *>(button);
    auto menu = watchedButton->menu();
    if (!menu) {
        return;
    }
    prepareParentlessMenuForShowing(menu, watchedButton);
}

bool ButtonPressListener::eventFilter(QObject *watched, QEvent *event)
{
    if (event->type() == QEvent::KeyPress || event->type() == QEvent::MouseButtonPress) {
        prepareHamburgerButtonForPress(watched);
    }
    return false;
}

bool VisibleActionsChangeListener::eventFilter(QObject *watched, QEvent *event)
{
    if (event->type() == QEvent::Show || event->type() == QEvent::Hide) {
        if (!event->spontaneous()) {
            static_cast<KHamburgerMenuPrivate *>(parent())->notifyMenuResetNeeded();
        }
    } else if (event->type() == QEvent::ActionAdded || event->type() == QEvent::ActionRemoved) {
        Q_ASSERT_X(qobject_cast<QWidget *>(watched), "VisibileActionsChangeListener", "The watched QObject is expected to be a QWidget.");
        if (static_cast<QWidget *>(watched)->isVisible()) {
            static_cast<KHamburgerMenuPrivate *>(parent())->notifyMenuResetNeeded();
        }
    }
    return false;
}

bool VisibilityChangesListener::eventFilter(QObject * /*watched*/, QEvent *event)
{
    if (event->type() == QEvent::Show || event->type() == QEvent::Hide) {
        if (!event->spontaneous()) {
            static_cast<KHamburgerMenuPrivate *>(parent())->updateVisibility();
        }
    }
    return false;
}

bool isMenuBarVisible(const QMenuBar *menuBar)
{
    return menuBar && (menuBar->isVisible() && !menuBar->isNativeMenuBar());
}

bool isWidgetActuallyVisible(const QWidget *widget)
{
    Q_CHECK_PTR(widget);
    if (widget->width() < 1 || widget->height() < 1) {
        return false;
    }

    bool actuallyVisible = widget->isVisible();
    const QWidget *ancestorWidget = widget->parentWidget();
    while (actuallyVisible && ancestorWidget) {
        actuallyVisible = ancestorWidget->isVisible();
        ancestorWidget = ancestorWidget->parentWidget();
    }
    return actuallyVisible;
}

void prepareParentlessMenuForShowing(QMenu *menu, const QWidget *surrogateParent)
{
    Q_CHECK_PTR(menu);
    // ensure polished so the style can change the surfaceformat of the window which is
    // not possible once the window has been created
    menu->ensurePolished();
    menu->winId(); // trigger being a native widget already, to ensure windowHandle created
    // generic code if not known if the available parent widget is a native widget or not

    if (surrogateParent) {
        auto parentWindowHandle = surrogateParent->windowHandle();
        if (!parentWindowHandle) {
            parentWindowHandle = surrogateParent->nativeParentWidget()->windowHandle();
        }
        menu->windowHandle()->setTransientParent(parentWindowHandle);
        return;
    }

    menu->windowHandle()->setTransientParent(qGuiApp->focusWindow());
    // Worst case: The menu's transientParent is now still nullptr in which case it might open as
    // its own window.
}

void setToolButtonVisible(QWidget *toolButton, bool visible)
{
    toolButton->setVisible(visible);
    // setVisible() unfortunately has no effect for QWidgetActions on toolbars,
    // so we work around this by using setMaximumSize().
    if (qobject_cast<QToolBar *>(toolButton->parent())) {
        if (visible) {
            toolButton->setMaximumSize(QSize(9999999, 9999999));
            toolButton->setFocusPolicy(Qt::TabFocus);
        } else {
            toolButton->setMaximumSize(QSize(0, 0));
            toolButton->setFocusPolicy(Qt::NoFocus); // We don't want focus on invisible items.
        }
    }
}

bool listContainsWidget(const std::forward_list<QPointer<const QWidget>> &list, const QWidget *widget)
{
    for (const auto &item : list) {
        if (widget == item) {
            return true;
        }
    }
    return false;
}

#include "moc_khamburgermenuhelpers_p.cpp"