forked from quickshell/quickshell
dbus/dbusmenu: separate menu handles from status notifier items
No api changes yet.
This commit is contained in:
parent
a71a6fb3ac
commit
acdbe73c10
6 changed files with 157 additions and 60 deletions
|
@ -111,6 +111,23 @@ 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;
|
||||||
|
|
|
@ -512,4 +512,72 @@ DBusMenuPngImage::requestImage(const QString& /*unused*/, QSize* size, const QSi
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DBusMenuHandle::setAddress(const QString& service, const QString& path) {
|
||||||
|
if (service == this->service && path == this->path) return;
|
||||||
|
this->service = service;
|
||||||
|
this->path = path;
|
||||||
|
this->onMenuPathChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DBusMenuHandle::ref() {
|
||||||
|
this->refcount++;
|
||||||
|
qCDebug(logDbusMenu) << this << "gained a reference. Refcount is now" << this->refcount;
|
||||||
|
|
||||||
|
if (this->refcount == 1 || !this->mMenu) {
|
||||||
|
this->onMenuPathChanged();
|
||||||
|
} else {
|
||||||
|
// Refresh the layout when opening a menu in case a bad client isn't updating it
|
||||||
|
// and another ref is open somewhere.
|
||||||
|
this->mMenu->rootItem.updateLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DBusMenuHandle::unref() {
|
||||||
|
this->refcount--;
|
||||||
|
qCDebug(logDbusMenu) << this << "lost a reference. Refcount is now" << this->refcount;
|
||||||
|
|
||||||
|
if (this->refcount == 0) {
|
||||||
|
this->onMenuPathChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DBusMenuHandle::onMenuPathChanged() {
|
||||||
|
qCDebug(logDbusMenu) << "Updating" << this << "with refcount" << this->refcount;
|
||||||
|
|
||||||
|
if (this->mMenu) {
|
||||||
|
this->mMenu->deleteLater();
|
||||||
|
this->mMenu = nullptr;
|
||||||
|
this->loaded = false;
|
||||||
|
emit this->menuChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->refcount > 0 && !this->service.isEmpty() && !this->path.isEmpty()) {
|
||||||
|
this->mMenu = new DBusMenu(this->service, this->path);
|
||||||
|
this->mMenu->setParent(this);
|
||||||
|
|
||||||
|
QObject::connect(&this->mMenu->rootItem, &DBusMenuItem::layoutUpdated, this, [this]() {
|
||||||
|
this->loaded = true;
|
||||||
|
emit this->menuChanged();
|
||||||
|
});
|
||||||
|
|
||||||
|
this->mMenu->rootItem.setShowChildrenRecursive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QsMenuEntry* DBusMenuHandle::menu() const {
|
||||||
|
return this->loaded ? &this->mMenu->rootItem : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug debug, const DBusMenuHandle* handle) {
|
||||||
|
if (handle) {
|
||||||
|
auto saver = QDebugStateSaver(debug);
|
||||||
|
debug.nospace() << "DBusMenuHandle(" << static_cast<const void*>(handle)
|
||||||
|
<< ", service=" << handle->service << ", path=" << handle->path << ')';
|
||||||
|
} else {
|
||||||
|
debug << "DBusMenuHandle(nullptr)";
|
||||||
|
}
|
||||||
|
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace qs::dbus::dbusmenu
|
} // namespace qs::dbus::dbusmenu
|
||||||
|
|
|
@ -157,4 +157,35 @@ public:
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DBusMenuHandle;
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug debug, const DBusMenuHandle* handle);
|
||||||
|
|
||||||
|
class DBusMenuHandle: public menu::QsMenuHandle {
|
||||||
|
Q_OBJECT;
|
||||||
|
QML_ELEMENT;
|
||||||
|
QML_UNCREATABLE("");
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DBusMenuHandle(QObject* parent): menu::QsMenuHandle(parent) {}
|
||||||
|
|
||||||
|
void setAddress(const QString& service, const QString& path);
|
||||||
|
|
||||||
|
void ref() override;
|
||||||
|
void unref() override;
|
||||||
|
|
||||||
|
[[nodiscard]] QsMenuEntry* menu() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onMenuPathChanged();
|
||||||
|
|
||||||
|
QString service;
|
||||||
|
QString path;
|
||||||
|
DBusMenu* mMenu = nullptr;
|
||||||
|
bool loaded = false;
|
||||||
|
quint32 refcount = 0;
|
||||||
|
|
||||||
|
friend QDebug operator<<(QDebug debug, const DBusMenuHandle* handle);
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace qs::dbus::dbusmenu
|
} // namespace qs::dbus::dbusmenu
|
||||||
|
|
|
@ -232,48 +232,12 @@ void StatusNotifierItem::updateIcon() {
|
||||||
emit this->iconChanged();
|
emit this->iconChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
DBusMenu* StatusNotifierItem::menu() const { return this->mMenu; }
|
DBusMenuHandle* StatusNotifierItem::menuHandle() {
|
||||||
|
return this->menuPath.get().path().isEmpty() ? nullptr : &this->mMenuHandle;
|
||||||
void StatusNotifierItem::refMenu() {
|
|
||||||
this->menuRefcount++;
|
|
||||||
qCDebug(logSniMenu) << "Menu of" << this << "gained a reference. Refcount is now"
|
|
||||||
<< this->menuRefcount;
|
|
||||||
|
|
||||||
if (this->menuRefcount == 1) {
|
|
||||||
this->onMenuPathChanged();
|
|
||||||
} else {
|
|
||||||
// Refresh the layout when opening a menu in case a bad client isn't updating it
|
|
||||||
// and another ref is open somewhere.
|
|
||||||
this->mMenu->rootItem.updateLayout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StatusNotifierItem::unrefMenu() {
|
|
||||||
this->menuRefcount--;
|
|
||||||
qCDebug(logSniMenu) << "Menu of" << this << "lost a reference. Refcount is now"
|
|
||||||
<< this->menuRefcount;
|
|
||||||
|
|
||||||
if (this->menuRefcount == 0) {
|
|
||||||
this->onMenuPathChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusNotifierItem::onMenuPathChanged() {
|
void StatusNotifierItem::onMenuPathChanged() {
|
||||||
qCDebug(logSniMenu) << "Updating menu of" << this << "with refcount" << this->menuRefcount
|
this->mMenuHandle.setAddress(this->item->service(), this->menuPath.get().path());
|
||||||
<< "path" << this->menuPath.get().path();
|
|
||||||
|
|
||||||
if (this->mMenu) {
|
|
||||||
this->mMenu->deleteLater();
|
|
||||||
this->mMenu = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->menuRefcount > 0 && !this->menuPath.get().path().isEmpty()) {
|
|
||||||
this->mMenu = new DBusMenu(this->item->service(), this->menuPath.get().path());
|
|
||||||
this->mMenu->setParent(this);
|
|
||||||
this->mMenu->rootItem.setShowChildrenRecursive(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit this->menuChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusNotifierItem::onGetAllFinished() {
|
void StatusNotifierItem::onGetAllFinished() {
|
||||||
|
|
|
@ -42,9 +42,7 @@ public:
|
||||||
[[nodiscard]] QString iconId() const;
|
[[nodiscard]] QString iconId() const;
|
||||||
[[nodiscard]] QPixmap createPixmap(const QSize& size) const;
|
[[nodiscard]] QPixmap createPixmap(const QSize& size) const;
|
||||||
|
|
||||||
[[nodiscard]] qs::dbus::dbusmenu::DBusMenu* menu() const;
|
[[nodiscard]] dbus::dbusmenu::DBusMenuHandle* menuHandle();
|
||||||
void refMenu();
|
|
||||||
void unrefMenu();
|
|
||||||
|
|
||||||
void activate();
|
void activate();
|
||||||
void secondaryActivate();
|
void secondaryActivate();
|
||||||
|
@ -73,7 +71,6 @@ public:
|
||||||
signals:
|
signals:
|
||||||
void iconChanged();
|
void iconChanged();
|
||||||
void ready();
|
void ready();
|
||||||
void menuChanged();
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateIcon();
|
void updateIcon();
|
||||||
|
@ -87,8 +84,7 @@ private:
|
||||||
TrayImageHandle imageHandle {this};
|
TrayImageHandle imageHandle {this};
|
||||||
bool mReady = false;
|
bool mReady = false;
|
||||||
|
|
||||||
dbus::dbusmenu::DBusMenu* mMenu = nullptr;
|
dbus::dbusmenu::DBusMenuHandle mMenuHandle {this};
|
||||||
quint32 menuRefcount = 0;
|
|
||||||
|
|
||||||
// bumped to inhibit caching
|
// bumped to inhibit caching
|
||||||
quint32 iconIndex = 0;
|
quint32 iconIndex = 0;
|
||||||
|
|
|
@ -20,6 +20,7 @@ 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)
|
||||||
|
@ -108,25 +109,41 @@ void SystemTrayItem::scroll(qint32 delta, bool horizontal) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemTrayItem::display(QObject* parentWindow, qint32 relativeX, qint32 relativeY) {
|
void SystemTrayItem::display(QObject* parentWindow, qint32 relativeX, qint32 relativeY) {
|
||||||
this->item->refMenu();
|
if (!this->item->menuHandle()) {
|
||||||
if (!this->item->menu()) {
|
|
||||||
this->item->unrefMenu();
|
|
||||||
qCritical() << "No menu present for" << this;
|
qCritical() << "No menu present for" << this;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* platform = new PlatformMenuEntry(&this->item->menu()->rootItem);
|
auto* handle = this->item->menuHandle();
|
||||||
|
|
||||||
|
auto onMenuChanged = [this, parentWindow, relativeX, relativeY, handle]() {
|
||||||
|
QObject::disconnect(handle, nullptr, this, nullptr);
|
||||||
|
|
||||||
|
if (!handle->menu()) {
|
||||||
|
handle->unref();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* platform = new PlatformMenuEntry(handle->menu());
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
QObject::connect(platform, &PlatformMenuEntry::closed, this, [=]() { platform->deleteLater(); });
|
||||||
|
QObject::connect(platform, &QObject::destroyed, this, [=]() { handle->unref(); });
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
QObject::connect(&this->item->menu()->rootItem, &DBusMenuItem::layoutUpdated, platform, [=]() {
|
|
||||||
platform->relayout();
|
|
||||||
auto success = platform->display(parentWindow, relativeX, relativeY);
|
auto success = platform->display(parentWindow, relativeX, relativeY);
|
||||||
|
|
||||||
// calls destroy which also unrefs
|
// calls destroy which also unrefs
|
||||||
if (!success) delete platform;
|
if (!success) delete platform;
|
||||||
});
|
};
|
||||||
|
|
||||||
QObject::connect(platform, &PlatformMenuEntry::closed, this, [=]() { platform->deleteLater(); });
|
if (handle->menu()) {
|
||||||
QObject::connect(platform, &QObject::destroyed, this, [this]() { this->item->unrefMenu(); });
|
onMenuChanged();
|
||||||
|
} else {
|
||||||
|
QObject::connect(handle, &QsMenuHandle::menuChanged, this, onMenuChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
handle->ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemTray::SystemTray(QObject* parent): QObject(parent) {
|
SystemTray::SystemTray(QObject* parent): QObject(parent) {
|
||||||
|
@ -162,7 +179,7 @@ SystemTrayItem* SystemTrayMenuWatcher::trayItem() const { return this->item; }
|
||||||
|
|
||||||
SystemTrayMenuWatcher::~SystemTrayMenuWatcher() {
|
SystemTrayMenuWatcher::~SystemTrayMenuWatcher() {
|
||||||
if (this->item != nullptr) {
|
if (this->item != nullptr) {
|
||||||
this->item->item->unrefMenu();
|
this->item->item->menuHandle()->unref();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,20 +187,20 @@ 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->unrefMenu();
|
this->item->item->menuHandle()->unref();
|
||||||
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->refMenu();
|
this->item->item->menuHandle()->ref();
|
||||||
|
|
||||||
QObject::connect(item, &QObject::destroyed, this, &SystemTrayMenuWatcher::onItemDestroyed);
|
QObject::connect(item, &QObject::destroyed, this, &SystemTrayMenuWatcher::onItemDestroyed);
|
||||||
|
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
item->item,
|
item->item->menuHandle(),
|
||||||
&StatusNotifierItem::menuChanged,
|
&DBusMenuHandle::menuChanged,
|
||||||
this,
|
this,
|
||||||
&SystemTrayMenuWatcher::menuChanged
|
&SystemTrayMenuWatcher::menuChanged
|
||||||
);
|
);
|
||||||
|
@ -194,7 +211,11 @@ void SystemTrayMenuWatcher::setTrayItem(SystemTrayItem* item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DBusMenuItem* SystemTrayMenuWatcher::menu() const {
|
DBusMenuItem* SystemTrayMenuWatcher::menu() const {
|
||||||
return this->item ? &this->item->item->menu()->rootItem : nullptr;
|
if (this->item) {
|
||||||
|
return static_cast<DBusMenuItem*>(this->item->item->menuHandle()->menu()); // NOLINT
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemTrayMenuWatcher::onItemDestroyed() {
|
void SystemTrayMenuWatcher::onItemDestroyed() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue