forked from quickshell/quickshell
core/menu: add handle support to QsMenuOpener + add handle to tray
This commit is contained in:
parent
acdbe73c10
commit
54350277be
6 changed files with 101 additions and 51 deletions
|
@ -21,6 +21,8 @@ QString QsMenuButtonType::toString(QsMenuButtonType::Enum value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QsMenuEntry* QsMenuEntry::menu() { return this; }
|
||||||
|
|
||||||
void QsMenuEntry::display(QObject* parentWindow, int relativeX, int relativeY) {
|
void QsMenuEntry::display(QObject* parentWindow, int relativeX, int relativeY) {
|
||||||
auto* platform = new PlatformMenuEntry(this);
|
auto* platform = new PlatformMenuEntry(this);
|
||||||
|
|
||||||
|
@ -53,22 +55,52 @@ void QsMenuEntry::unref() {
|
||||||
|
|
||||||
QQmlListProperty<QsMenuEntry> QsMenuEntry::children() { return QsMenuEntry::emptyChildren(this); }
|
QQmlListProperty<QsMenuEntry> QsMenuEntry::children() { return QsMenuEntry::emptyChildren(this); }
|
||||||
|
|
||||||
QsMenuEntry* QsMenuOpener::menu() const { return this->mMenu; }
|
QsMenuOpener::~QsMenuOpener() {
|
||||||
|
if (this->mMenu) {
|
||||||
|
if (this->mMenu->menu()) this->mMenu->menu()->unref();
|
||||||
|
this->mMenu->unrefHandle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QsMenuOpener::setMenu(QsMenuEntry* menu) {
|
QsMenuHandle* QsMenuOpener::menu() const { return this->mMenu; }
|
||||||
|
|
||||||
|
void QsMenuOpener::setMenu(QsMenuHandle* menu) {
|
||||||
if (menu == this->mMenu) return;
|
if (menu == this->mMenu) return;
|
||||||
|
|
||||||
if (this->mMenu != nullptr) {
|
if (this->mMenu != nullptr) {
|
||||||
this->mMenu->unref();
|
|
||||||
QObject::disconnect(this->mMenu, nullptr, this, nullptr);
|
QObject::disconnect(this->mMenu, nullptr, this, nullptr);
|
||||||
|
|
||||||
|
if (this->mMenu->menu()) {
|
||||||
|
this->mMenu->menu()->unref();
|
||||||
|
QObject::disconnect(this->mMenu->menu(), nullptr, this, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mMenu->unrefHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->mMenu = menu;
|
this->mMenu = menu;
|
||||||
|
|
||||||
if (menu != nullptr) {
|
if (menu != nullptr) {
|
||||||
|
auto onMenuChanged = [this, menu]() {
|
||||||
|
if (menu->menu()) {
|
||||||
|
QObject::connect(
|
||||||
|
menu->menu(),
|
||||||
|
&QsMenuEntry::childrenChanged,
|
||||||
|
this,
|
||||||
|
&QsMenuOpener::childrenChanged
|
||||||
|
);
|
||||||
|
|
||||||
|
menu->menu()->ref();
|
||||||
|
}
|
||||||
|
|
||||||
|
emit this->childrenChanged();
|
||||||
|
};
|
||||||
|
|
||||||
QObject::connect(menu, &QObject::destroyed, this, &QsMenuOpener::onMenuDestroyed);
|
QObject::connect(menu, &QObject::destroyed, this, &QsMenuOpener::onMenuDestroyed);
|
||||||
QObject::connect(menu, &QsMenuEntry::childrenChanged, this, &QsMenuOpener::childrenChanged);
|
QObject::connect(menu, &QsMenuHandle::menuChanged, this, onMenuChanged);
|
||||||
menu->ref();
|
|
||||||
|
if (menu->menu()) onMenuChanged();
|
||||||
|
menu->refHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
emit this->menuChanged();
|
emit this->menuChanged();
|
||||||
|
@ -82,7 +114,11 @@ void QsMenuOpener::onMenuDestroyed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QQmlListProperty<QsMenuEntry> QsMenuOpener::children() {
|
QQmlListProperty<QsMenuEntry> QsMenuOpener::children() {
|
||||||
return this->mMenu ? this->mMenu->children() : QsMenuEntry::emptyChildren(this);
|
if (this->mMenu && this->mMenu->menu()) {
|
||||||
|
return this->mMenu->menu()->children();
|
||||||
|
} else {
|
||||||
|
return QsMenuEntry::emptyChildren(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qsizetype QsMenuEntry::childCount(QQmlListProperty<QsMenuEntry>* /*property*/) { return 0; }
|
qsizetype QsMenuEntry::childCount(QQmlListProperty<QsMenuEntry>* /*property*/) { return 0; }
|
||||||
|
|
|
@ -33,7 +33,28 @@ public:
|
||||||
Q_INVOKABLE static QString toString(QsMenuButtonType::Enum value);
|
Q_INVOKABLE static QString toString(QsMenuButtonType::Enum value);
|
||||||
};
|
};
|
||||||
|
|
||||||
class QsMenuEntry: public QObject {
|
class QsMenuEntry;
|
||||||
|
|
||||||
|
///! Menu handle for QsMenuOpener
|
||||||
|
/// See @@QsMenuOpener.
|
||||||
|
class QsMenuHandle: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
QML_ELEMENT;
|
||||||
|
QML_UNCREATABLE("");
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit QsMenuHandle(QObject* parent): QObject(parent) {}
|
||||||
|
|
||||||
|
virtual void refHandle() {};
|
||||||
|
virtual void unrefHandle() {};
|
||||||
|
|
||||||
|
[[nodiscard]] virtual QsMenuEntry* menu() = 0;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void menuChanged();
|
||||||
|
};
|
||||||
|
|
||||||
|
class QsMenuEntry: public QsMenuHandle {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
/// If this menu item should be rendered as a separator between other items.
|
/// If this menu item should be rendered as a separator between other items.
|
||||||
///
|
///
|
||||||
|
@ -68,7 +89,9 @@ class QsMenuEntry: public QObject {
|
||||||
QML_UNCREATABLE("QsMenuEntry cannot be directly created");
|
QML_UNCREATABLE("QsMenuEntry cannot be directly created");
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit QsMenuEntry(QObject* parent = nullptr): QObject(parent) {}
|
explicit QsMenuEntry(QObject* parent): QsMenuHandle(parent) {}
|
||||||
|
|
||||||
|
[[nodiscard]] QsMenuEntry* menu() override;
|
||||||
|
|
||||||
/// Display a platform menu at the given location relative to the parent window.
|
/// Display a platform menu at the given location relative to the parent window.
|
||||||
Q_INVOKABLE void display(QObject* parentWindow, qint32 relativeX, qint32 relativeY);
|
Q_INVOKABLE void display(QObject* parentWindow, qint32 relativeX, qint32 relativeY);
|
||||||
|
@ -111,37 +134,22 @@ private:
|
||||||
qsizetype refcount = 0;
|
qsizetype refcount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class QsMenuHandle: public QObject {
|
|
||||||
Q_OBJECT;
|
|
||||||
QML_ELEMENT;
|
|
||||||
QML_UNCREATABLE("");
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit QsMenuHandle(QObject* parent): QObject(parent) {}
|
|
||||||
|
|
||||||
virtual void ref() {};
|
|
||||||
virtual void unref() {};
|
|
||||||
|
|
||||||
[[nodiscard]] virtual QsMenuEntry* menu() const = 0;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void menuChanged();
|
|
||||||
};
|
|
||||||
|
|
||||||
///! Provides access to children of a QsMenuEntry
|
///! Provides access to children of a QsMenuEntry
|
||||||
class QsMenuOpener: public QObject {
|
class QsMenuOpener: public QObject {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
/// The menu to retrieve children from.
|
/// The menu to retrieve children from.
|
||||||
Q_PROPERTY(QsMenuEntry* menu READ menu WRITE setMenu NOTIFY menuChanged);
|
Q_PROPERTY(QsMenuHandle* menu READ menu WRITE setMenu NOTIFY menuChanged);
|
||||||
/// The children of the given menu.
|
/// The children of the given menu.
|
||||||
Q_PROPERTY(QQmlListProperty<QsMenuEntry> children READ children NOTIFY childrenChanged);
|
Q_PROPERTY(QQmlListProperty<QsMenuEntry> children READ children NOTIFY childrenChanged);
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit QsMenuOpener(QObject* parent = nullptr): QObject(parent) {}
|
explicit QsMenuOpener(QObject* parent = nullptr): QObject(parent) {}
|
||||||
|
~QsMenuOpener() override;
|
||||||
|
Q_DISABLE_COPY_MOVE(QsMenuOpener);
|
||||||
|
|
||||||
[[nodiscard]] QsMenuEntry* menu() const;
|
[[nodiscard]] QsMenuHandle* menu() const;
|
||||||
void setMenu(QsMenuEntry* menu);
|
void setMenu(QsMenuHandle* menu);
|
||||||
|
|
||||||
[[nodiscard]] QQmlListProperty<QsMenuEntry> children();
|
[[nodiscard]] QQmlListProperty<QsMenuEntry> children();
|
||||||
|
|
||||||
|
@ -153,7 +161,7 @@ private slots:
|
||||||
void onMenuDestroyed();
|
void onMenuDestroyed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QsMenuEntry* mMenu = nullptr;
|
QsMenuHandle* mMenu = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace qs::menu
|
} // namespace qs::menu
|
||||||
|
|
|
@ -519,7 +519,7 @@ void DBusMenuHandle::setAddress(const QString& service, const QString& path) {
|
||||||
this->onMenuPathChanged();
|
this->onMenuPathChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DBusMenuHandle::ref() {
|
void DBusMenuHandle::refHandle() {
|
||||||
this->refcount++;
|
this->refcount++;
|
||||||
qCDebug(logDbusMenu) << this << "gained a reference. Refcount is now" << this->refcount;
|
qCDebug(logDbusMenu) << this << "gained a reference. Refcount is now" << this->refcount;
|
||||||
|
|
||||||
|
@ -532,7 +532,7 @@ void DBusMenuHandle::ref() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DBusMenuHandle::unref() {
|
void DBusMenuHandle::unrefHandle() {
|
||||||
this->refcount--;
|
this->refcount--;
|
||||||
qCDebug(logDbusMenu) << this << "lost a reference. Refcount is now" << this->refcount;
|
qCDebug(logDbusMenu) << this << "lost a reference. Refcount is now" << this->refcount;
|
||||||
|
|
||||||
|
@ -564,9 +564,7 @@ void DBusMenuHandle::onMenuPathChanged() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QsMenuEntry* DBusMenuHandle::menu() const {
|
QsMenuEntry* DBusMenuHandle::menu() { return this->loaded ? &this->mMenu->rootItem : nullptr; }
|
||||||
return this->loaded ? &this->mMenu->rootItem : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, const DBusMenuHandle* handle) {
|
QDebug operator<<(QDebug debug, const DBusMenuHandle* handle) {
|
||||||
if (handle) {
|
if (handle) {
|
||||||
|
|
|
@ -162,19 +162,15 @@ class DBusMenuHandle;
|
||||||
QDebug operator<<(QDebug debug, const DBusMenuHandle* handle);
|
QDebug operator<<(QDebug debug, const DBusMenuHandle* handle);
|
||||||
|
|
||||||
class DBusMenuHandle: public menu::QsMenuHandle {
|
class DBusMenuHandle: public menu::QsMenuHandle {
|
||||||
Q_OBJECT;
|
|
||||||
QML_ELEMENT;
|
|
||||||
QML_UNCREATABLE("");
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit DBusMenuHandle(QObject* parent): menu::QsMenuHandle(parent) {}
|
explicit DBusMenuHandle(QObject* parent): menu::QsMenuHandle(parent) {}
|
||||||
|
|
||||||
void setAddress(const QString& service, const QString& path);
|
void setAddress(const QString& service, const QString& path);
|
||||||
|
|
||||||
void ref() override;
|
void refHandle() override;
|
||||||
void unref() override;
|
void unrefHandle() override;
|
||||||
|
|
||||||
[[nodiscard]] QsMenuEntry* menu() const override;
|
[[nodiscard]] QsMenuEntry* menu() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onMenuPathChanged();
|
void onMenuPathChanged();
|
||||||
|
|
|
@ -20,7 +20,6 @@ using namespace qs::dbus;
|
||||||
using namespace qs::dbus::dbusmenu;
|
using namespace qs::dbus::dbusmenu;
|
||||||
using namespace qs::service::sni;
|
using namespace qs::service::sni;
|
||||||
using namespace qs::menu::platform;
|
using namespace qs::menu::platform;
|
||||||
using qs::menu::QsMenuHandle;
|
|
||||||
|
|
||||||
SystemTrayItem::SystemTrayItem(qs::service::sni::StatusNotifierItem* item, QObject* parent)
|
SystemTrayItem::SystemTrayItem(qs::service::sni::StatusNotifierItem* item, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
@ -96,6 +95,11 @@ bool SystemTrayItem::hasMenu() const {
|
||||||
return !this->item->menuPath.get().path().isEmpty();
|
return !this->item->menuPath.get().path().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DBusMenuHandle* SystemTrayItem::menu() const {
|
||||||
|
if (this->item == nullptr) return nullptr;
|
||||||
|
return this->item->menuHandle();
|
||||||
|
}
|
||||||
|
|
||||||
bool SystemTrayItem::onlyMenu() const {
|
bool SystemTrayItem::onlyMenu() const {
|
||||||
if (this->item == nullptr) return false;
|
if (this->item == nullptr) return false;
|
||||||
return this->item->isMenu.get();
|
return this->item->isMenu.get();
|
||||||
|
@ -120,7 +124,7 @@ void SystemTrayItem::display(QObject* parentWindow, qint32 relativeX, qint32 rel
|
||||||
QObject::disconnect(handle, nullptr, this, nullptr);
|
QObject::disconnect(handle, nullptr, this, nullptr);
|
||||||
|
|
||||||
if (!handle->menu()) {
|
if (!handle->menu()) {
|
||||||
handle->unref();
|
handle->unrefHandle();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +132,7 @@ void SystemTrayItem::display(QObject* parentWindow, qint32 relativeX, qint32 rel
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
QObject::connect(platform, &PlatformMenuEntry::closed, this, [=]() { platform->deleteLater(); });
|
QObject::connect(platform, &PlatformMenuEntry::closed, this, [=]() { platform->deleteLater(); });
|
||||||
QObject::connect(platform, &QObject::destroyed, this, [=]() { handle->unref(); });
|
QObject::connect(platform, &QObject::destroyed, this, [=]() { handle->unrefHandle(); });
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
auto success = platform->display(parentWindow, relativeX, relativeY);
|
auto success = platform->display(parentWindow, relativeX, relativeY);
|
||||||
|
@ -140,10 +144,10 @@ void SystemTrayItem::display(QObject* parentWindow, qint32 relativeX, qint32 rel
|
||||||
if (handle->menu()) {
|
if (handle->menu()) {
|
||||||
onMenuChanged();
|
onMenuChanged();
|
||||||
} else {
|
} else {
|
||||||
QObject::connect(handle, &QsMenuHandle::menuChanged, this, onMenuChanged);
|
QObject::connect(handle, &DBusMenuHandle::menuChanged, this, onMenuChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle->ref();
|
handle->refHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemTray::SystemTray(QObject* parent): QObject(parent) {
|
SystemTray::SystemTray(QObject* parent): QObject(parent) {
|
||||||
|
@ -179,7 +183,7 @@ SystemTrayItem* SystemTrayMenuWatcher::trayItem() const { return this->item; }
|
||||||
|
|
||||||
SystemTrayMenuWatcher::~SystemTrayMenuWatcher() {
|
SystemTrayMenuWatcher::~SystemTrayMenuWatcher() {
|
||||||
if (this->item != nullptr) {
|
if (this->item != nullptr) {
|
||||||
this->item->item->menuHandle()->unref();
|
this->item->item->menuHandle()->unrefHandle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,14 +191,14 @@ void SystemTrayMenuWatcher::setTrayItem(SystemTrayItem* item) {
|
||||||
if (item == this->item) return;
|
if (item == this->item) return;
|
||||||
|
|
||||||
if (this->item != nullptr) {
|
if (this->item != nullptr) {
|
||||||
this->item->item->menuHandle()->unref();
|
this->item->item->menuHandle()->unrefHandle();
|
||||||
QObject::disconnect(this->item, nullptr, this, nullptr);
|
QObject::disconnect(this->item, nullptr, this, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->item = item;
|
this->item = item;
|
||||||
|
|
||||||
if (item != nullptr) {
|
if (item != nullptr) {
|
||||||
this->item->item->menuHandle()->ref();
|
this->item->item->menuHandle()->refHandle();
|
||||||
|
|
||||||
QObject::connect(item, &QObject::destroyed, this, &SystemTrayMenuWatcher::onItemDestroyed);
|
QObject::connect(item, &QObject::destroyed, this, &SystemTrayMenuWatcher::onItemDestroyed);
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,9 @@ Q_ENUM_NS(Enum);
|
||||||
class SystemTrayItem: public QObject {
|
class SystemTrayItem: public QObject {
|
||||||
using DBusMenuItem = qs::dbus::dbusmenu::DBusMenuItem;
|
using DBusMenuItem = qs::dbus::dbusmenu::DBusMenuItem;
|
||||||
|
|
||||||
|
// intentionally wrongly aliased to temporarily hack around a docgen issue
|
||||||
|
using QsMenuHandle = qs::dbus::dbusmenu::DBusMenuHandle;
|
||||||
|
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
/// A name unique to the application, such as its name.
|
/// A name unique to the application, such as its name.
|
||||||
Q_PROPERTY(QString id READ id NOTIFY idChanged);
|
Q_PROPERTY(QString id READ id NOTIFY idChanged);
|
||||||
|
@ -64,9 +67,10 @@ class SystemTrayItem: public QObject {
|
||||||
Q_PROPERTY(QString icon READ icon NOTIFY iconChanged);
|
Q_PROPERTY(QString icon READ icon NOTIFY iconChanged);
|
||||||
Q_PROPERTY(QString tooltipTitle READ tooltipTitle NOTIFY tooltipTitleChanged);
|
Q_PROPERTY(QString tooltipTitle READ tooltipTitle NOTIFY tooltipTitleChanged);
|
||||||
Q_PROPERTY(QString tooltipDescription READ tooltipDescription NOTIFY tooltipDescriptionChanged);
|
Q_PROPERTY(QString tooltipDescription READ tooltipDescription NOTIFY tooltipDescriptionChanged);
|
||||||
/// If this tray item has an associated menu accessible via @@display()
|
/// If this tray item has an associated menu accessible via @@display() or @@menu.
|
||||||
/// or a @@SystemTrayMenuWatcher.
|
|
||||||
Q_PROPERTY(bool hasMenu READ hasMenu NOTIFY hasMenuChanged);
|
Q_PROPERTY(bool hasMenu READ hasMenu NOTIFY hasMenuChanged);
|
||||||
|
/// A handle to the menu associated with this tray item, if any.
|
||||||
|
Q_PROPERTY(QsMenuHandle* menu READ menu NOTIFY hasMenuChanged);
|
||||||
/// If this tray item only offers a menu and activation will do nothing.
|
/// If this tray item only offers a menu and activation will do nothing.
|
||||||
Q_PROPERTY(bool onlyMenu READ onlyMenu NOTIFY onlyMenuChanged);
|
Q_PROPERTY(bool onlyMenu READ onlyMenu NOTIFY onlyMenuChanged);
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
|
@ -95,6 +99,7 @@ public:
|
||||||
[[nodiscard]] QString tooltipTitle() const;
|
[[nodiscard]] QString tooltipTitle() const;
|
||||||
[[nodiscard]] QString tooltipDescription() const;
|
[[nodiscard]] QString tooltipDescription() const;
|
||||||
[[nodiscard]] bool hasMenu() const;
|
[[nodiscard]] bool hasMenu() const;
|
||||||
|
[[nodiscard]] QsMenuHandle* menu() const;
|
||||||
[[nodiscard]] bool onlyMenu() const;
|
[[nodiscard]] bool onlyMenu() const;
|
||||||
|
|
||||||
qs::service::sni::StatusNotifierItem* item = nullptr;
|
qs::service::sni::StatusNotifierItem* item = nullptr;
|
||||||
|
@ -136,6 +141,9 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
///! Accessor for SystemTrayItem menus.
|
///! Accessor for SystemTrayItem menus.
|
||||||
|
/// > [!ERROR] Deprecated in favor of @@Quickshell.QsMenuOpener.menu,
|
||||||
|
/// > which now supports directly accessing a tray menu via @@SystemTrayItem.menu.
|
||||||
|
///
|
||||||
/// SystemTrayMenuWatcher provides access to the associated
|
/// SystemTrayMenuWatcher provides access to the associated
|
||||||
/// @@Quickshell.DBusMenu.DBusMenuItem for a tray item.
|
/// @@Quickshell.DBusMenu.DBusMenuItem for a tray item.
|
||||||
class SystemTrayMenuWatcher: public QObject {
|
class SystemTrayMenuWatcher: public QObject {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue