forked from quickshell/quickshell
		
	service/tray: adopt bindable properties
This commit is contained in:
		
							parent
							
								
									0e9e593078
								
							
						
					
					
						commit
						b43b4a06d0
					
				
					 4 changed files with 200 additions and 135 deletions
				
			
		| 
						 | 
				
			
			@ -1,7 +1,6 @@
 | 
			
		|||
#include "dbus_item_types.hpp"
 | 
			
		||||
 | 
			
		||||
#include <qdbusargument.h>
 | 
			
		||||
#include <qdbusextratypes.h>
 | 
			
		||||
#include <qdebug.h>
 | 
			
		||||
#include <qendian.h>
 | 
			
		||||
#include <qimage.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -10,6 +9,15 @@
 | 
			
		|||
#include <qsysinfo.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
bool DBusSniIconPixmap::operator==(const DBusSniIconPixmap& other) const {
 | 
			
		||||
	return this->width == other.width && this->height == other.height && this->data == other.data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool DBusSniTooltip::operator==(const DBusSniTooltip& other) const {
 | 
			
		||||
	return this->icon == other.icon && this->title == other.title
 | 
			
		||||
	    && this->description == other.description && this->iconPixmaps == other.iconPixmaps;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QImage DBusSniIconPixmap::createImage() const {
 | 
			
		||||
	// fix byte order if on a little endian machine
 | 
			
		||||
	if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
 | 
			
		||||
| 
						 | 
				
			
			@ -113,9 +121,3 @@ QDebug operator<<(QDebug debug, const DBusSniTooltip& tooltip) {
 | 
			
		|||
 | 
			
		||||
	return debug;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QDebug operator<<(QDebug debug, const QDBusObjectPath& path) {
 | 
			
		||||
	debug.nospace() << "QDBusObjectPath(" << path.path() << ")";
 | 
			
		||||
 | 
			
		||||
	return debug;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,6 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <qdbusargument.h>
 | 
			
		||||
#include <qdbusextratypes.h>
 | 
			
		||||
#include <qdebug.h>
 | 
			
		||||
#include <qlist.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -12,6 +11,8 @@ struct DBusSniIconPixmap {
 | 
			
		|||
 | 
			
		||||
	// valid only for the lifetime of the pixmap
 | 
			
		||||
	[[nodiscard]] QImage createImage() const;
 | 
			
		||||
 | 
			
		||||
	bool operator==(const DBusSniIconPixmap& other) const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using DBusSniIconPixmapList = QList<DBusSniIconPixmap>;
 | 
			
		||||
| 
						 | 
				
			
			@ -21,6 +22,8 @@ struct DBusSniTooltip {
 | 
			
		|||
	DBusSniIconPixmapList iconPixmaps;
 | 
			
		||||
	QString title;
 | 
			
		||||
	QString description;
 | 
			
		||||
 | 
			
		||||
	bool operator==(const DBusSniTooltip& other) const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const QDBusArgument& operator>>(const QDBusArgument& argument, DBusSniIconPixmap& pixmap);
 | 
			
		||||
| 
						 | 
				
			
			@ -32,4 +35,3 @@ const QDBusArgument& operator<<(QDBusArgument& argument, const DBusSniTooltip& t
 | 
			
		|||
 | 
			
		||||
QDebug operator<<(QDebug debug, const DBusSniIconPixmap& pixmap);
 | 
			
		||||
QDebug operator<<(QDebug debug, const DBusSniTooltip& tooltip);
 | 
			
		||||
QDebug operator<<(QDebug debug, const QDBusObjectPath& path);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
#include "item.hpp"
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include <qdbuserror.h>
 | 
			
		||||
#include <qdbusextratypes.h>
 | 
			
		||||
#include <qdbusmetatype.h>
 | 
			
		||||
#include <qdbuspendingcall.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +16,7 @@
 | 
			
		|||
#include <qrect.h>
 | 
			
		||||
#include <qsize.h>
 | 
			
		||||
#include <qstring.h>
 | 
			
		||||
#include <qstringliteral.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -58,36 +59,71 @@ StatusNotifierItem::StatusNotifierItem(const QString& address, QObject* parent)
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// clang-format off
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewTitle, &this->pTitle, &AbstractDBusProperty::update);
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewIcon, &this->pIconName, &AbstractDBusProperty::update);
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewIcon, &this->pIconPixmaps, &AbstractDBusProperty::update);
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewIcon, &this->pIconThemePath, &AbstractDBusProperty::update);
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewOverlayIcon, &this->pOverlayIconName, &AbstractDBusProperty::update);
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewOverlayIcon, &this->pOverlayIconPixmaps, &AbstractDBusProperty::update);
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewOverlayIcon, &this->pIconThemePath, &AbstractDBusProperty::update);
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewAttentionIcon, &this->pAttentionIconName, &AbstractDBusProperty::update);
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewAttentionIcon, &this->pAttentionIconPixmaps, &AbstractDBusProperty::update);
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewAttentionIcon, &this->pIconThemePath, &AbstractDBusProperty::update);
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewToolTip, &this->pTooltip, &AbstractDBusProperty::update);
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewTitle, this, [this]() {
 | 
			
		||||
		this->pTitle.requestUpdate();
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	QObject::connect(&this->pIconThemePath, &AbstractDBusProperty::changed, this, &StatusNotifierItem::updateIcon);
 | 
			
		||||
	QObject::connect(&this->pIconName, &AbstractDBusProperty::changed, this, &StatusNotifierItem::updateIcon);
 | 
			
		||||
	QObject::connect(&this->pAttentionIconName, &AbstractDBusProperty::changed, this, &StatusNotifierItem::updateIcon);
 | 
			
		||||
	QObject::connect(&this->pOverlayIconName, &AbstractDBusProperty::changed, this, &StatusNotifierItem::updateIcon);
 | 
			
		||||
	QObject::connect(&this->pIconPixmaps, &AbstractDBusProperty::changed, this, &StatusNotifierItem::updateIcon);
 | 
			
		||||
	QObject::connect(&this->pAttentionIconPixmaps, &AbstractDBusProperty::changed, this, &StatusNotifierItem::updateIcon);
 | 
			
		||||
	QObject::connect(&this->pOverlayIconPixmaps, &AbstractDBusProperty::changed, this, &StatusNotifierItem::updateIcon);
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewIcon, this, [this]() {
 | 
			
		||||
		this->pIconName.requestUpdate();
 | 
			
		||||
		this->pIconPixmaps.requestUpdate();
 | 
			
		||||
		this->pIconThemePath.requestUpdate();
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewOverlayIcon, this, [this]() {
 | 
			
		||||
		this->pOverlayIconName.requestUpdate();
 | 
			
		||||
		this->pOverlayIconPixmaps.requestUpdate();
 | 
			
		||||
		this->pIconThemePath.requestUpdate();
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewAttentionIcon, this, [this]() {
 | 
			
		||||
		this->pAttentionIconName.requestUpdate();
 | 
			
		||||
		this->pAttentionIconPixmaps.requestUpdate();
 | 
			
		||||
		this->pIconThemePath.requestUpdate();
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewToolTip, this, [this]() {
 | 
			
		||||
		this->pTooltip.requestUpdate();
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	QObject::connect(&this->properties, &DBusPropertyGroup::getAllFinished, this, &StatusNotifierItem::onGetAllFinished);
 | 
			
		||||
	QObject::connect(&this->properties, &DBusPropertyGroup::getAllFailed, this, &StatusNotifierItem::onGetAllFailed);
 | 
			
		||||
	QObject::connect(&this->pMenuPath, &AbstractDBusProperty::changed, this, &StatusNotifierItem::onMenuPathChanged);
 | 
			
		||||
	// clang-format on
 | 
			
		||||
 | 
			
		||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewStatus, this, [this](QString value) {
 | 
			
		||||
		qCDebug(logStatusNotifierItem) << "Received update for" << this->pStatus.toString() << value;
 | 
			
		||||
		this->pStatus.set(std::move(value));
 | 
			
		||||
	this->bIcon.setBinding([this]() -> QString {
 | 
			
		||||
		if (this->bStatus.value() == Status::NeedsAttention) {
 | 
			
		||||
			auto name = this->bAttentionIconName.value();
 | 
			
		||||
			if (!name.isEmpty())
 | 
			
		||||
				return IconImageProvider::requestString(name, this->bIconThemePath.value());
 | 
			
		||||
		} else {
 | 
			
		||||
			auto name = this->bIconName.value();
 | 
			
		||||
			auto overlayName = this->bOverlayIconName.value();
 | 
			
		||||
			if (!name.isEmpty() && overlayName.isEmpty())
 | 
			
		||||
				return IconImageProvider::requestString(name, this->bIconThemePath.value());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return this->imageHandle.url() % "/" % QString::number(this->pixmapIndex);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	this->bHasMenu.setBinding([this]() { return !this->bMenuPath.value().path().isEmpty(); });
 | 
			
		||||
 | 
			
		||||
	QObject::connect(
 | 
			
		||||
	    this->item,
 | 
			
		||||
	    &DBusStatusNotifierItem::NewStatus,
 | 
			
		||||
	    this,
 | 
			
		||||
	    [this](const QString& value) {
 | 
			
		||||
		    auto result = DBusDataTransform<Status::Enum>::fromWire(value);
 | 
			
		||||
 | 
			
		||||
		    if (result.isValid()) {
 | 
			
		||||
			    this->bStatus = result.value;
 | 
			
		||||
			    qCDebug(logStatusNotifierItem)
 | 
			
		||||
			        << "Received status update for" << this->properties.toString() << result.value;
 | 
			
		||||
		    } else {
 | 
			
		||||
			    qCWarning(logStatusNotifierItem)
 | 
			
		||||
			        << "Received invalid status update for" << this->properties.toString() << value;
 | 
			
		||||
		    }
 | 
			
		||||
	    }
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	this->properties.setInterface(this->item);
 | 
			
		||||
	this->properties.updateAllViaGetAll();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -95,22 +131,8 @@ StatusNotifierItem::StatusNotifierItem(const QString& address, QObject* parent)
 | 
			
		|||
bool StatusNotifierItem::isValid() const { return this->item->isValid(); }
 | 
			
		||||
bool StatusNotifierItem::isReady() const { return this->mReady; }
 | 
			
		||||
 | 
			
		||||
QString StatusNotifierItem::iconId() const {
 | 
			
		||||
	if (this->pStatus.get() == "NeedsAttention") {
 | 
			
		||||
		auto name = this->pAttentionIconName.get();
 | 
			
		||||
		if (!name.isEmpty()) return IconImageProvider::requestString(name, this->pIconThemePath.get());
 | 
			
		||||
	} else {
 | 
			
		||||
		auto name = this->pIconName.get();
 | 
			
		||||
		auto overlayName = this->pOverlayIconName.get();
 | 
			
		||||
		if (!name.isEmpty() && overlayName.isEmpty())
 | 
			
		||||
			return IconImageProvider::requestString(name, this->pIconThemePath.get());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return this->imageHandle.url() + "/" + QString::number(this->iconIndex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QPixmap StatusNotifierItem::createPixmap(const QSize& size) const {
 | 
			
		||||
	auto needsAttention = this->pStatus.get() == "NeedsAttention";
 | 
			
		||||
	auto needsAttention = this->bStatus.value() == Status::NeedsAttention;
 | 
			
		||||
 | 
			
		||||
	auto closestPixmap = [](const QSize& size, const DBusSniIconPixmapList& pixmaps) {
 | 
			
		||||
		const DBusSniIconPixmap* ret = nullptr;
 | 
			
		||||
| 
						 | 
				
			
			@ -135,11 +157,11 @@ QPixmap StatusNotifierItem::createPixmap(const QSize& size) const {
 | 
			
		|||
 | 
			
		||||
	QPixmap pixmap;
 | 
			
		||||
	if (needsAttention) {
 | 
			
		||||
		if (!this->pAttentionIconName.get().isEmpty()) {
 | 
			
		||||
			auto icon = QIcon::fromTheme(this->pAttentionIconName.get());
 | 
			
		||||
		if (!this->bAttentionIconName.value().isEmpty()) {
 | 
			
		||||
			auto icon = QIcon::fromTheme(this->bAttentionIconName.value());
 | 
			
		||||
			pixmap = icon.pixmap(size.width(), size.height());
 | 
			
		||||
		} else {
 | 
			
		||||
			const auto* icon = closestPixmap(size, this->pAttentionIconPixmaps.get());
 | 
			
		||||
			const auto* icon = closestPixmap(size, this->bAttentionIconPixmaps.value());
 | 
			
		||||
 | 
			
		||||
			if (icon != nullptr) {
 | 
			
		||||
				const auto image =
 | 
			
		||||
| 
						 | 
				
			
			@ -149,11 +171,11 @@ QPixmap StatusNotifierItem::createPixmap(const QSize& size) const {
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if (!this->pIconName.get().isEmpty()) {
 | 
			
		||||
			auto icon = QIcon::fromTheme(this->pIconName.get());
 | 
			
		||||
		if (!this->bIconName.value().isEmpty()) {
 | 
			
		||||
			auto icon = QIcon::fromTheme(this->bIconName.value());
 | 
			
		||||
			pixmap = icon.pixmap(size.width(), size.height());
 | 
			
		||||
		} else {
 | 
			
		||||
			const auto* icon = closestPixmap(size, this->pIconPixmaps.get());
 | 
			
		||||
			const auto* icon = closestPixmap(size, this->bIconPixmaps.value());
 | 
			
		||||
 | 
			
		||||
			if (icon != nullptr) {
 | 
			
		||||
				const auto image =
 | 
			
		||||
| 
						 | 
				
			
			@ -164,11 +186,11 @@ QPixmap StatusNotifierItem::createPixmap(const QSize& size) const {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		QPixmap overlay;
 | 
			
		||||
		if (!this->pOverlayIconName.get().isEmpty()) {
 | 
			
		||||
			auto icon = QIcon::fromTheme(this->pOverlayIconName.get());
 | 
			
		||||
		if (!this->bOverlayIconName.value().isEmpty()) {
 | 
			
		||||
			auto icon = QIcon::fromTheme(this->bOverlayIconName.value());
 | 
			
		||||
			overlay = icon.pixmap(pixmap.width(), pixmap.height());
 | 
			
		||||
		} else {
 | 
			
		||||
			const auto* icon = closestPixmap(pixmap.size(), this->pOverlayIconPixmaps.get());
 | 
			
		||||
			const auto* icon = closestPixmap(pixmap.size(), this->bOverlayIconPixmaps.value());
 | 
			
		||||
 | 
			
		||||
			if (icon != nullptr) {
 | 
			
		||||
				const auto image =
 | 
			
		||||
| 
						 | 
				
			
			@ -231,17 +253,14 @@ void StatusNotifierItem::scroll(qint32 delta, bool horizontal) const {
 | 
			
		|||
	this->item->Scroll(delta, horizontal ? "horizontal" : "vertical");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StatusNotifierItem::updateIcon() {
 | 
			
		||||
	this->iconIndex++;
 | 
			
		||||
	emit this->iconChanged();
 | 
			
		||||
}
 | 
			
		||||
void StatusNotifierItem::updatePixmapIndex() { this->pixmapIndex = this->pixmapIndex + 1; }
 | 
			
		||||
 | 
			
		||||
DBusMenuHandle* StatusNotifierItem::menuHandle() {
 | 
			
		||||
	return this->pMenuPath.get().path().isEmpty() ? nullptr : &this->mMenuHandle;
 | 
			
		||||
	return this->bMenuPath.value().path().isEmpty() ? nullptr : &this->mMenuHandle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StatusNotifierItem::onMenuPathChanged() {
 | 
			
		||||
	this->mMenuHandle.setAddress(this->item->service(), this->pMenuPath.get().path());
 | 
			
		||||
	this->mMenuHandle.setAddress(this->item->service(), this->bMenuPath.value().path());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StatusNotifierItem::onGetAllFinished() {
 | 
			
		||||
| 
						 | 
				
			
			@ -282,41 +301,6 @@ TrayImageHandle::requestPixmap(const QString& /*unused*/, QSize* size, const QSi
 | 
			
		|||
	return pixmap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString StatusNotifierItem::id() const { return this->pId.get(); }
 | 
			
		||||
QString StatusNotifierItem::title() const { return this->pTitle.get(); }
 | 
			
		||||
 | 
			
		||||
Status::Enum StatusNotifierItem::status() const {
 | 
			
		||||
	auto status = this->pStatus.get();
 | 
			
		||||
 | 
			
		||||
	if (status == "Passive") return Status::Passive;
 | 
			
		||||
	if (status == "Active") return Status::Active;
 | 
			
		||||
	if (status == "NeedsAttention") return Status::NeedsAttention;
 | 
			
		||||
 | 
			
		||||
	qCWarning(logStatusNotifierItem) << "Nonconformant StatusNotifierItem status" << status
 | 
			
		||||
	                                 << "returned for" << this->properties.toString();
 | 
			
		||||
 | 
			
		||||
	return Status::Passive;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Category::Enum StatusNotifierItem::category() const {
 | 
			
		||||
	auto category = this->pCategory.get();
 | 
			
		||||
 | 
			
		||||
	if (category == "ApplicationStatus") return Category::ApplicationStatus;
 | 
			
		||||
	if (category == "SystemServices") return Category::SystemServices;
 | 
			
		||||
	if (category == "Hardware") return Category::Hardware;
 | 
			
		||||
 | 
			
		||||
	qCWarning(logStatusNotifierItem) << "Nonconformant StatusNotifierItem category" << category
 | 
			
		||||
	                                 << "returned for" << this->properties.toString();
 | 
			
		||||
 | 
			
		||||
	return Category::ApplicationStatus;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString StatusNotifierItem::tooltipTitle() const { return this->pTooltip.get().title; }
 | 
			
		||||
QString StatusNotifierItem::tooltipDescription() const { return this->pTooltip.get().description; }
 | 
			
		||||
 | 
			
		||||
bool StatusNotifierItem::hasMenu() const { return !this->pMenuPath.get().path().isEmpty(); }
 | 
			
		||||
bool StatusNotifierItem::onlyMenu() const { return this->pIsMenu.get(); }
 | 
			
		||||
 | 
			
		||||
void StatusNotifierItem::display(QObject* parentWindow, qint32 relativeX, qint32 relativeY) {
 | 
			
		||||
	if (!this->menuHandle()) {
 | 
			
		||||
		qCritical() << "No menu present for" << this;
 | 
			
		||||
| 
						 | 
				
			
			@ -352,3 +336,30 @@ void StatusNotifierItem::display(QObject* parentWindow, qint32 relativeX, qint32
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
} // namespace qs::service::sni
 | 
			
		||||
 | 
			
		||||
namespace qs::dbus {
 | 
			
		||||
using namespace qs::service::sni;
 | 
			
		||||
 | 
			
		||||
DBusResult<Status::Enum> DBusDataTransform<Status::Enum>::fromWire(const QString& wire) {
 | 
			
		||||
	if (wire == QStringLiteral("Passive")) return DBusResult(Status::Passive);
 | 
			
		||||
	if (wire == QStringLiteral("Active")) return DBusResult(Status::Active);
 | 
			
		||||
	if (wire == QStringLiteral("NeedsAttention")) return DBusResult(Status::NeedsAttention);
 | 
			
		||||
 | 
			
		||||
	return DBusResult<Status::Enum>(QDBusError(
 | 
			
		||||
	    QDBusError::InvalidArgs,
 | 
			
		||||
	    QString("Nonconformant StatusNotifierItem Status: %1").arg(wire)
 | 
			
		||||
	));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DBusResult<Category::Enum> DBusDataTransform<Category::Enum>::fromWire(const QString& wire) {
 | 
			
		||||
	if (wire == "ApplicationStatus") return DBusResult(Category::ApplicationStatus);
 | 
			
		||||
	if (wire == "SystemServices") return DBusResult(Category::SystemServices);
 | 
			
		||||
	if (wire == "Hardware") return DBusResult(Category::Hardware);
 | 
			
		||||
 | 
			
		||||
	return DBusResult<Category::Enum>(QDBusError(
 | 
			
		||||
	    QDBusError::InvalidArgs,
 | 
			
		||||
	    QString("Nonconformant StatusNotifierItem Category: %1").arg(wire)
 | 
			
		||||
	));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace qs::dbus
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@
 | 
			
		|||
#include <qloggingcategory.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qpixmap.h>
 | 
			
		||||
#include <qproperty.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +55,28 @@ enum Enum {
 | 
			
		|||
Q_ENUM_NS(Enum);
 | 
			
		||||
} // namespace Category
 | 
			
		||||
 | 
			
		||||
} // namespace qs::service::sni
 | 
			
		||||
 | 
			
		||||
namespace qs::dbus {
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
struct DBusDataTransform<qs::service::sni::Status::Enum> {
 | 
			
		||||
	using Wire = QString;
 | 
			
		||||
	using Data = qs::service::sni::Status::Enum;
 | 
			
		||||
	static DBusResult<Data> fromWire(const QString& wire);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
struct DBusDataTransform<qs::service::sni::Category::Enum> {
 | 
			
		||||
	using Wire = QString;
 | 
			
		||||
	using Data = qs::service::sni::Category::Enum;
 | 
			
		||||
	static DBusResult<Data> fromWire(const QString& wire);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace qs::dbus
 | 
			
		||||
 | 
			
		||||
namespace qs::service::sni {
 | 
			
		||||
 | 
			
		||||
class StatusNotifierItem;
 | 
			
		||||
 | 
			
		||||
class TrayImageHandle: public QsImageHandle {
 | 
			
		||||
| 
						 | 
				
			
			@ -74,24 +97,26 @@ public:
 | 
			
		|||
class StatusNotifierItem: public QObject {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
 | 
			
		||||
	// clang-format off
 | 
			
		||||
	/// 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 BINDABLE bindableId);
 | 
			
		||||
	/// Text that describes the application.
 | 
			
		||||
	Q_PROPERTY(QString title READ title NOTIFY titleChanged);
 | 
			
		||||
	Q_PROPERTY(qs::service::sni::Status::Enum status READ status NOTIFY statusChanged);
 | 
			
		||||
	Q_PROPERTY(qs::service::sni::Category::Enum category READ category NOTIFY categoryChanged);
 | 
			
		||||
	Q_PROPERTY(QString title READ title NOTIFY titleChanged BINDABLE bindableTitle);
 | 
			
		||||
	Q_PROPERTY(qs::service::sni::Status::Enum status READ status NOTIFY statusChanged BINDABLE bindableStatus);
 | 
			
		||||
	Q_PROPERTY(qs::service::sni::Category::Enum category READ category NOTIFY categoryChanged BINDABLE bindableCategory);
 | 
			
		||||
	/// Icon source string, usable as an Image source.
 | 
			
		||||
	Q_PROPERTY(QString icon READ iconId NOTIFY iconChanged);
 | 
			
		||||
	Q_PROPERTY(QString icon READ icon NOTIFY iconChanged BINDABLE bindableIcon);
 | 
			
		||||
	Q_PROPERTY(QString tooltipTitle READ tooltipTitle NOTIFY tooltipTitleChanged);
 | 
			
		||||
	Q_PROPERTY(QString tooltipDescription READ tooltipDescription NOTIFY tooltipDescriptionChanged);
 | 
			
		||||
	/// If this tray item has an associated menu accessible via @@display() or @@menu.
 | 
			
		||||
	Q_PROPERTY(bool hasMenu READ hasMenu NOTIFY hasMenuChanged);
 | 
			
		||||
	Q_PROPERTY(bool hasMenu READ hasMenu NOTIFY hasMenuChanged BINDABLE bindableHasMenu);
 | 
			
		||||
	/// A handle to the menu associated with this tray item, if any.
 | 
			
		||||
	///
 | 
			
		||||
	/// Can be displayed with @@Quickshell.QsMenuAnchor or @@Quickshell.QsMenuOpener.
 | 
			
		||||
	Q_PROPERTY(qs::dbus::dbusmenu::DBusMenuHandle* menu READ menuHandle NOTIFY hasMenuChanged);
 | 
			
		||||
	/// 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 BINDABLE bindableOnlyMenu);
 | 
			
		||||
	// clang-format on
 | 
			
		||||
	QML_NAMED_ELEMENT(SystemTrayItem);
 | 
			
		||||
	QML_UNCREATABLE("SystemTrayItems can only be acquired from SystemTray");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -100,7 +125,7 @@ public:
 | 
			
		|||
 | 
			
		||||
	[[nodiscard]] bool isValid() const;
 | 
			
		||||
	[[nodiscard]] bool isReady() const;
 | 
			
		||||
	[[nodiscard]] QString iconId() const;
 | 
			
		||||
	QS_BINDABLE_GETTER(QString, bIcon, icon, bindableIcon);
 | 
			
		||||
	[[nodiscard]] QPixmap createPixmap(const QSize& size) const;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] dbus::dbusmenu::DBusMenuHandle* menuHandle();
 | 
			
		||||
| 
						 | 
				
			
			@ -114,21 +139,21 @@ public:
 | 
			
		|||
	/// Display a platform menu at the given location relative to the parent window.
 | 
			
		||||
	void display(QObject* parentWindow, qint32 relativeX, qint32 relativeY);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] QString id() const;
 | 
			
		||||
	[[nodiscard]] QString title() const;
 | 
			
		||||
	[[nodiscard]] Status::Enum status() const;
 | 
			
		||||
	[[nodiscard]] Category::Enum category() const;
 | 
			
		||||
	[[nodiscard]] QString tooltipTitle() const;
 | 
			
		||||
	[[nodiscard]] QString tooltipDescription() const;
 | 
			
		||||
	[[nodiscard]] bool hasMenu() const;
 | 
			
		||||
	[[nodiscard]] bool onlyMenu() const;
 | 
			
		||||
	QS_BINDABLE_GETTER(QString, bId, id, bindableId);
 | 
			
		||||
	QS_BINDABLE_GETTER(QString, bTitle, title, bindableTitle);
 | 
			
		||||
	QS_BINDABLE_GETTER(Status::Enum, bStatus, status, bindableStatus);
 | 
			
		||||
	QS_BINDABLE_GETTER(Category::Enum, bCategory, category, bindableCategory);
 | 
			
		||||
	[[nodiscard]] QString tooltipTitle() const { return this->bTooltip.value().title; };
 | 
			
		||||
	[[nodiscard]] QString tooltipDescription() const { return this->bTooltip.value().description; };
 | 
			
		||||
	QS_BINDABLE_GETTER(bool, bHasMenu, hasMenu, bindableHasMenu);
 | 
			
		||||
	QS_BINDABLE_GETTER(bool, bIsMenu, onlyMenu, bindableOnlyMenu);
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	void iconChanged();
 | 
			
		||||
	void ready();
 | 
			
		||||
 | 
			
		||||
	void idChanged();
 | 
			
		||||
	void titleChanged();
 | 
			
		||||
	void iconChanged();
 | 
			
		||||
	void statusChanged();
 | 
			
		||||
	void categoryChanged();
 | 
			
		||||
	void tooltipTitleChanged();
 | 
			
		||||
| 
						 | 
				
			
			@ -137,13 +162,13 @@ signals:
 | 
			
		|||
	void onlyMenuChanged();
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
	void updateIcon();
 | 
			
		||||
	void onGetAllFinished();
 | 
			
		||||
	void onGetAllFailed() const;
 | 
			
		||||
	void onMenuPathChanged();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void updateMenuState();
 | 
			
		||||
	void updatePixmapIndex();
 | 
			
		||||
	void onMenuPathChanged();
 | 
			
		||||
 | 
			
		||||
	DBusStatusNotifierItem* item = nullptr;
 | 
			
		||||
	TrayImageHandle imageHandle {this};
 | 
			
		||||
| 
						 | 
				
			
			@ -151,28 +176,53 @@ private:
 | 
			
		|||
 | 
			
		||||
	dbus::dbusmenu::DBusMenuHandle mMenuHandle {this};
 | 
			
		||||
 | 
			
		||||
	// bumped to inhibit caching
 | 
			
		||||
	quint32 iconIndex = 0;
 | 
			
		||||
	QString watcherId;
 | 
			
		||||
 | 
			
		||||
	// clang-format off
 | 
			
		||||
	dbus::DBusPropertyGroup properties;
 | 
			
		||||
	dbus::DBusProperty<QString> pId {this->properties, "Id"};
 | 
			
		||||
	dbus::DBusProperty<QString> pTitle {this->properties, "Title"};
 | 
			
		||||
	dbus::DBusProperty<QString> pStatus {this->properties, "Status"};
 | 
			
		||||
	dbus::DBusProperty<QString> pCategory {this->properties, "Category"};
 | 
			
		||||
	dbus::DBusProperty<quint32> pWindowId {this->properties, "WindowId"};
 | 
			
		||||
	dbus::DBusProperty<QString> pIconThemePath {this->properties, "IconThemePath", "", false};
 | 
			
		||||
	dbus::DBusProperty<QString> pIconName {this->properties, "IconName", "", false}; // IconPixmap may be set
 | 
			
		||||
	dbus::DBusProperty<DBusSniIconPixmapList> pIconPixmaps {this->properties, "IconPixmap", {}, false}; // IconName may be set
 | 
			
		||||
	dbus::DBusProperty<QString> pOverlayIconName {this->properties, "OverlayIconName"};
 | 
			
		||||
	dbus::DBusProperty<DBusSniIconPixmapList> pOverlayIconPixmaps {this->properties, "OverlayIconPixmap"};
 | 
			
		||||
	dbus::DBusProperty<QString> pAttentionIconName {this->properties, "AttentionIconName"};
 | 
			
		||||
	dbus::DBusProperty<DBusSniIconPixmapList> pAttentionIconPixmaps {this->properties, "AttentionIconPixmap"};
 | 
			
		||||
	dbus::DBusProperty<QString> pAttentionMovieName {this->properties, "AttentionMovieName", "", false};
 | 
			
		||||
	dbus::DBusProperty<DBusSniTooltip> pTooltip {this->properties, "ToolTip"};
 | 
			
		||||
	dbus::DBusProperty<bool> pIsMenu {this->properties, "ItemIsMenu"};
 | 
			
		||||
	dbus::DBusProperty<QDBusObjectPath> pMenuPath {this->properties, "Menu"};
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, QString, bId, &StatusNotifierItem::idChanged);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, QString, bTitle, &StatusNotifierItem::titleChanged);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, Status::Enum, bStatus, &StatusNotifierItem::statusChanged);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, Category::Enum, bCategory, &StatusNotifierItem::categoryChanged);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, QString, bIconThemePath);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, QString, bIconName);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, QString, bOverlayIconName);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, QString, bAttentionIconName);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, DBusSniIconPixmapList, bIconPixmaps);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, DBusSniIconPixmapList, bOverlayIconPixmaps);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, DBusSniIconPixmapList, bAttentionIconPixmaps);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, QString, bAttentionMovieName);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, DBusSniTooltip, bTooltip);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, bool, bIsMenu);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, QDBusObjectPath, bMenuPath);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, bool, bHasMenu, &StatusNotifierItem::hasMenuChanged);
 | 
			
		||||
 | 
			
		||||
	QS_BINDING_SUBSCRIBE_METHOD(StatusNotifierItem, bIconThemePath, updatePixmapIndex, onValueChanged);
 | 
			
		||||
	QS_BINDING_SUBSCRIBE_METHOD(StatusNotifierItem, bIconName, updatePixmapIndex, onValueChanged);
 | 
			
		||||
	QS_BINDING_SUBSCRIBE_METHOD(StatusNotifierItem, bOverlayIconName, updatePixmapIndex, onValueChanged);
 | 
			
		||||
	QS_BINDING_SUBSCRIBE_METHOD(StatusNotifierItem, bAttentionIconName, updatePixmapIndex, onValueChanged);
 | 
			
		||||
	QS_BINDING_SUBSCRIBE_METHOD(StatusNotifierItem, bIconPixmaps, updatePixmapIndex, onValueChanged);
 | 
			
		||||
	QS_BINDING_SUBSCRIBE_METHOD(StatusNotifierItem, bOverlayIconPixmaps, updatePixmapIndex, onValueChanged);
 | 
			
		||||
	QS_BINDING_SUBSCRIBE_METHOD(StatusNotifierItem, bAttentionIconPixmaps, updatePixmapIndex, onValueChanged);
 | 
			
		||||
	QS_BINDING_SUBSCRIBE_METHOD(StatusNotifierItem, bMenuPath, onMenuPathChanged, onValueChanged);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, quint32, pixmapIndex);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(StatusNotifierItem, QString, bIcon, &StatusNotifierItem::iconChanged);
 | 
			
		||||
 | 
			
		||||
	QS_DBUS_BINDABLE_PROPERTY_GROUP(StatusNotifierItem, properties);
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pId, bId, properties, "Id");
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pTitle, bTitle, properties, "Title");
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pStatus, bStatus, properties, "Status");
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pCategory, bCategory, properties, "Category");
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pIconThemePath, bIconThemePath, properties, "IconThemePath", false);
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pIconName, bIconName, properties, "IconName", false);
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pIconPixmaps, bIconPixmaps, properties, "IconPixmap", false);
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pOverlayIconName, bOverlayIconName, properties, "OverlayIconName");
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pOverlayIconPixmaps, bOverlayIconPixmaps, properties, "OverlayIconPixmap");
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pAttentionIconName, bAttentionIconName, properties, "AttentionIconName");
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pAttentionIconPixmaps, bAttentionIconPixmaps, properties, "AttentionIconPixmap");
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pAttentionMovieName, bAttentionMovieName, properties, "AttentionMovieName", false);
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pTooltip, bTooltip, properties, "ToolTip");
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pIsMenu, bIsMenu, properties, "ItemIsMenu");
 | 
			
		||||
	QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pMenuPath, bMenuPath, properties, "Menu");
 | 
			
		||||
	// clang-format on
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue