forked from quickshell/quickshell
		
	dbus/dbusmenu: add DBusMenu support
This commit is contained in:
		
							parent
							
								
									7cc1b54587
								
							
						
					
					
						commit
						61061644a5
					
				
					 13 changed files with 984 additions and 11 deletions
				
			
		| 
						 | 
					@ -8,7 +8,7 @@
 | 
				
			||||||
#include <qpixmap.h>
 | 
					#include <qpixmap.h>
 | 
				
			||||||
#include <qqmlengine.h>
 | 
					#include <qqmlengine.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static QMap<QString, QsImageHandle*> liveImages;
 | 
					static QMap<QString, QsImageHandle*> liveImages; // NOLINT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
QsImageHandle::QsImageHandle(QQmlImageProviderBase::ImageType type, QObject* parent)
 | 
					QsImageHandle::QsImageHandle(QQmlImageProviderBase::ImageType type, QObject* parent)
 | 
				
			||||||
    : QObject(parent)
 | 
					    : QObject(parent)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,4 +18,5 @@ target_include_directories(quickshell-dbus PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
 | 
				
			||||||
target_link_libraries(quickshell-dbus PRIVATE ${QT_DEPS})
 | 
					target_link_libraries(quickshell-dbus PRIVATE ${QT_DEPS})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
qs_pch(quickshell-dbus)
 | 
					qs_pch(quickshell-dbus)
 | 
				
			||||||
#qs_pch(quickshell-dbusplugin)
 | 
					
 | 
				
			||||||
 | 
					add_subdirectory(dbusmenu)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/dbus/dbusmenu/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/dbus/dbusmenu/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					set_source_files_properties(com.canonical.dbusmenu.xml PROPERTIES
 | 
				
			||||||
 | 
						CLASSNAME DBusMenuInterface
 | 
				
			||||||
 | 
						INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/dbus_menu_types.hpp
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					qt_add_dbus_interface(DBUS_INTERFACES
 | 
				
			||||||
 | 
						com.canonical.dbusmenu.xml
 | 
				
			||||||
 | 
						dbus_menu
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					qt_add_library(quickshell-dbusmenu STATIC
 | 
				
			||||||
 | 
						dbus_menu_types.cpp
 | 
				
			||||||
 | 
						dbusmenu.cpp
 | 
				
			||||||
 | 
						${DBUS_INTERFACES}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					qt_add_qml_module(quickshell-dbusmenu URI Quickshell.DBusMenu VERSION 0.1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# dbus headers
 | 
				
			||||||
 | 
					target_include_directories(quickshell-dbusmenu PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					target_link_libraries(quickshell-dbusmenu PRIVATE ${QT_DEPS})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					qs_pch(quickshell-dbusmenu)
 | 
				
			||||||
 | 
					qs_pch(quickshell-dbusmenuplugin)
 | 
				
			||||||
							
								
								
									
										68
									
								
								src/dbus/dbusmenu/com.canonical.dbusmenu.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/dbus/dbusmenu/com.canonical.dbusmenu.xml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					<node>
 | 
				
			||||||
 | 
						<interface name="com.canonical.dbusmenu">
 | 
				
			||||||
 | 
							<property name="Version" type="u" access="read"/>
 | 
				
			||||||
 | 
							<property name="TextDirection" type="s" access="read"/>
 | 
				
			||||||
 | 
							<property name="Status" type="s" access="read"/>
 | 
				
			||||||
 | 
							<property name="IconThemePath" type="as" access="read"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<method name="GetLayout">
 | 
				
			||||||
 | 
								<arg type="i" name="parentId" direction="in"/>
 | 
				
			||||||
 | 
								<arg type="i" name="recursionDepth" direction="in"/>
 | 
				
			||||||
 | 
								<arg type="as" name="propertyNames" direction="in"/>
 | 
				
			||||||
 | 
								<arg type="u" name="revision" direction="out"/>
 | 
				
			||||||
 | 
								<arg type="(ia{sv}av)" name="layout" direction="out"/>
 | 
				
			||||||
 | 
								<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="DBusMenuLayout"/>
 | 
				
			||||||
 | 
							</method>
 | 
				
			||||||
 | 
							<method name="GetGroupProperties">
 | 
				
			||||||
 | 
								<arg type="ai" name="ids" direction="in"/>
 | 
				
			||||||
 | 
								<arg type="as" name="propertyNames" direction="in"/>
 | 
				
			||||||
 | 
								<arg type="a(ia{sv})" name="properties" direction="out"/>
 | 
				
			||||||
 | 
								<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="DBusMenuIdList"/>
 | 
				
			||||||
 | 
								<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="DBusMenuItemPropertiesList"/>
 | 
				
			||||||
 | 
							</method>
 | 
				
			||||||
 | 
							<method name="GetProperty">
 | 
				
			||||||
 | 
								<arg type="i" name="id" direction="in"/>
 | 
				
			||||||
 | 
								<arg type="s" name="name" direction="in"/>
 | 
				
			||||||
 | 
								<arg type="v" name="value" direction="out"/>
 | 
				
			||||||
 | 
							</method>
 | 
				
			||||||
 | 
							<method name="Event">
 | 
				
			||||||
 | 
								<arg type="i" name="id" direction="in"/>
 | 
				
			||||||
 | 
								<arg type="s" name="eventId" direction="in"/>
 | 
				
			||||||
 | 
								<arg type="v" name="data" direction="in"/>
 | 
				
			||||||
 | 
								<arg type="u" name="timestamp" direction="in"/>
 | 
				
			||||||
 | 
							</method>
 | 
				
			||||||
 | 
							<!--<method name="EventGroup">
 | 
				
			||||||
 | 
								<arg type="a(isvu)" name="events" direction="in"/>
 | 
				
			||||||
 | 
								<arg type="ai" name="idErrors" direction="out"/>
 | 
				
			||||||
 | 
								<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="DBusMenuEventList"/>
 | 
				
			||||||
 | 
								<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="DBusMenuIdList"/>
 | 
				
			||||||
 | 
							</method>-->
 | 
				
			||||||
 | 
							<method name="AboutToShow">
 | 
				
			||||||
 | 
								<arg type="i" name="id" direction="in"/>
 | 
				
			||||||
 | 
								<arg type="b" name="needUpdate" direction="out"/>
 | 
				
			||||||
 | 
							</method>
 | 
				
			||||||
 | 
							<method name="AboutToShowGroup">
 | 
				
			||||||
 | 
								<arg type="ai" name="ids" direction="in"/>
 | 
				
			||||||
 | 
								<arg type="ai" name="updatesNeeded" direction="out"/>
 | 
				
			||||||
 | 
								<arg type="ai" name="idErrors" direction="out"/>
 | 
				
			||||||
 | 
								<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="DBusMenuIdList"/>
 | 
				
			||||||
 | 
								<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="DBusMenuIdList"/>
 | 
				
			||||||
 | 
								<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="DBusMenuIdList"/>
 | 
				
			||||||
 | 
							</method>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<signal name="ItemsPropertiesUpdated">
 | 
				
			||||||
 | 
								<arg type="a(ia{sv})" name="updatedProps" direction="out"/>
 | 
				
			||||||
 | 
								<arg type="a(ias)" name="removedProps" direction="out"/>
 | 
				
			||||||
 | 
								<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="DBusMenuItemPropertiesList"/>
 | 
				
			||||||
 | 
								<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="DBusMenuItemPropertyNamesList"/>
 | 
				
			||||||
 | 
							</signal>
 | 
				
			||||||
 | 
							<signal name="LayoutUpdated">
 | 
				
			||||||
 | 
								<arg type="u" name="revision" direction="out"/>
 | 
				
			||||||
 | 
								<arg type="i" name="parent" direction="out"/>
 | 
				
			||||||
 | 
							</signal>
 | 
				
			||||||
 | 
							<signal name="ItemActivationRequested">
 | 
				
			||||||
 | 
								<arg type="i" name="id" direction="out"/>
 | 
				
			||||||
 | 
								<arg type="u" name="timestamp" direction="out"/>
 | 
				
			||||||
 | 
							</signal>
 | 
				
			||||||
 | 
						</interface>
 | 
				
			||||||
 | 
					</node>
 | 
				
			||||||
							
								
								
									
										92
									
								
								src/dbus/dbusmenu/dbus_menu_types.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/dbus/dbusmenu/dbus_menu_types.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,92 @@
 | 
				
			||||||
 | 
					#include "dbus_menu_types.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qdbusargument.h>
 | 
				
			||||||
 | 
					#include <qdbusextratypes.h>
 | 
				
			||||||
 | 
					#include <qdebug.h>
 | 
				
			||||||
 | 
					#include <qmetatype.h>
 | 
				
			||||||
 | 
					#include <qvariant.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const QDBusArgument& operator>>(const QDBusArgument& argument, DBusMenuLayout& layout) {
 | 
				
			||||||
 | 
						layout.children.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						argument.beginStructure();
 | 
				
			||||||
 | 
						argument >> layout.id;
 | 
				
			||||||
 | 
						argument >> layout.properties;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						argument.beginArray();
 | 
				
			||||||
 | 
						while (!argument.atEnd()) {
 | 
				
			||||||
 | 
							auto childArgument = qdbus_cast<QDBusVariant>(argument).variant().value<QDBusArgument>();
 | 
				
			||||||
 | 
							auto child = qdbus_cast<DBusMenuLayout>(childArgument);
 | 
				
			||||||
 | 
							layout.children.append(child);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						argument.endArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						argument.endStructure();
 | 
				
			||||||
 | 
						return argument;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const QDBusArgument& operator<<(QDBusArgument& argument, const DBusMenuLayout& layout) {
 | 
				
			||||||
 | 
						argument.beginStructure();
 | 
				
			||||||
 | 
						argument << layout.id;
 | 
				
			||||||
 | 
						argument << layout.properties;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						argument.beginArray(qMetaTypeId<QDBusVariant>());
 | 
				
			||||||
 | 
						for (const auto& child: layout.children) {
 | 
				
			||||||
 | 
							argument << QDBusVariant(QVariant::fromValue(child));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						argument.endArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						argument.endStructure();
 | 
				
			||||||
 | 
						return argument;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const QDBusArgument& operator>>(const QDBusArgument& argument, DBusMenuItemProperties& item) {
 | 
				
			||||||
 | 
						argument.beginStructure();
 | 
				
			||||||
 | 
						argument >> item.id;
 | 
				
			||||||
 | 
						argument >> item.properties;
 | 
				
			||||||
 | 
						argument.endStructure();
 | 
				
			||||||
 | 
						return argument;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const QDBusArgument& operator<<(QDBusArgument& argument, const DBusMenuItemProperties& item) {
 | 
				
			||||||
 | 
						argument.beginStructure();
 | 
				
			||||||
 | 
						argument << item.id;
 | 
				
			||||||
 | 
						argument << item.properties;
 | 
				
			||||||
 | 
						argument.endStructure();
 | 
				
			||||||
 | 
						return argument;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const QDBusArgument& operator>>(const QDBusArgument& argument, DBusMenuItemPropertyNames& names) {
 | 
				
			||||||
 | 
						argument.beginStructure();
 | 
				
			||||||
 | 
						argument >> names.id;
 | 
				
			||||||
 | 
						argument >> names.properties;
 | 
				
			||||||
 | 
						argument.endStructure();
 | 
				
			||||||
 | 
						return argument;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const QDBusArgument& operator<<(QDBusArgument& argument, const DBusMenuItemPropertyNames& names) {
 | 
				
			||||||
 | 
						argument.beginStructure();
 | 
				
			||||||
 | 
						argument << names.id;
 | 
				
			||||||
 | 
						argument << names.properties;
 | 
				
			||||||
 | 
						argument.endStructure();
 | 
				
			||||||
 | 
						return argument;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDebug operator<<(QDebug debug, const DBusMenuLayout& layout) {
 | 
				
			||||||
 | 
						debug.nospace() << "DBusMenuLayout(id=" << layout.id << ", properties=" << layout.properties
 | 
				
			||||||
 | 
						                << ", children=" << layout.children << ")";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return debug;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDebug operator<<(QDebug debug, const DBusMenuItemProperties& item) {
 | 
				
			||||||
 | 
						debug.nospace() << "DBusMenuItemProperties(id=" << item.id << ", properties=" << item.properties
 | 
				
			||||||
 | 
						                << ")";
 | 
				
			||||||
 | 
						return debug;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDebug operator<<(QDebug debug, const DBusMenuItemPropertyNames& names) {
 | 
				
			||||||
 | 
						debug.nospace() << "DBusMenuItemPropertyNames(id=" << names.id
 | 
				
			||||||
 | 
						                << ", properties=" << names.properties << ")";
 | 
				
			||||||
 | 
						return debug;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										47
									
								
								src/dbus/dbusmenu/dbus_menu_types.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/dbus/dbusmenu/dbus_menu_types.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,47 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qcontainerfwd.h>
 | 
				
			||||||
 | 
					#include <qdbusargument.h>
 | 
				
			||||||
 | 
					#include <qdebug.h>
 | 
				
			||||||
 | 
					#include <qlist.h>
 | 
				
			||||||
 | 
					#include <qtypes.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct DBusMenuLayout {
 | 
				
			||||||
 | 
						qint32 id = 0;
 | 
				
			||||||
 | 
						QVariantMap properties;
 | 
				
			||||||
 | 
						QList<DBusMenuLayout> children;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using DBusMenuIdList = QList<qint32>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct DBusMenuItemProperties {
 | 
				
			||||||
 | 
						qint32 id = 0;
 | 
				
			||||||
 | 
						QVariantMap properties;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using DBusMenuItemPropertiesList = QList<DBusMenuItemProperties>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct DBusMenuItemPropertyNames {
 | 
				
			||||||
 | 
						qint32 id = 0;
 | 
				
			||||||
 | 
						QStringList properties;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using DBusMenuItemPropertyNamesList = QList<DBusMenuItemPropertyNames>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const QDBusArgument& operator>>(const QDBusArgument& argument, DBusMenuLayout& layout);
 | 
				
			||||||
 | 
					const QDBusArgument& operator<<(QDBusArgument& argument, const DBusMenuLayout& layout);
 | 
				
			||||||
 | 
					const QDBusArgument& operator>>(const QDBusArgument& argument, DBusMenuItemProperties& item);
 | 
				
			||||||
 | 
					const QDBusArgument& operator<<(QDBusArgument& argument, const DBusMenuItemProperties& item);
 | 
				
			||||||
 | 
					const QDBusArgument& operator>>(const QDBusArgument& argument, DBusMenuItemPropertyNames& names);
 | 
				
			||||||
 | 
					const QDBusArgument& operator<<(QDBusArgument& argument, const DBusMenuItemPropertyNames& names);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDebug operator<<(QDebug debug, const DBusMenuLayout& layout);
 | 
				
			||||||
 | 
					QDebug operator<<(QDebug debug, const DBusMenuItemProperties& item);
 | 
				
			||||||
 | 
					QDebug operator<<(QDebug debug, const DBusMenuItemPropertyNames& names);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_DECLARE_METATYPE(DBusMenuLayout);
 | 
				
			||||||
 | 
					Q_DECLARE_METATYPE(DBusMenuIdList);
 | 
				
			||||||
 | 
					Q_DECLARE_METATYPE(DBusMenuItemProperties);
 | 
				
			||||||
 | 
					Q_DECLARE_METATYPE(DBusMenuItemPropertiesList);
 | 
				
			||||||
 | 
					Q_DECLARE_METATYPE(DBusMenuItemPropertyNames);
 | 
				
			||||||
 | 
					Q_DECLARE_METATYPE(DBusMenuItemPropertyNamesList);
 | 
				
			||||||
							
								
								
									
										525
									
								
								src/dbus/dbusmenu/dbusmenu.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										525
									
								
								src/dbus/dbusmenu/dbusmenu.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,525 @@
 | 
				
			||||||
 | 
					#include "dbusmenu.hpp"
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qbytearray.h>
 | 
				
			||||||
 | 
					#include <qcontainerfwd.h>
 | 
				
			||||||
 | 
					#include <qdatetime.h>
 | 
				
			||||||
 | 
					#include <qdbusconnection.h>
 | 
				
			||||||
 | 
					#include <qdbusextratypes.h>
 | 
				
			||||||
 | 
					#include <qdbusmetatype.h>
 | 
				
			||||||
 | 
					#include <qdbuspendingcall.h>
 | 
				
			||||||
 | 
					#include <qdbuspendingreply.h>
 | 
				
			||||||
 | 
					#include <qdebug.h>
 | 
				
			||||||
 | 
					#include <qimage.h>
 | 
				
			||||||
 | 
					#include <qlogging.h>
 | 
				
			||||||
 | 
					#include <qloggingcategory.h>
 | 
				
			||||||
 | 
					#include <qnamespace.h>
 | 
				
			||||||
 | 
					#include <qobject.h>
 | 
				
			||||||
 | 
					#include <qqmllist.h>
 | 
				
			||||||
 | 
					#include <qtmetamacros.h>
 | 
				
			||||||
 | 
					#include <qtypes.h>
 | 
				
			||||||
 | 
					#include <qvariant.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../../core/iconimageprovider.hpp"
 | 
				
			||||||
 | 
					#include "../../dbus/properties.hpp"
 | 
				
			||||||
 | 
					#include "dbus_menu.h"
 | 
				
			||||||
 | 
					#include "dbus_menu_types.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_LOGGING_CATEGORY(logDbusMenu, "quickshell.dbus.dbusmenu", QtWarningMsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace qs::dbus::dbusmenu {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DBusMenuItem::DBusMenuItem(qint32 id, DBusMenu* menu, DBusMenuItem* parentMenu)
 | 
				
			||||||
 | 
					    : QObject(menu)
 | 
				
			||||||
 | 
					    , id(id)
 | 
				
			||||||
 | 
					    , menu(menu)
 | 
				
			||||||
 | 
					    , parentMenu(parentMenu) {
 | 
				
			||||||
 | 
						QObject::connect(
 | 
				
			||||||
 | 
						    &this->menu->iconThemePath,
 | 
				
			||||||
 | 
						    &AbstractDBusProperty::changed,
 | 
				
			||||||
 | 
						    this,
 | 
				
			||||||
 | 
						    &DBusMenuItem::iconChanged
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusMenuItem::click() {
 | 
				
			||||||
 | 
						if (this->displayChildren) {
 | 
				
			||||||
 | 
							this->setShowChildren(!this->mShowChildren);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							this->menu->sendEvent(this->id, "clicked");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusMenuItem::hover() const { this->menu->sendEvent(this->id, "hovered"); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DBusMenu* DBusMenuItem::menuHandle() const { return this->menu; }
 | 
				
			||||||
 | 
					QString DBusMenuItem::label() const { return this->mLabel; }
 | 
				
			||||||
 | 
					QString DBusMenuItem::cleanLabel() const { return this->mCleanLabel; }
 | 
				
			||||||
 | 
					bool DBusMenuItem::enabled() const { return this->mEnabled; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QString DBusMenuItem::icon() const {
 | 
				
			||||||
 | 
						if (!this->iconName.isEmpty()) {
 | 
				
			||||||
 | 
							return IconImageProvider::requestString(
 | 
				
			||||||
 | 
							    this->iconName,
 | 
				
			||||||
 | 
							    this->menu->iconThemePath.get().join(':')
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						} else if (this->image != nullptr) {
 | 
				
			||||||
 | 
							return this->image->url();
 | 
				
			||||||
 | 
						} else return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ToggleButtonType::Enum DBusMenuItem::toggleType() const { return this->mToggleType; };
 | 
				
			||||||
 | 
					Qt::CheckState DBusMenuItem::checkState() const { return this->mCheckState; }
 | 
				
			||||||
 | 
					bool DBusMenuItem::isSeparator() const { return this->mSeparator; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool DBusMenuItem::isShowingChildren() const { return this->mShowChildren && this->childrenLoaded; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusMenuItem::setShowChildren(bool showChildren) {
 | 
				
			||||||
 | 
						if (showChildren == this->mShowChildren) return;
 | 
				
			||||||
 | 
						this->mShowChildren = showChildren;
 | 
				
			||||||
 | 
						this->childrenLoaded = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (showChildren) {
 | 
				
			||||||
 | 
							this->menu->prepareToShow(this->id, true);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							this->menu->sendEvent(this->id, "closed");
 | 
				
			||||||
 | 
							emit this->showingChildrenChanged();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!this->mChildren.isEmpty()) {
 | 
				
			||||||
 | 
								for (auto child: this->mChildren) {
 | 
				
			||||||
 | 
									this->menu->removeRecursive(child);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this->mChildren.clear();
 | 
				
			||||||
 | 
								this->onChildrenUpdated();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool DBusMenuItem::hasChildren() const { return this->displayChildren; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QQmlListProperty<DBusMenuItem> DBusMenuItem::children() {
 | 
				
			||||||
 | 
						return QQmlListProperty<DBusMenuItem>(
 | 
				
			||||||
 | 
						    this,
 | 
				
			||||||
 | 
						    nullptr,
 | 
				
			||||||
 | 
						    &DBusMenuItem::childrenCount,
 | 
				
			||||||
 | 
						    &DBusMenuItem::childAt
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					qsizetype DBusMenuItem::childrenCount(QQmlListProperty<DBusMenuItem>* property) {
 | 
				
			||||||
 | 
						return reinterpret_cast<DBusMenuItem*>(property->object)->enabledChildren.count(); // NOLINT
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DBusMenuItem* DBusMenuItem::childAt(QQmlListProperty<DBusMenuItem>* property, qsizetype index) {
 | 
				
			||||||
 | 
						auto* item = reinterpret_cast<DBusMenuItem*>(property->object); // NOLINT
 | 
				
			||||||
 | 
						return item->menu->items.value(item->enabledChildren.at(index));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusMenuItem::updateProperties(const QVariantMap& properties, const QStringList& removed) {
 | 
				
			||||||
 | 
						// Some programs appear to think sending an empty map does not mean "reset everything"
 | 
				
			||||||
 | 
						// and instead means "do nothing". oh well...
 | 
				
			||||||
 | 
						if (properties.isEmpty() && removed.isEmpty()) {
 | 
				
			||||||
 | 
							qCDebug(logDbusMenu) << "Ignoring empty property update for" << this;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto originalLabel = this->mLabel;
 | 
				
			||||||
 | 
						//auto originalMnemonic = this->mnemonic;
 | 
				
			||||||
 | 
						auto originalEnabled = this->mEnabled;
 | 
				
			||||||
 | 
						auto originalVisible = this->visible;
 | 
				
			||||||
 | 
						auto originalIconName = this->iconName;
 | 
				
			||||||
 | 
						auto* originalImage = this->image;
 | 
				
			||||||
 | 
						auto originalIsSeparator = this->mSeparator;
 | 
				
			||||||
 | 
						auto originalToggleType = this->mToggleType;
 | 
				
			||||||
 | 
						auto originalToggleState = this->mCheckState;
 | 
				
			||||||
 | 
						auto originalDisplayChildren = this->displayChildren;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto label = properties.value("label");
 | 
				
			||||||
 | 
						if (label.canConvert<QString>()) {
 | 
				
			||||||
 | 
							auto text = label.value<QString>();
 | 
				
			||||||
 | 
							this->mLabel = text;
 | 
				
			||||||
 | 
							this->mCleanLabel = text;
 | 
				
			||||||
 | 
							//this->mnemonic = QChar();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (auto i = 0; i < this->mLabel.length() - 1;) {
 | 
				
			||||||
 | 
								if (this->mLabel.at(i) == '_') {
 | 
				
			||||||
 | 
									//if (this->mnemonic == QChar()) this->mnemonic = this->mLabel.at(i + 1);
 | 
				
			||||||
 | 
									this->mLabel.remove(i, 1);
 | 
				
			||||||
 | 
									this->mLabel.insert(i + 1, "</u>");
 | 
				
			||||||
 | 
									this->mLabel.insert(i, "<u>");
 | 
				
			||||||
 | 
									i += 8;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									i++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (auto i = 0; i < this->mCleanLabel.length() - 1; i++) {
 | 
				
			||||||
 | 
								if (this->mCleanLabel.at(i) == '_') {
 | 
				
			||||||
 | 
									this->mCleanLabel.remove(i, 1);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (removed.isEmpty() || removed.contains("label")) {
 | 
				
			||||||
 | 
							this->mLabel = "";
 | 
				
			||||||
 | 
							//this->mnemonic = QChar();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto enabled = properties.value("enabled");
 | 
				
			||||||
 | 
						if (enabled.canConvert<bool>()) {
 | 
				
			||||||
 | 
							this->mEnabled = enabled.value<bool>();
 | 
				
			||||||
 | 
						} else if (removed.isEmpty() || removed.contains("enabled")) {
 | 
				
			||||||
 | 
							this->mEnabled = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto visible = properties.value("visible");
 | 
				
			||||||
 | 
						if (visible.canConvert<bool>()) {
 | 
				
			||||||
 | 
							this->visible = visible.value<bool>();
 | 
				
			||||||
 | 
						} else if (removed.isEmpty() || removed.contains("visible")) {
 | 
				
			||||||
 | 
							this->visible = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto iconName = properties.value("icon-name");
 | 
				
			||||||
 | 
						if (iconName.canConvert<QString>()) {
 | 
				
			||||||
 | 
							this->iconName = iconName.value<QString>();
 | 
				
			||||||
 | 
						} else if (removed.isEmpty() || removed.contains("icon-name")) {
 | 
				
			||||||
 | 
							this->iconName = "";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto iconData = properties.value("icon-data");
 | 
				
			||||||
 | 
						if (iconData.canConvert<QByteArray>()) {
 | 
				
			||||||
 | 
							auto data = iconData.value<QByteArray>();
 | 
				
			||||||
 | 
							if (data.isEmpty()) {
 | 
				
			||||||
 | 
								this->image = nullptr;
 | 
				
			||||||
 | 
							} else if (this->image == nullptr || this->image->data != data) {
 | 
				
			||||||
 | 
								this->image = new DBusMenuPngImage(data, this);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (removed.isEmpty() || removed.contains("icon-data")) {
 | 
				
			||||||
 | 
							this->image = nullptr;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto type = properties.value("type");
 | 
				
			||||||
 | 
						if (type.canConvert<QString>()) {
 | 
				
			||||||
 | 
							this->mSeparator = type.value<QString>() == "separator";
 | 
				
			||||||
 | 
						} else if (removed.isEmpty() || removed.contains("type")) {
 | 
				
			||||||
 | 
							this->mSeparator = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto toggleType = properties.value("toggle-type");
 | 
				
			||||||
 | 
						if (toggleType.canConvert<QString>()) {
 | 
				
			||||||
 | 
							auto toggleTypeStr = toggleType.value<QString>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (toggleTypeStr == "") this->mToggleType = ToggleButtonType::None;
 | 
				
			||||||
 | 
							else if (toggleTypeStr == "checkmark") this->mToggleType = ToggleButtonType::CheckBox;
 | 
				
			||||||
 | 
							else if (toggleTypeStr == "radio") this->mToggleType = ToggleButtonType::RadioButton;
 | 
				
			||||||
 | 
							else {
 | 
				
			||||||
 | 
								qCWarning(logDbusMenu) << "Unrecognized toggle type" << toggleTypeStr << "for" << this;
 | 
				
			||||||
 | 
								this->mToggleType = ToggleButtonType::None;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (removed.isEmpty() || removed.contains("toggle-type")) {
 | 
				
			||||||
 | 
							this->mToggleType = ToggleButtonType::None;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto toggleState = properties.value("toggle-state");
 | 
				
			||||||
 | 
						if (toggleState.canConvert<qint32>()) {
 | 
				
			||||||
 | 
							auto toggleStateInt = toggleState.value<qint32>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (toggleStateInt == 0) this->mCheckState = Qt::Unchecked;
 | 
				
			||||||
 | 
							else if (toggleStateInt == 1) this->mCheckState = Qt::Checked;
 | 
				
			||||||
 | 
							else this->mCheckState = Qt::PartiallyChecked;
 | 
				
			||||||
 | 
						} else if (removed.isEmpty() || removed.contains("toggle-state")) {
 | 
				
			||||||
 | 
							this->mCheckState = Qt::PartiallyChecked;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto childrenDisplay = properties.value("children-display");
 | 
				
			||||||
 | 
						if (childrenDisplay.canConvert<QString>()) {
 | 
				
			||||||
 | 
							auto childrenDisplayStr = childrenDisplay.value<QString>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (childrenDisplayStr == "") this->displayChildren = false;
 | 
				
			||||||
 | 
							else if (childrenDisplayStr == "submenu") this->displayChildren = true;
 | 
				
			||||||
 | 
							else {
 | 
				
			||||||
 | 
								qCWarning(logDbusMenu) << "Unrecognized children-display mode" << childrenDisplayStr << "for"
 | 
				
			||||||
 | 
								                       << this;
 | 
				
			||||||
 | 
								this->displayChildren = false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (removed.isEmpty() || removed.contains("children-display")) {
 | 
				
			||||||
 | 
							this->displayChildren = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (this->mLabel != originalLabel) emit this->labelChanged();
 | 
				
			||||||
 | 
						//if (this->mnemonic != originalMnemonic) emit this->labelChanged();
 | 
				
			||||||
 | 
						if (this->mEnabled != originalEnabled) emit this->enabledChanged();
 | 
				
			||||||
 | 
						if (this->visible != originalVisible && this->parentMenu != nullptr)
 | 
				
			||||||
 | 
							this->parentMenu->onChildrenUpdated();
 | 
				
			||||||
 | 
						if (this->mToggleType != originalToggleType) emit this->toggleTypeChanged();
 | 
				
			||||||
 | 
						if (this->mCheckState != originalToggleState) emit this->checkStateChanged();
 | 
				
			||||||
 | 
						if (this->mSeparator != originalIsSeparator) emit this->separatorChanged();
 | 
				
			||||||
 | 
						if (this->displayChildren != originalDisplayChildren) emit this->hasChildrenChanged();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (this->iconName != originalIconName || this->image != originalImage) {
 | 
				
			||||||
 | 
							if (this->image != originalImage) {
 | 
				
			||||||
 | 
								delete originalImage;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							emit this->iconChanged();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qCDebug(logDbusMenu).nospace() << "Updated properties of " << this << " { label=" << this->mLabel
 | 
				
			||||||
 | 
						                               << ", enabled=" << this->mEnabled << ", visible=" << this->visible
 | 
				
			||||||
 | 
						                               << ", iconName=" << this->iconName << ", iconData=" << this->image
 | 
				
			||||||
 | 
						                               << ", separator=" << this->mSeparator
 | 
				
			||||||
 | 
						                               << ", toggleType=" << this->mToggleType
 | 
				
			||||||
 | 
						                               << ", toggleState=" << this->mCheckState
 | 
				
			||||||
 | 
						                               << ", displayChildren=" << this->displayChildren << " }";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusMenuItem::onChildrenUpdated() {
 | 
				
			||||||
 | 
						this->enabledChildren.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (auto child: this->mChildren) {
 | 
				
			||||||
 | 
							auto* item = this->menu->items.value(child);
 | 
				
			||||||
 | 
							if (item->visible) this->enabledChildren.push_back(child);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						emit this->childrenChanged();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDebug operator<<(QDebug debug, DBusMenuItem* item) {
 | 
				
			||||||
 | 
						if (item == nullptr) {
 | 
				
			||||||
 | 
							debug << "DBusMenuItem(nullptr)";
 | 
				
			||||||
 | 
							return debug;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto saver = QDebugStateSaver(debug);
 | 
				
			||||||
 | 
						debug.nospace() << "DBusMenuItem(" << static_cast<void*>(item) << ", id=" << item->id
 | 
				
			||||||
 | 
						                << ", label=" << item->mLabel << ", menu=" << item->menu << ")";
 | 
				
			||||||
 | 
						return debug;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDebug operator<<(QDebug debug, const ToggleButtonType::Enum& toggleType) {
 | 
				
			||||||
 | 
						auto saver = QDebugStateSaver(debug);
 | 
				
			||||||
 | 
						debug.nospace() << "ToggleType::";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (toggleType) {
 | 
				
			||||||
 | 
						case ToggleButtonType::None: debug << "None"; break;
 | 
				
			||||||
 | 
						case ToggleButtonType::CheckBox: debug << "Checkbox"; break;
 | 
				
			||||||
 | 
						case ToggleButtonType::RadioButton: debug << "Radiobutton"; break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return debug;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DBusMenu::DBusMenu(const QString& service, const QString& path, QObject* parent): QObject(parent) {
 | 
				
			||||||
 | 
						qDBusRegisterMetaType<DBusMenuLayout>();
 | 
				
			||||||
 | 
						qDBusRegisterMetaType<DBusMenuIdList>();
 | 
				
			||||||
 | 
						qDBusRegisterMetaType<DBusMenuItemProperties>();
 | 
				
			||||||
 | 
						qDBusRegisterMetaType<DBusMenuItemPropertiesList>();
 | 
				
			||||||
 | 
						qDBusRegisterMetaType<DBusMenuItemPropertyNames>();
 | 
				
			||||||
 | 
						qDBusRegisterMetaType<DBusMenuItemPropertyNamesList>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this->interface = new DBusMenuInterface(service, path, QDBusConnection::sessionBus(), this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!this->interface->isValid()) {
 | 
				
			||||||
 | 
							qCWarning(logDbusMenu).noquote() << "Cannot create DBusMenu for" << service << "at" << path;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QObject::connect(
 | 
				
			||||||
 | 
						    this->interface,
 | 
				
			||||||
 | 
						    &DBusMenuInterface::LayoutUpdated,
 | 
				
			||||||
 | 
						    this,
 | 
				
			||||||
 | 
						    &DBusMenu::onLayoutUpdated
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this->properties.setInterface(this->interface);
 | 
				
			||||||
 | 
						this->properties.updateAllViaGetAll();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusMenu::prepareToShow(qint32 item, bool sendOpened) {
 | 
				
			||||||
 | 
						auto pending = this->interface->AboutToShow(item);
 | 
				
			||||||
 | 
						auto* call = new QDBusPendingCallWatcher(pending, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto responseCallback = [this, item, sendOpened](QDBusPendingCallWatcher* call) {
 | 
				
			||||||
 | 
							const QDBusPendingReply<bool> reply = *call;
 | 
				
			||||||
 | 
							if (reply.isError()) {
 | 
				
			||||||
 | 
								qCWarning(logDbusMenu) << "Error in AboutToShow, but showing anyway for menu" << item << "of"
 | 
				
			||||||
 | 
								                       << this << reply.error();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this->updateLayout(item, 1);
 | 
				
			||||||
 | 
							if (sendOpened) this->sendEvent(item, "opened");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							delete call;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusMenu::updateLayout(qint32 parent, qint32 depth) {
 | 
				
			||||||
 | 
						auto pending = this->interface->GetLayout(parent, depth, QStringList());
 | 
				
			||||||
 | 
						auto* call = new QDBusPendingCallWatcher(pending, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto responseCallback = [this, parent, depth](QDBusPendingCallWatcher* call) {
 | 
				
			||||||
 | 
							const QDBusPendingReply<uint, DBusMenuLayout> reply = *call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (reply.isError()) {
 | 
				
			||||||
 | 
								qCWarning(logDbusMenu) << "Error updating layout for menu" << parent << "of" << this
 | 
				
			||||||
 | 
								                       << reply.error();
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								auto layout = reply.argumentAt<1>();
 | 
				
			||||||
 | 
								this->updateLayoutRecursive(layout, this->items.value(parent), depth);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							delete call;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusMenu::updateLayoutRecursive(
 | 
				
			||||||
 | 
					    const DBusMenuLayout& layout,
 | 
				
			||||||
 | 
					    DBusMenuItem* parent,
 | 
				
			||||||
 | 
					    qint32 depth
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						auto* item = this->items.value(layout.id);
 | 
				
			||||||
 | 
						if (item == nullptr) {
 | 
				
			||||||
 | 
							// there is an actual nullptr in the map and not no entry
 | 
				
			||||||
 | 
							if (this->items.contains(layout.id)) {
 | 
				
			||||||
 | 
								item = new DBusMenuItem(layout.id, this, parent);
 | 
				
			||||||
 | 
								this->items.insert(layout.id, item);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (item == nullptr) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qCDebug(logDbusMenu) << "Updating layout recursively for" << this << "menu" << layout.id;
 | 
				
			||||||
 | 
						item->updateProperties(layout.properties);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (depth != 0) {
 | 
				
			||||||
 | 
							auto childrenChanged = false;
 | 
				
			||||||
 | 
							auto iter = item->mChildren.begin();
 | 
				
			||||||
 | 
							while (iter != item->mChildren.end()) {
 | 
				
			||||||
 | 
								auto existing = std::find_if(
 | 
				
			||||||
 | 
								    layout.children.begin(),
 | 
				
			||||||
 | 
								    layout.children.end(),
 | 
				
			||||||
 | 
								    [&](const DBusMenuLayout& layout) { return layout.id == *iter; }
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (existing == layout.children.end()) {
 | 
				
			||||||
 | 
									qCDebug(logDbusMenu) << "Removing missing layout item" << this->items.value(*iter) << "from"
 | 
				
			||||||
 | 
									                     << item;
 | 
				
			||||||
 | 
									this->removeRecursive(*iter);
 | 
				
			||||||
 | 
									iter = item->mChildren.erase(iter);
 | 
				
			||||||
 | 
									childrenChanged = true;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									iter++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (const auto& child: layout.children) {
 | 
				
			||||||
 | 
								if (item->mShowChildren && !item->mChildren.contains(child.id)) {
 | 
				
			||||||
 | 
									qCDebug(logDbusMenu) << "Creating new layout item" << child.id << "in" << item;
 | 
				
			||||||
 | 
									item->mChildren.push_back(child.id);
 | 
				
			||||||
 | 
									this->items.insert(child.id, nullptr);
 | 
				
			||||||
 | 
									childrenChanged = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this->updateLayoutRecursive(child, item, depth - 1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (childrenChanged) item->onChildrenUpdated();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (item->mShowChildren && !item->childrenLoaded) {
 | 
				
			||||||
 | 
							item->childrenLoaded = true;
 | 
				
			||||||
 | 
							emit item->showingChildrenChanged();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusMenu::removeRecursive(qint32 id) {
 | 
				
			||||||
 | 
						auto* item = this->items.value(id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (item != nullptr) {
 | 
				
			||||||
 | 
							for (auto child: item->mChildren) {
 | 
				
			||||||
 | 
								this->removeRecursive(child);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this->items.remove(id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (item != nullptr) {
 | 
				
			||||||
 | 
							item->deleteLater();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusMenu::sendEvent(qint32 item, const QString& event) {
 | 
				
			||||||
 | 
						qCDebug(logDbusMenu) << "Sending event" << event << "to menu" << item << "of" << this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto pending =
 | 
				
			||||||
 | 
						    this->interface->Event(item, event, QDBusVariant(0), QDateTime::currentSecsSinceEpoch());
 | 
				
			||||||
 | 
						auto* call = new QDBusPendingCallWatcher(pending, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto responseCallback = [this, item, event](QDBusPendingCallWatcher* call) {
 | 
				
			||||||
 | 
							const QDBusPendingReply<> reply = *call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (reply.isError()) {
 | 
				
			||||||
 | 
								qCWarning(logDbusMenu) << "Error sending event" << event << "to" << item << "of" << this
 | 
				
			||||||
 | 
								                       << reply.error();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							delete call;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DBusMenuItem* DBusMenu::menu() { return &this->rootItem; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusMenu::onLayoutUpdated(quint32 /*unused*/, qint32 parent) {
 | 
				
			||||||
 | 
						// note: spec says this is recursive
 | 
				
			||||||
 | 
						this->updateLayout(parent, -1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusMenu::onItemPropertiesUpdated( // NOLINT
 | 
				
			||||||
 | 
					    const DBusMenuItemPropertiesList& updatedProps,
 | 
				
			||||||
 | 
					    const DBusMenuItemPropertyNamesList& removedProps
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						for (const auto& propset: updatedProps) {
 | 
				
			||||||
 | 
							auto* item = this->items.value(propset.id);
 | 
				
			||||||
 | 
							if (item != nullptr) {
 | 
				
			||||||
 | 
								item->updateProperties(propset.properties);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (const auto& propset: removedProps) {
 | 
				
			||||||
 | 
							auto* item = this->items.value(propset.id);
 | 
				
			||||||
 | 
							if (item != nullptr) {
 | 
				
			||||||
 | 
								item->updateProperties({}, propset.properties);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDebug operator<<(QDebug debug, DBusMenu* menu) {
 | 
				
			||||||
 | 
						if (menu == nullptr) {
 | 
				
			||||||
 | 
							debug << "DBusMenu(nullptr)";
 | 
				
			||||||
 | 
							return debug;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto saver = QDebugStateSaver(debug);
 | 
				
			||||||
 | 
						debug.nospace() << "DBusMenu(" << static_cast<void*>(menu) << ", " << menu->properties.toString()
 | 
				
			||||||
 | 
						                << ")";
 | 
				
			||||||
 | 
						return debug;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QImage
 | 
				
			||||||
 | 
					DBusMenuPngImage::requestImage(const QString& /*unused*/, QSize* size, const QSize& /*unused*/) {
 | 
				
			||||||
 | 
						auto image = QImage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!image.loadFromData(this->data, "PNG")) {
 | 
				
			||||||
 | 
							qCWarning(logDbusMenu) << "Failed to load dbusmenu item png";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (size != nullptr) *size = image.size();
 | 
				
			||||||
 | 
						return image;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace qs::dbus::dbusmenu
 | 
				
			||||||
							
								
								
									
										176
									
								
								src/dbus/dbusmenu/dbusmenu.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/dbus/dbusmenu/dbusmenu.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,176 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qbytearray.h>
 | 
				
			||||||
 | 
					#include <qcontainerfwd.h>
 | 
				
			||||||
 | 
					#include <qdebug.h>
 | 
				
			||||||
 | 
					#include <qhash.h>
 | 
				
			||||||
 | 
					#include <qloggingcategory.h>
 | 
				
			||||||
 | 
					#include <qnamespace.h>
 | 
				
			||||||
 | 
					#include <qobject.h>
 | 
				
			||||||
 | 
					#include <qqmlintegration.h>
 | 
				
			||||||
 | 
					#include <qqmllist.h>
 | 
				
			||||||
 | 
					#include <qquickimageprovider.h>
 | 
				
			||||||
 | 
					#include <qtmetamacros.h>
 | 
				
			||||||
 | 
					#include <qtypes.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../../core/imageprovider.hpp"
 | 
				
			||||||
 | 
					#include "../properties.hpp"
 | 
				
			||||||
 | 
					#include "dbus_menu_types.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_DECLARE_LOGGING_CATEGORY(logDbusMenu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ToggleButtonType { // NOLINT
 | 
				
			||||||
 | 
					Q_NAMESPACE;
 | 
				
			||||||
 | 
					QML_ELEMENT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum Enum {
 | 
				
			||||||
 | 
						None = 0,
 | 
				
			||||||
 | 
						CheckBox = 1,
 | 
				
			||||||
 | 
						RadioButton = 2,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					Q_ENUM_NS(Enum);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace ToggleButtonType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DBusMenuInterface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace qs::dbus::dbusmenu {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDebug operator<<(QDebug debug, const ToggleButtonType::Enum& toggleType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DBusMenu;
 | 
				
			||||||
 | 
					class DBusMenuPngImage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DBusMenuItem: public QObject {
 | 
				
			||||||
 | 
						Q_OBJECT;
 | 
				
			||||||
 | 
						// clang-format off
 | 
				
			||||||
 | 
						Q_PROPERTY(DBusMenu* menuHandle READ menuHandle CONSTANT);
 | 
				
			||||||
 | 
						Q_PROPERTY(QString label READ label NOTIFY labelChanged);
 | 
				
			||||||
 | 
						Q_PROPERTY(QString cleanLabel READ cleanLabel NOTIFY labelChanged);
 | 
				
			||||||
 | 
						Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged);
 | 
				
			||||||
 | 
						Q_PROPERTY(QString icon READ icon NOTIFY iconChanged);
 | 
				
			||||||
 | 
						Q_PROPERTY(ToggleButtonType::Enum toggleType READ toggleType NOTIFY toggleTypeChanged);
 | 
				
			||||||
 | 
						Q_PROPERTY(Qt::CheckState checkState READ checkState NOTIFY checkStateChanged);
 | 
				
			||||||
 | 
						Q_PROPERTY(bool isSeparator READ isSeparator NOTIFY separatorChanged);
 | 
				
			||||||
 | 
						Q_PROPERTY(bool hasChildren READ hasChildren NOTIFY hasChildrenChanged);
 | 
				
			||||||
 | 
						Q_PROPERTY(bool showChildren READ isShowingChildren WRITE setShowChildren NOTIFY showingChildrenChanged);
 | 
				
			||||||
 | 
						Q_PROPERTY(QQmlListProperty<DBusMenuItem> children READ children NOTIFY childrenChanged);
 | 
				
			||||||
 | 
						// clang-format on
 | 
				
			||||||
 | 
						QML_NAMED_ELEMENT(DBusMenu);
 | 
				
			||||||
 | 
						QML_UNCREATABLE("DBusMenus can only be acquired from a DBusMenuHandle");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						explicit DBusMenuItem(qint32 id, DBusMenu* menu, DBusMenuItem* parentMenu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Q_INVOKABLE void click();
 | 
				
			||||||
 | 
						Q_INVOKABLE void hover() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] DBusMenu* menuHandle() const;
 | 
				
			||||||
 | 
						[[nodiscard]] QString label() const;
 | 
				
			||||||
 | 
						[[nodiscard]] QString cleanLabel() const;
 | 
				
			||||||
 | 
						[[nodiscard]] bool enabled() const;
 | 
				
			||||||
 | 
						[[nodiscard]] QString icon() const;
 | 
				
			||||||
 | 
						[[nodiscard]] ToggleButtonType::Enum toggleType() const;
 | 
				
			||||||
 | 
						[[nodiscard]] Qt::CheckState checkState() const;
 | 
				
			||||||
 | 
						[[nodiscard]] bool isSeparator() const;
 | 
				
			||||||
 | 
						[[nodiscard]] bool hasChildren() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] bool isShowingChildren() const;
 | 
				
			||||||
 | 
						void setShowChildren(bool showChildren);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] QQmlListProperty<DBusMenuItem> children();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void updateProperties(const QVariantMap& properties, const QStringList& removed = {});
 | 
				
			||||||
 | 
						void onChildrenUpdated();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qint32 id = 0;
 | 
				
			||||||
 | 
						QString mLabel;
 | 
				
			||||||
 | 
						QVector<qint32> mChildren;
 | 
				
			||||||
 | 
						bool mShowChildren = false;
 | 
				
			||||||
 | 
						bool childrenLoaded = false;
 | 
				
			||||||
 | 
						DBusMenu* menu = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					signals:
 | 
				
			||||||
 | 
						void labelChanged();
 | 
				
			||||||
 | 
						//void mnemonicChanged();
 | 
				
			||||||
 | 
						void enabledChanged();
 | 
				
			||||||
 | 
						void iconChanged();
 | 
				
			||||||
 | 
						void separatorChanged();
 | 
				
			||||||
 | 
						void toggleTypeChanged();
 | 
				
			||||||
 | 
						void checkStateChanged();
 | 
				
			||||||
 | 
						void hasChildrenChanged();
 | 
				
			||||||
 | 
						void showingChildrenChanged();
 | 
				
			||||||
 | 
						void childrenChanged();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						QString mCleanLabel;
 | 
				
			||||||
 | 
						//QChar mnemonic;
 | 
				
			||||||
 | 
						bool mEnabled = true;
 | 
				
			||||||
 | 
						bool visible = true;
 | 
				
			||||||
 | 
						bool mSeparator = false;
 | 
				
			||||||
 | 
						QString iconName;
 | 
				
			||||||
 | 
						DBusMenuPngImage* image = nullptr;
 | 
				
			||||||
 | 
						ToggleButtonType::Enum mToggleType = ToggleButtonType::None;
 | 
				
			||||||
 | 
						Qt::CheckState mCheckState = Qt::Checked;
 | 
				
			||||||
 | 
						bool displayChildren = false;
 | 
				
			||||||
 | 
						QVector<qint32> enabledChildren;
 | 
				
			||||||
 | 
						DBusMenuItem* parentMenu = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static qsizetype childrenCount(QQmlListProperty<DBusMenuItem>* property);
 | 
				
			||||||
 | 
						static DBusMenuItem* childAt(QQmlListProperty<DBusMenuItem>* property, qsizetype index);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDebug operator<<(QDebug debug, DBusMenuItem* item);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DBusMenu: public QObject {
 | 
				
			||||||
 | 
						Q_OBJECT;
 | 
				
			||||||
 | 
						Q_PROPERTY(DBusMenuItem* menu READ menu CONSTANT);
 | 
				
			||||||
 | 
						QML_NAMED_ELEMENT(DBusMenuHandle);
 | 
				
			||||||
 | 
						QML_UNCREATABLE("Menu handles cannot be directly created");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						explicit DBusMenu(const QString& service, const QString& path, QObject* parent = nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus::DBusPropertyGroup properties;
 | 
				
			||||||
 | 
						dbus::DBusProperty<quint32> version {this->properties, "Version"};
 | 
				
			||||||
 | 
						dbus::DBusProperty<QString> textDirection {this->properties, "TextDirection"};
 | 
				
			||||||
 | 
						dbus::DBusProperty<QString> status {this->properties, "Status"};
 | 
				
			||||||
 | 
						dbus::DBusProperty<QStringList> iconThemePath {this->properties, "IconThemePath"};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void prepareToShow(qint32 item, bool sendOpened);
 | 
				
			||||||
 | 
						void updateLayout(qint32 parent, qint32 depth);
 | 
				
			||||||
 | 
						void removeRecursive(qint32 id);
 | 
				
			||||||
 | 
						void sendEvent(qint32 item, const QString& event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DBusMenuItem rootItem {0, this, nullptr};
 | 
				
			||||||
 | 
						QHash<qint32, DBusMenuItem*> items {std::make_pair(0, &this->rootItem)};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] DBusMenuItem* menu();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private slots:
 | 
				
			||||||
 | 
						void onLayoutUpdated(quint32 revision, qint32 parent);
 | 
				
			||||||
 | 
						void onItemPropertiesUpdated(
 | 
				
			||||||
 | 
						    const DBusMenuItemPropertiesList& updatedProps,
 | 
				
			||||||
 | 
						    const DBusMenuItemPropertyNamesList& removedProps
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						void updateLayoutRecursive(const DBusMenuLayout& layout, DBusMenuItem* parent, qint32 depth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DBusMenuInterface* interface = nullptr;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDebug operator<<(QDebug debug, DBusMenu* menu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DBusMenuPngImage: public QsImageHandle {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						explicit DBusMenuPngImage(QByteArray data, DBusMenuItem* parent)
 | 
				
			||||||
 | 
						    : QsImageHandle(QQuickImageProvider::Image, parent)
 | 
				
			||||||
 | 
						    , data(std::move(data)) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QByteArray data;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace qs::dbus::dbusmenu
 | 
				
			||||||
| 
						 | 
					@ -43,7 +43,7 @@ qt_add_qml_module(quickshell-service-statusnotifier
 | 
				
			||||||
  VERSION 0.1
 | 
					  VERSION 0.1
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
target_link_libraries(quickshell-service-statusnotifier PRIVATE ${QT_DEPS} quickshell-dbus)
 | 
					target_link_libraries(quickshell-service-statusnotifier PRIVATE ${QT_DEPS} quickshell-dbus quickshell-dbusmenuplugin)
 | 
				
			||||||
target_link_libraries(quickshell PRIVATE quickshell-service-statusnotifierplugin)
 | 
					target_link_libraries(quickshell PRIVATE quickshell-service-statusnotifierplugin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
qs_pch(quickshell-service-statusnotifier)
 | 
					qs_pch(quickshell-service-statusnotifier)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
#include "item.hpp"
 | 
					#include "item.hpp"
 | 
				
			||||||
#include <utility>
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qdbusextratypes.h>
 | 
				
			||||||
#include <qdbusmetatype.h>
 | 
					#include <qdbusmetatype.h>
 | 
				
			||||||
#include <qdbuspendingcall.h>
 | 
					#include <qdbuspendingcall.h>
 | 
				
			||||||
#include <qdbuspendingreply.h>
 | 
					#include <qdbuspendingreply.h>
 | 
				
			||||||
| 
						 | 
					@ -20,12 +21,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "../../core/iconimageprovider.hpp"
 | 
					#include "../../core/iconimageprovider.hpp"
 | 
				
			||||||
#include "../../core/imageprovider.hpp"
 | 
					#include "../../core/imageprovider.hpp"
 | 
				
			||||||
 | 
					#include "../../dbus/dbusmenu/dbusmenu.hpp"
 | 
				
			||||||
#include "../../dbus/properties.hpp"
 | 
					#include "../../dbus/properties.hpp"
 | 
				
			||||||
#include "dbus_item.h"
 | 
					#include "dbus_item.h"
 | 
				
			||||||
#include "dbus_item_types.hpp"
 | 
					#include "dbus_item_types.hpp"
 | 
				
			||||||
#include "host.hpp"
 | 
					#include "host.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace qs::dbus;
 | 
					using namespace qs::dbus;
 | 
				
			||||||
 | 
					using namespace qs::dbus::dbusmenu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Q_LOGGING_CATEGORY(logStatusNotifierItem, "quickshell.service.sni.item", QtWarningMsg);
 | 
					Q_LOGGING_CATEGORY(logStatusNotifierItem, "quickshell.service.sni.item", QtWarningMsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,6 +37,10 @@ namespace qs::service::sni {
 | 
				
			||||||
StatusNotifierItem::StatusNotifierItem(const QString& address, QObject* parent)
 | 
					StatusNotifierItem::StatusNotifierItem(const QString& address, QObject* parent)
 | 
				
			||||||
    : QObject(parent)
 | 
					    : QObject(parent)
 | 
				
			||||||
    , watcherId(address) {
 | 
					    , watcherId(address) {
 | 
				
			||||||
 | 
						qDBusRegisterMetaType<DBusSniIconPixmap>();
 | 
				
			||||||
 | 
						qDBusRegisterMetaType<DBusSniIconPixmapList>();
 | 
				
			||||||
 | 
						qDBusRegisterMetaType<DBusSniTooltip>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// spec is unclear about what exactly an item address is, so account for both combinations
 | 
						// spec is unclear about what exactly an item address is, so account for both combinations
 | 
				
			||||||
	auto splitIdx = address.indexOf('/');
 | 
						auto splitIdx = address.indexOf('/');
 | 
				
			||||||
	auto conn = splitIdx == -1 ? address : address.sliced(0, splitIdx);
 | 
						auto conn = splitIdx == -1 ? address : address.sliced(0, splitIdx);
 | 
				
			||||||
| 
						 | 
					@ -46,10 +53,6 @@ StatusNotifierItem::StatusNotifierItem(const QString& address, QObject* parent)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	qDBusRegisterMetaType<DBusSniIconPixmap>();
 | 
					 | 
				
			||||||
	qDBusRegisterMetaType<DBusSniIconPixmapList>();
 | 
					 | 
				
			||||||
	qDBusRegisterMetaType<DBusSniTooltip>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// clang-format off
 | 
						// clang-format off
 | 
				
			||||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewTitle, &this->title, &AbstractDBusProperty::update);
 | 
						QObject::connect(this->item, &DBusStatusNotifierItem::NewTitle, &this->title, &AbstractDBusProperty::update);
 | 
				
			||||||
	QObject::connect(this->item, &DBusStatusNotifierItem::NewIcon, &this->iconName, &AbstractDBusProperty::update);
 | 
						QObject::connect(this->item, &DBusStatusNotifierItem::NewIcon, &this->iconName, &AbstractDBusProperty::update);
 | 
				
			||||||
| 
						 | 
					@ -227,6 +230,15 @@ void StatusNotifierItem::updateIcon() {
 | 
				
			||||||
	emit this->iconChanged();
 | 
						emit this->iconChanged();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DBusMenu* StatusNotifierItem::createMenu() const {
 | 
				
			||||||
 | 
						auto path = this->menuPath.get().path();
 | 
				
			||||||
 | 
						if (!path.isEmpty()) {
 | 
				
			||||||
 | 
							return new DBusMenu(this->item->service(), this->menuPath.get().path());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void StatusNotifierItem::onGetAllFinished() {
 | 
					void StatusNotifierItem::onGetAllFinished() {
 | 
				
			||||||
	if (this->mReady) return;
 | 
						if (this->mReady) return;
 | 
				
			||||||
	this->mReady = true;
 | 
						this->mReady = true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@
 | 
				
			||||||
#include <qtypes.h>
 | 
					#include <qtypes.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "../../core/imageprovider.hpp"
 | 
					#include "../../core/imageprovider.hpp"
 | 
				
			||||||
 | 
					#include "../../dbus/dbusmenu/dbusmenu.hpp"
 | 
				
			||||||
#include "../../dbus/properties.hpp"
 | 
					#include "../../dbus/properties.hpp"
 | 
				
			||||||
#include "dbus_item.h"
 | 
					#include "dbus_item.h"
 | 
				
			||||||
#include "dbus_item_types.hpp"
 | 
					#include "dbus_item_types.hpp"
 | 
				
			||||||
| 
						 | 
					@ -40,6 +41,7 @@ public:
 | 
				
			||||||
	[[nodiscard]] bool isReady() const;
 | 
						[[nodiscard]] bool isReady() const;
 | 
				
			||||||
	[[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* createMenu() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void activate();
 | 
						void activate();
 | 
				
			||||||
	void secondaryActivate();
 | 
						void secondaryActivate();
 | 
				
			||||||
| 
						 | 
					@ -77,6 +79,7 @@ private:
 | 
				
			||||||
	DBusStatusNotifierItem* item = nullptr;
 | 
						DBusStatusNotifierItem* item = nullptr;
 | 
				
			||||||
	TrayImageHandle imageHandle {this};
 | 
						TrayImageHandle imageHandle {this};
 | 
				
			||||||
	bool mReady = false;
 | 
						bool mReady = false;
 | 
				
			||||||
 | 
						dbus::dbusmenu::DBusMenu* mMenu = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// bumped to inhibit caching
 | 
						// bumped to inhibit caching
 | 
				
			||||||
	quint32 iconIndex = 0;
 | 
						quint32 iconIndex = 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,11 +9,13 @@
 | 
				
			||||||
#include <qtmetamacros.h>
 | 
					#include <qtmetamacros.h>
 | 
				
			||||||
#include <qtypes.h>
 | 
					#include <qtypes.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../../dbus/dbusmenu/dbusmenu.hpp"
 | 
				
			||||||
#include "../../dbus/properties.hpp"
 | 
					#include "../../dbus/properties.hpp"
 | 
				
			||||||
#include "host.hpp"
 | 
					#include "host.hpp"
 | 
				
			||||||
#include "item.hpp"
 | 
					#include "item.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace qs::dbus;
 | 
					using namespace qs::dbus;
 | 
				
			||||||
 | 
					using namespace qs::dbus::dbusmenu;
 | 
				
			||||||
using namespace qs::service::sni;
 | 
					using namespace qs::service::sni;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SystemTrayItem::SystemTrayItem(qs::service::sni::StatusNotifierItem* item, QObject* parent)
 | 
					SystemTrayItem::SystemTrayItem(qs::service::sni::StatusNotifierItem* item, QObject* parent)
 | 
				
			||||||
| 
						 | 
					@ -27,8 +29,11 @@ SystemTrayItem::SystemTrayItem(qs::service::sni::StatusNotifierItem* item, QObje
 | 
				
			||||||
	QObject::connect(this->item, &StatusNotifierItem::iconChanged, this, &SystemTrayItem::iconChanged);
 | 
						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::tooltipTitleChanged);
 | 
				
			||||||
	QObject::connect(&this->item->tooltip, &AbstractDBusProperty::changed, this, &SystemTrayItem::tooltipDescriptionChanged);
 | 
						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);
 | 
						QObject::connect(&this->item->isMenu, &AbstractDBusProperty::changed, this, &SystemTrayItem::onlyMenuChanged);
 | 
				
			||||||
	// clang-format on
 | 
						// clang-format on
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!this->item->menuPath.get().path().isEmpty()) this->onMenuPathChanged();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
QString SystemTrayItem::id() const {
 | 
					QString SystemTrayItem::id() const {
 | 
				
			||||||
| 
						 | 
					@ -84,11 +89,25 @@ QString SystemTrayItem::tooltipDescription() const {
 | 
				
			||||||
	return this->item->tooltip.get().description;
 | 
						return this->item->tooltip.get().description;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DBusMenuItem* SystemTrayItem::menu() const {
 | 
				
			||||||
 | 
						if (this->mMenu == nullptr) return nullptr;
 | 
				
			||||||
 | 
						return &this->mMenu->rootItem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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::activate() { this->item->activate(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SystemTrayItem::secondaryActivate() { this->item->secondaryActivate(); }
 | 
					void SystemTrayItem::secondaryActivate() { this->item->secondaryActivate(); }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,15 +58,14 @@ 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);
 | 
				
			||||||
 | 
						Q_PROPERTY(qs::dbus::dbusmenu::DBusMenuItem* menu READ menu NOTIFY menuChanged);
 | 
				
			||||||
	// If this tray item only offers a menu and no activation action.
 | 
						// If this tray item only offers a menu and no activation action.
 | 
				
			||||||
	Q_PROPERTY(bool onlyMenu READ onlyMenu NOTIFY onlyMenuChanged);
 | 
						Q_PROPERTY(bool onlyMenu READ onlyMenu NOTIFY onlyMenuChanged);
 | 
				
			||||||
	QML_ELEMENT;
 | 
						QML_ELEMENT;
 | 
				
			||||||
 | 
						QML_UNCREATABLE("SystemTrayItems can only be acquired from SystemTray");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	explicit SystemTrayItem(
 | 
						explicit SystemTrayItem(qs::service::sni::StatusNotifierItem* item, QObject* parent = nullptr);
 | 
				
			||||||
	    qs::service::sni::StatusNotifierItem* item = nullptr,
 | 
					 | 
				
			||||||
	    QObject* parent = nullptr
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Primary activation action, generally triggered via a left click.
 | 
						// Primary activation action, generally triggered via a left click.
 | 
				
			||||||
	Q_INVOKABLE void activate();
 | 
						Q_INVOKABLE void activate();
 | 
				
			||||||
| 
						 | 
					@ -84,6 +83,7 @@ public:
 | 
				
			||||||
	[[nodiscard]] QString icon() const;
 | 
						[[nodiscard]] QString icon() const;
 | 
				
			||||||
	[[nodiscard]] QString tooltipTitle() const;
 | 
						[[nodiscard]] QString tooltipTitle() const;
 | 
				
			||||||
	[[nodiscard]] QString tooltipDescription() const;
 | 
						[[nodiscard]] QString tooltipDescription() const;
 | 
				
			||||||
 | 
						[[nodiscard]] qs::dbus::dbusmenu::DBusMenuItem* menu() const;
 | 
				
			||||||
	[[nodiscard]] bool onlyMenu() const;
 | 
						[[nodiscard]] bool onlyMenu() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
signals:
 | 
					signals:
 | 
				
			||||||
| 
						 | 
					@ -94,10 +94,15 @@ signals:
 | 
				
			||||||
	void iconChanged();
 | 
						void iconChanged();
 | 
				
			||||||
	void tooltipTitleChanged();
 | 
						void tooltipTitleChanged();
 | 
				
			||||||
	void tooltipDescriptionChanged();
 | 
						void tooltipDescriptionChanged();
 | 
				
			||||||
 | 
						void menuChanged();
 | 
				
			||||||
	void onlyMenuChanged();
 | 
						void onlyMenuChanged();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private slots:
 | 
				
			||||||
 | 
						void onMenuPathChanged();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	qs::service::sni::StatusNotifierItem* item = nullptr;
 | 
						qs::service::sni::StatusNotifierItem* item = nullptr;
 | 
				
			||||||
 | 
						qs::dbus::dbusmenu::DBusMenu* mMenu = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	friend class SystemTray;
 | 
						friend class SystemTray;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue