From e7cfb5cf37de6956eaf9343c4a69e951f8d268f5 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Wed, 1 May 2024 02:55:23 -0700 Subject: [PATCH] service/tray: move menu access to SystemTrayMenuWatcher --- docs | 2 +- src/dbus/dbusmenu/dbusmenu.hpp | 2 +- src/services/status_notifier/qml.cpp | 71 +++++++++++++++++++--------- src/services/status_notifier/qml.hpp | 59 ++++++++++++++++------- 4 files changed, 94 insertions(+), 40 deletions(-) diff --git a/docs b/docs index 0488683f..149b784a 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 0488683f290c9bf38147de06c2692a1d9b98ae07 +Subproject commit 149b784a5a4c40ada67cb9f6af5a5350678ab6d4 diff --git a/src/dbus/dbusmenu/dbusmenu.hpp b/src/dbus/dbusmenu/dbusmenu.hpp index 1fa5e58f..b07919ad 100644 --- a/src/dbus/dbusmenu/dbusmenu.hpp +++ b/src/dbus/dbusmenu/dbusmenu.hpp @@ -104,7 +104,7 @@ class DBusMenuItem: public QObject { /// > instead of checking if `children` is empty. Q_PROPERTY(QQmlListProperty children READ children NOTIFY childrenChanged); // clang-format on - QML_NAMED_ELEMENT(DBusMenu); + QML_ELEMENT; QML_UNCREATABLE("DBusMenus can only be acquired from a DBusMenuHandle"); public: diff --git a/src/services/status_notifier/qml.cpp b/src/services/status_notifier/qml.cpp index f3a6c7f9..cea5646e 100644 --- a/src/services/status_notifier/qml.cpp +++ b/src/services/status_notifier/qml.cpp @@ -29,11 +29,8 @@ SystemTrayItem::SystemTrayItem(qs::service::sni::StatusNotifierItem* item, QObje QObject::connect(this->item, &StatusNotifierItem::iconChanged, this, &SystemTrayItem::iconChanged); QObject::connect(&this->item->tooltip, &AbstractDBusProperty::changed, this, &SystemTrayItem::tooltipTitleChanged); QObject::connect(&this->item->tooltip, &AbstractDBusProperty::changed, this, &SystemTrayItem::tooltipDescriptionChanged); - QObject::connect(&this->item->menuPath, &AbstractDBusProperty::changed, this, &SystemTrayItem::onMenuPathChanged); QObject::connect(&this->item->isMenu, &AbstractDBusProperty::changed, this, &SystemTrayItem::onlyMenuChanged); // clang-format on - - if (!this->item->menuPath.get().path().isEmpty()) this->onMenuPathChanged(); } QString SystemTrayItem::id() const { @@ -89,30 +86,14 @@ QString SystemTrayItem::tooltipDescription() const { return this->item->tooltip.get().description; } -DBusMenuItem* SystemTrayItem::menu() const { - if (this->mMenu == nullptr) return nullptr; - return &this->mMenu->rootItem; -} - bool SystemTrayItem::onlyMenu() const { if (this->item == nullptr) return false; return this->item->isMenu.get(); } -void SystemTrayItem::onMenuPathChanged() { - if (this->mMenu != nullptr) { - this->mMenu->deleteLater(); - } - - this->mMenu = this->item->createMenu(); - emit this->menuChanged(); -} - -void SystemTrayItem::activate() { this->item->activate(); } - -void SystemTrayItem::secondaryActivate() { this->item->secondaryActivate(); } - -void SystemTrayItem::scroll(qint32 delta, bool horizontal) { +void SystemTrayItem::activate() const { this->item->activate(); } +void SystemTrayItem::secondaryActivate() const { this->item->secondaryActivate(); } +void SystemTrayItem::scroll(qint32 delta, bool horizontal) const { this->item->scroll(delta, horizontal); } @@ -165,3 +146,49 @@ qsizetype SystemTray::itemsCount(QQmlListProperty* property) { SystemTrayItem* SystemTray::itemAt(QQmlListProperty* property, qsizetype index) { return reinterpret_cast(property->object)->mItems.at(index); // NOLINT } + +SystemTrayItem* SystemTrayMenuWatcher::trayItem() const { return this->item; } + +void SystemTrayMenuWatcher::setTrayItem(SystemTrayItem* item) { + if (item == this->item) return; + + if (this->item != nullptr) { + QObject::disconnect(this->item, nullptr, this, nullptr); + } + + this->item = item; + + if (item != nullptr) { + QObject::connect(item, &QObject::destroyed, this, &SystemTrayMenuWatcher::onItemDestroyed); + + QObject::connect( + &item->item->menuPath, + &AbstractDBusProperty::changed, + this, + &SystemTrayMenuWatcher::onMenuPathChanged + ); + } + + this->onMenuPathChanged(); + emit this->trayItemChanged(); +} + +DBusMenuItem* SystemTrayMenuWatcher::menu() const { + if (this->mMenu == nullptr) return nullptr; + return &this->mMenu->rootItem; +} + +void SystemTrayMenuWatcher::onItemDestroyed() { + this->item = nullptr; + this->onMenuPathChanged(); + emit this->trayItemChanged(); +} + +void SystemTrayMenuWatcher::onMenuPathChanged() { + if (this->mMenu != nullptr) { + this->mMenu->deleteLater(); + } + + this->mMenu = this->item == nullptr ? nullptr : this->item->item->createMenu(); + emit this->menuChanged(); +} diff --git a/src/services/status_notifier/qml.hpp b/src/services/status_notifier/qml.hpp index 39463871..01f6bb05 100644 --- a/src/services/status_notifier/qml.hpp +++ b/src/services/status_notifier/qml.hpp @@ -45,6 +45,8 @@ Q_ENUM_NS(Enum); /// A system tray item, roughly conforming to the [kde/freedesktop spec] /// (there is no real spec, we just implemented whatever seemed to actually be used). /// +/// The associated context menu can be retrieved using a [SystemTrayMenuWatcher](../systemtraymenuwatcher). +/// /// [kde/freedesktop spec]: https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierItem/ class SystemTrayItem: public QObject { using DBusMenuItem = qs::dbus::dbusmenu::DBusMenuItem; @@ -60,8 +62,6 @@ class SystemTrayItem: public QObject { Q_PROPERTY(QString icon READ icon NOTIFY iconChanged); Q_PROPERTY(QString tooltipTitle READ tooltipTitle NOTIFY tooltipTitleChanged); Q_PROPERTY(QString tooltipDescription READ tooltipDescription NOTIFY tooltipDescriptionChanged); - // The context menu provided by the application, generally displayed via a right click. - Q_PROPERTY(DBusMenuItem* menu READ menu NOTIFY menuChanged); /// If this tray item only offers a menu and activation will do nothing. Q_PROPERTY(bool onlyMenu READ onlyMenu NOTIFY onlyMenuChanged); QML_ELEMENT; @@ -71,13 +71,13 @@ public: explicit SystemTrayItem(qs::service::sni::StatusNotifierItem* item, QObject* parent = nullptr); /// Primary activation action, generally triggered via a left click. - Q_INVOKABLE void activate(); + Q_INVOKABLE void activate() const; /// Secondary activation action, generally triggered via a middle click. - Q_INVOKABLE void secondaryActivate(); + Q_INVOKABLE void secondaryActivate() const; /// Scroll action, such as changing volume on a mixer. - Q_INVOKABLE void scroll(qint32 delta, bool horizontal); + Q_INVOKABLE void scroll(qint32 delta, bool horizontal) const; [[nodiscard]] QString id() const; [[nodiscard]] QString title() const; @@ -86,9 +86,10 @@ public: [[nodiscard]] QString icon() const; [[nodiscard]] QString tooltipTitle() const; [[nodiscard]] QString tooltipDescription() const; - [[nodiscard]] DBusMenuItem* menu() const; [[nodiscard]] bool onlyMenu() const; + qs::service::sni::StatusNotifierItem* item = nullptr; + signals: void idChanged(); void titleChanged(); @@ -97,17 +98,7 @@ signals: void iconChanged(); void tooltipTitleChanged(); void tooltipDescriptionChanged(); - void menuChanged(); void onlyMenuChanged(); - -private slots: - void onMenuPathChanged(); - -private: - qs::service::sni::StatusNotifierItem* item = nullptr; - qs::dbus::dbusmenu::DBusMenu* mMenu = nullptr; - - friend class SystemTray; }; ///! System tray @@ -139,3 +130,39 @@ private: QList mItems; }; + +///! Accessor for SystemTrayItem menus. +/// SystemTrayMenuWatcher provides access to the associated +/// [DBusMenuItem](../../quickshell.dbusmenu/dbusmenuitem) for a tray item. +class SystemTrayMenuWatcher: public QObject { + using DBusMenu = qs::dbus::dbusmenu::DBusMenu; + using DBusMenuItem = qs::dbus::dbusmenu::DBusMenuItem; + + Q_OBJECT; + /// The tray item to watch. + Q_PROPERTY(SystemTrayItem* trayItem READ trayItem WRITE setTrayItem NOTIFY trayItemChanged); + /// The menu associated with the tray item. Will be null if `trayItem` is null + /// or has no associated menu. + Q_PROPERTY(DBusMenuItem* menu READ menu NOTIFY menuChanged); + QML_ELEMENT; + +public: + explicit SystemTrayMenuWatcher(QObject* parent = nullptr): QObject(parent) {} + + [[nodiscard]] SystemTrayItem* trayItem() const; + void setTrayItem(SystemTrayItem* item); + + [[nodiscard]] DBusMenuItem* menu() const; + +signals: + void menuChanged(); + void trayItemChanged(); + +private slots: + void onItemDestroyed(); + void onMenuPathChanged(); + +private: + SystemTrayItem* item = nullptr; + DBusMenu* mMenu = nullptr; +};