#pragma once #include #include #include #include #include #include #include #include #include #include "../../core/imageprovider.hpp" #include "../../dbus/dbusmenu/dbusmenu.hpp" #include "../../dbus/properties.hpp" #include "dbus_item.h" #include "dbus_item_types.hpp" Q_DECLARE_LOGGING_CATEGORY(logStatusNotifierItem); namespace qs::service::sni { ///! Status of a StatusNotifierItem. /// See @@StatusNotifierItem.status. namespace Status { // NOLINT Q_NAMESPACE; QML_ELEMENT; enum Enum : quint8 { /// A passive item does not convey important information and can be considered idle. You may want to hide these. Passive = 0, /// An active item may have information more important than a passive one and you probably do not want to hide it. Active = 1, /// An item that needs attention conveys very important information such as low battery. NeedsAttention = 2, }; Q_ENUM_NS(Enum); } // namespace Status ///! Category of a StatusNotifierItem. /// See @@StatusNotifierItem.category. namespace Category { // NOLINT Q_NAMESPACE; QML_ELEMENT; enum Enum : quint8 { /// Hardware controls like battery indicators or volume control. Hardware = 0, /// System services such as IMEs or disk indexing. SystemServices = 1, /// The fallback category for general applications or anything that does /// not fit into a different category. ApplicationStatus = 2, /// Communication related applications such as instant messengers or email clients. Communications = 3, }; Q_ENUM_NS(Enum); } // namespace Category } // namespace qs::service::sni namespace qs::dbus { template <> struct DBusDataTransform { using Wire = QString; using Data = qs::service::sni::Status::Enum; static DBusResult fromWire(const QString& wire); }; template <> struct DBusDataTransform { using Wire = QString; using Data = qs::service::sni::Category::Enum; static DBusResult fromWire(const QString& wire); }; } // namespace qs::dbus namespace qs::service::sni { class StatusNotifierItem; class TrayImageHandle: public QsImageHandle { public: explicit TrayImageHandle(StatusNotifierItem* item); QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) override; public: StatusNotifierItem* item; }; ///! An item in the system tray. /// 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). /// /// [kde/freedesktop spec]: https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierItem/ 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 BINDABLE bindableId); /// Text that describes the application. 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 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 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 BINDABLE bindableOnlyMenu); // clang-format on QML_NAMED_ELEMENT(SystemTrayItem); QML_UNCREATABLE("SystemTrayItems can only be acquired from SystemTray"); public: explicit StatusNotifierItem(const QString& address, QObject* parent = nullptr); [[nodiscard]] bool isValid() const; [[nodiscard]] bool isReady() const; QS_BINDABLE_GETTER(QString, bIcon, icon, bindableIcon); [[nodiscard]] QPixmap createPixmap(const QSize& size) const; [[nodiscard]] dbus::dbusmenu::DBusMenuHandle* menuHandle(); /// Primary activation action, generally triggered via a left click. Q_INVOKABLE void activate(); /// Secondary activation action, generally triggered via a middle click. Q_INVOKABLE void secondaryActivate(); /// Scroll action, such as changing volume on a mixer. Q_INVOKABLE void scroll(qint32 delta, bool horizontal) const; /// Display a platform menu at the given location relative to the parent window. Q_INVOKABLE void display(QObject* parentWindow, qint32 relativeX, qint32 relativeY); 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 ready(); void idChanged(); void titleChanged(); void iconChanged(); void statusChanged(); void categoryChanged(); void tooltipTitleChanged(); void tooltipDescriptionChanged(); void hasMenuChanged(); void onlyMenuChanged(); private slots: void onGetAllFinished(); void onGetAllFailed() const; private: void updateMenuState(); void updatePixmapIndex(); void onMenuPathChanged(); DBusStatusNotifierItem* item = nullptr; TrayImageHandle imageHandle {this}; bool mReady = false; dbus::dbusmenu::DBusMenuHandle mMenuHandle {this}; QString watcherId; // clang-format off 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", false); 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", false); QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pOverlayIconPixmaps, bOverlayIconPixmaps, properties, "OverlayIconPixmap", false); QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pAttentionIconName, bAttentionIconName, properties, "AttentionIconName", false); QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pAttentionIconPixmaps, bAttentionIconPixmaps, properties, "AttentionIconPixmap", false); 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", false); QS_DBUS_PROPERTY_BINDING(StatusNotifierItem, pMenuPath, bMenuPath, properties, "Menu", false); // clang-format on }; } // namespace qs::service::sni