aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKai Uwe Broulik <[email protected]>2017-01-11 10:17:12 +0100
committerKai Uwe Broulik <[email protected]>2017-01-11 10:17:52 +0100
commita4bdedc38abbf84b3f82baa593500be9a96b7fd5 (patch)
tree35587d43f5cbed21f01b1503770dd3146c05dc58
parent6e0d099054b1ffba660e2c9d28109d47b4700959 (diff)
[KDecoration] Restore application menu button
Differential Revision: https://phabricator.kde.org/D3088
-rw-r--r--autotests/decorationbuttontest.cpp56
-rw-r--r--autotests/mockclient.cpp24
-rw-r--r--autotests/mockclient.h8
-rw-r--r--src/decoratedclient.cpp23
-rw-r--r--src/decoratedclient.h32
-rw-r--r--src/decoration.cpp18
-rw-r--r--src/decoration.h3
-rw-r--r--src/decorationbutton.cpp10
-rw-r--r--src/private/decoratedclientprivate.cpp8
-rw-r--r--src/private/decoratedclientprivate.h15
10 files changed, 195 insertions, 2 deletions
diff --git a/autotests/decorationbuttontest.cpp b/autotests/decorationbuttontest.cpp
index bafccbe..c15a2cf 100644
--- a/autotests/decorationbuttontest.cpp
+++ b/autotests/decorationbuttontest.cpp
@@ -59,6 +59,7 @@ private Q_SLOTS:
void testMenu();
void testMenuDoubleClick();
void testMenuPressAndHold();
+ void testApplicationMenu();
};
void DecorationButtonTest::testButton()
@@ -1286,5 +1287,60 @@ void DecorationButtonTest::testMenuPressAndHold()
QCOMPARE(closeRequestedSpy.count(), 0);
}
+void DecorationButtonTest::testApplicationMenu()
+{
+ MockBridge bridge;
+ auto decoSettings = QSharedPointer<KDecoration2::DecorationSettings>::create(&bridge);
+ MockDecoration mockDecoration(&bridge);
+ mockDecoration.setSettings(decoSettings);
+ MockClient *client = bridge.lastCreatedClient();
+ MockButton button(KDecoration2::DecorationButtonType::ApplicationMenu, &mockDecoration);
+ button.setGeometry(QRect(0, 0, 10, 10));
+
+ QCOMPARE(button.isEnabled(), true);
+ QCOMPARE(button.isCheckable(), true);
+ QCOMPARE(button.isChecked(), false);
+ QCOMPARE(button.isVisible(), true);
+ QCOMPARE(button.acceptedButtons(), Qt::LeftButton);
+
+ // clicking the button should trigger a request for application menu
+ QSignalSpy clickedSpy(&button, SIGNAL(clicked(Qt::MouseButton)));
+ QVERIFY(clickedSpy.isValid());
+ QSignalSpy pressedSpy(&button, SIGNAL(pressed()));
+ QVERIFY(pressedSpy.isValid());
+ QSignalSpy releasedSpy(&button, SIGNAL(released()));
+ QVERIFY(releasedSpy.isValid());
+ QSignalSpy pressedChangedSpy(&button, SIGNAL(pressedChanged(bool)));
+ QVERIFY(pressedChangedSpy.isValid());
+ QSignalSpy applicationMenuRequestedSpy(client, SIGNAL(applicationMenuRequested()));
+ QVERIFY(applicationMenuRequestedSpy.isValid());
+
+ QMouseEvent pressEvent(QEvent::MouseButtonPress, QPointF(5, 5), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+ pressEvent.setAccepted(false);
+ button.event(&pressEvent);
+ QCOMPARE(pressEvent.isAccepted(), true);
+ QCOMPARE(button.isPressed(), true);
+ QCOMPARE(clickedSpy.count(), 0);
+ QCOMPARE(pressedSpy.count(), 1);
+ QCOMPARE(releasedSpy.count(), 0);
+ QCOMPARE(applicationMenuRequestedSpy.count(), 0);
+ QCOMPARE(pressedChangedSpy.count(), 1);
+ QCOMPARE(pressedChangedSpy.first().first().toBool(), true);
+
+ QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPointF(5, 5), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
+ releaseEvent.setAccepted(false);
+ button.event(&releaseEvent);
+ QCOMPARE(releaseEvent.isAccepted(), true);
+ QCOMPARE(button.isPressed(), false);
+ QCOMPARE(clickedSpy.count(), 1);
+ QCOMPARE(clickedSpy.first().first().value<Qt::MouseButton>(), Qt::LeftButton);
+ QCOMPARE(pressedSpy.count(), 1);
+ QCOMPARE(releasedSpy.count(), 1);
+ QVERIFY(applicationMenuRequestedSpy.wait());
+ QCOMPARE(applicationMenuRequestedSpy.count(), 1);
+ QCOMPARE(pressedChangedSpy.count(), 2);
+ QCOMPARE(pressedChangedSpy.last().first().toBool(), false);
+}
+
QTEST_MAIN(DecorationButtonTest)
#include "decorationbuttontest.moc"
diff --git a/autotests/mockclient.cpp b/autotests/mockclient.cpp
index 6caf0f2..f855b3d 100644
--- a/autotests/mockclient.cpp
+++ b/autotests/mockclient.cpp
@@ -24,7 +24,7 @@
MockClient::MockClient(KDecoration2::DecoratedClient *client, KDecoration2::Decoration *decoration)
: QObject()
- , DecoratedClientPrivate(client, decoration)
+ , ApplicationMenuEnabledDecoratedClientPrivate(client, decoration)
{
}
@@ -138,6 +138,16 @@ QPalette MockClient::palette() const
return QPalette();
}
+bool MockClient::hasApplicationMenu() const
+{
+ return true;
+}
+
+bool MockClient::isApplicationMenuActive() const
+{
+ return false;
+}
+
bool MockClient::providesContextHelp() const
{
return m_contextHelp;
@@ -191,6 +201,13 @@ void MockClient::requestShowWindowMenu()
emit menuRequested();
}
+void MockClient::requestShowApplicationMenu(const QRect &rect, int actionId)
+{
+ Q_UNUSED(rect);
+ Q_UNUSED(actionId);
+ emit applicationMenuRequested(); // FIXME TODO pass geometry
+}
+
void MockClient::requestToggleKeepAbove()
{
m_keepAbove = !m_keepAbove;
@@ -266,3 +283,8 @@ void MockClient::setHeight(int h)
m_height = h;
emit client()->heightChanged(h);
}
+
+void MockClient::showApplicationMenu(int actionId)
+{
+ Q_UNUSED(actionId)
+}
diff --git a/autotests/mockclient.h b/autotests/mockclient.h
index c55d357..8755589 100644
--- a/autotests/mockclient.h
+++ b/autotests/mockclient.h
@@ -24,7 +24,7 @@
#include <QObject>
-class MockClient : public QObject, public KDecoration2::DecoratedClientPrivate
+class MockClient : public QObject, public KDecoration2::ApplicationMenuEnabledDecoratedClientPrivate
{
Q_OBJECT
public:
@@ -52,12 +52,15 @@ public:
bool isShadeable() const override;
bool isShaded() const override;
QPalette palette() const override;
+ bool hasApplicationMenu() const override;
+ bool isApplicationMenuActive() const override;
bool providesContextHelp() const override;
void requestClose() override;
void requestContextHelp() override;
void requestToggleMaximization(Qt::MouseButtons buttons) override;
void requestMinimize() override;
void requestShowWindowMenu() override;
+ void requestShowApplicationMenu(const QRect &rect, int actionId) override;
void requestToggleKeepAbove() override;
void requestToggleKeepBelow() override;
void requestToggleOnAllDesktops() override;
@@ -65,6 +68,8 @@ public:
int width() const override;
WId windowId() const override;
+ void showApplicationMenu(int actionId) override;
+
void setCloseable(bool set);
void setMinimizable(bool set);
void setProvidesContextHelp(bool set);
@@ -79,6 +84,7 @@ Q_SIGNALS:
void minimizeRequested();
void quickHelpRequested();
void menuRequested();
+ void applicationMenuRequested();
private:
bool m_closeable = false;
diff --git a/src/decoratedclient.cpp b/src/decoratedclient.cpp
index b107aa0..6e11ee7 100644
--- a/src/decoratedclient.cpp
+++ b/src/decoratedclient.cpp
@@ -69,6 +69,22 @@ DELEGATE(Qt::Edges, adjacentScreenEdges)
#undef DELEGATE
+bool DecoratedClient::hasApplicationMenu() const
+{
+ if (const auto *appMenuEnabledPrivate = dynamic_cast<ApplicationMenuEnabledDecoratedClientPrivate *>(d.get())) {
+ return appMenuEnabledPrivate->hasApplicationMenu();
+ }
+ return false;
+}
+
+bool DecoratedClient::isApplicationMenuActive() const
+{
+ if (const auto *appMenuEnabledPrivate = dynamic_cast<ApplicationMenuEnabledDecoratedClientPrivate *>(d.get())) {
+ return appMenuEnabledPrivate->isApplicationMenuActive();
+ }
+ return false;
+}
+
QPointer< Decoration > DecoratedClient::decoration() const
{
return QPointer<Decoration>(d->decoration());
@@ -84,4 +100,11 @@ QColor DecoratedClient::color(ColorGroup group, ColorRole role) const
return d->color(group, role);
}
+void DecoratedClient::showApplicationMenu(int actionId)
+{
+ if (auto *appMenuEnabledPrivate = dynamic_cast<ApplicationMenuEnabledDecoratedClientPrivate *>(d.get())) {
+ appMenuEnabledPrivate->showApplicationMenu(actionId);
+ }
+}
+
} // namespace
diff --git a/src/decoratedclient.h b/src/decoratedclient.h
index 501ebd6..cfd7836 100644
--- a/src/decoratedclient.h
+++ b/src/decoratedclient.h
@@ -167,6 +167,17 @@ class KDECORATIONS2_EXPORT DecoratedClient : public QObject
* will include all Edges. The Decoration can use this information to hide borders.
**/
Q_PROPERTY(Qt::Edges adjacentScreenEdges READ adjacentScreenEdges NOTIFY adjacentScreenEdgesChanged)
+ /**
+ * Whether the DecoratedClient has an application menu
+ * @since 5.9
+ */
+ Q_PROPERTY(bool hasApplicationMenu READ hasApplicationMenu NOTIFY hasApplicationMenuChanged)
+ /**
+ * Whether the application menu for this DecoratedClient is currently shown to the user
+ * The Decoration can use this information to highlight the respective button.
+ * @since 5.9
+ */
+ Q_PROPERTY(bool applicationMenuActive READ isApplicationMenuActive NOTIFY applicationMenuActiveChanged)
// TODO: properties for windowId and decorationId?
@@ -221,6 +232,24 @@ public:
**/
QColor color(ColorGroup group, ColorRole role) const;
+ /**
+ * Whether the DecoratedClient has an application menu
+ * @since 5.9
+ */
+ bool hasApplicationMenu() const;
+ /**
+ * Whether the application menu for this DecoratedClient is currently shown to the user
+ * The Decoration can use this information to highlight the respective button.
+ * @since 5.9
+ */
+ bool isApplicationMenuActive() const;
+
+ /**
+ * Request the application menu to be shown to the user
+ * @param actionId The DBus menu ID of the action that should be highlighted, 0 for none.
+ */
+ void showApplicationMenu(int actionId);
+
Q_SIGNALS:
void activeChanged(bool);
void captionChanged(QString);
@@ -247,6 +276,9 @@ Q_SIGNALS:
void paletteChanged(const QPalette &palette);
void adjacentScreenEdgesChanged(Qt::Edges edges);
+ void hasApplicationMenuChanged(bool);
+ void applicationMenuActiveChanged(bool);
+
private:
friend class Decoration;
DecoratedClient(Decoration *parent, DecorationBridge *bridge);
diff --git a/src/decoration.cpp b/src/decoration.cpp
index 7cb73e1..3e24f36 100644
--- a/src/decoration.cpp
+++ b/src/decoration.cpp
@@ -185,6 +185,24 @@ void Decoration::requestToggleMaximization(Qt::MouseButtons buttons)
d->client->d->requestToggleMaximization(buttons);
}
+void Decoration::showApplicationMenu(int actionId)
+{
+ auto it = std::find_if(d->buttons.constBegin(), d->buttons.constEnd(), [](DecorationButton *button) {
+ return button->type() == DecorationButtonType::ApplicationMenu;
+ });
+
+ if (it != d->buttons.constEnd()) {
+ requestShowApplicationMenu((*it)->geometry().toRect(), actionId);
+ }
+}
+
+void Decoration::requestShowApplicationMenu(const QRect &rect, int actionId)
+{
+ if (auto *appMenuEnabledPrivate = dynamic_cast<ApplicationMenuEnabledDecoratedClientPrivate *>(d->client->d.get())) {
+ appMenuEnabledPrivate->requestShowApplicationMenu(rect, actionId);
+ }
+}
+
#define DELEGATE(name, variableName, type, emitValue) \
void Decoration::name(type a) \
{ \
diff --git a/src/decoration.h b/src/decoration.h
index 5420238..95b0b7c 100644
--- a/src/decoration.h
+++ b/src/decoration.h
@@ -177,6 +177,9 @@ public Q_SLOTS:
void requestToggleKeepBelow();
void requestShowWindowMenu();
+ void showApplicationMenu(int actionId);
+ void requestShowApplicationMenu(const QRect &rect, int actionId);
+
void update(const QRect &rect);
void update();
diff --git a/src/decorationbutton.cpp b/src/decorationbutton.cpp
index b8aa7ff..5d3c2f9 100644
--- a/src/decorationbutton.cpp
+++ b/src/decorationbutton.cpp
@@ -77,6 +77,16 @@ void DecorationButton::Private::init()
setPressAndHold(settings->isCloseOnDoubleClickOnMenu());
setAcceptedButtons(Qt::LeftButton | Qt::RightButton);
break;
+ case DecorationButtonType::ApplicationMenu:
+ setVisible(c->hasApplicationMenu());
+ setCheckable(true); // will be "checked" whilst the menu is opened
+ // FIXME TODO connect directly and figure out the button geometry/offset stuff
+ QObject::connect(q, &DecorationButton::clicked, decoration.data(), [this] {
+ decoration->requestShowApplicationMenu(q->geometry().toRect(), 0 /* actionId */);
+ }, Qt::QueuedConnection); //&Decoration::requestShowApplicationMenu, Qt::QueuedConnection);
+ QObject::connect(c, &DecoratedClient::hasApplicationMenuChanged, q, &DecorationButton::setVisible);
+ QObject::connect(c, &DecoratedClient::applicationMenuActiveChanged, q, &DecorationButton::setChecked);
+ break;
case DecorationButtonType::OnAllDesktops:
setVisible(settings->isOnAllDesktopsAvailable());
setCheckable(true);
diff --git a/src/private/decoratedclientprivate.cpp b/src/private/decoratedclientprivate.cpp
index bbec12e..ec1459b 100644
--- a/src/private/decoratedclientprivate.cpp
+++ b/src/private/decoratedclientprivate.cpp
@@ -68,4 +68,12 @@ QColor DecoratedClientPrivate::color(ColorGroup group, ColorRole role) const
return QColor();
}
+ApplicationMenuEnabledDecoratedClientPrivate::ApplicationMenuEnabledDecoratedClientPrivate(DecoratedClient *client, Decoration *decoration)
+ : DecoratedClientPrivate(client, decoration)
+{
+
+}
+
+ApplicationMenuEnabledDecoratedClientPrivate::~ApplicationMenuEnabledDecoratedClientPrivate() = default;
+
}
diff --git a/src/private/decoratedclientprivate.h b/src/private/decoratedclientprivate.h
index 77c9689..51bcead 100644
--- a/src/private/decoratedclientprivate.h
+++ b/src/private/decoratedclientprivate.h
@@ -100,6 +100,21 @@ private:
const QScopedPointer<Private> d;
};
+class KDECORATIONS_PRIVATE_EXPORT ApplicationMenuEnabledDecoratedClientPrivate : public DecoratedClientPrivate
+{
+public:
+ ~ApplicationMenuEnabledDecoratedClientPrivate() override;
+
+ virtual bool hasApplicationMenu() const = 0;
+ virtual bool isApplicationMenuActive() const = 0;
+
+ virtual void showApplicationMenu(int actionId) = 0;
+ virtual void requestShowApplicationMenu(const QRect &rect, int actionId) = 0;
+
+protected:
+ explicit ApplicationMenuEnabledDecoratedClientPrivate(DecoratedClient *client, Decoration *decoration);
+};
+
} // namespace
#endif