From c7bc43571acc57546cd732e3658be86d308f0256 Mon Sep 17 00:00:00 2001 From: luoqing Date: Thu, 14 Sep 2023 17:34:29 +0800 Subject: [PATCH] feature(sn-icon-menu):Support dynamic increase or decrease of sub-item MenuItem in the tray right-click menu and property changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 支持托盘右键菜单中的子项MenuItem动态增减以及属性变化 Closed #15944 --- src/tray/kiran-sn-icon-menu.c | 126 +++++++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 17 deletions(-) diff --git a/src/tray/kiran-sn-icon-menu.c b/src/tray/kiran-sn-icon-menu.c index 22e321f..ffae1bb 100644 --- a/src/tray/kiran-sn-icon-menu.c +++ b/src/tray/kiran-sn-icon-menu.c @@ -1,19 +1,20 @@ /** - * Copyright (c) 2020 ~ 2021 KylinSec Co., Ltd. + * Copyright (c) 2020 ~ 2021 KylinSec Co., Ltd. * kiran-cc-daemon is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - * + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * * Author: wangxiaoqing */ #include "kiran-sn-icon-menu.h" #include +#include struct _KiranSnIconMenuPrivate { gchar *bus_name; @@ -30,6 +31,7 @@ enum LAST_PROP }; +#define DATA_KEY_SIGNAL_PROPERTY_CHANGED_IS_CONNECTED "signal_property_changed_is_connected" static GParamSpec *properties[LAST_PROP] = {NULL}; G_DEFINE_TYPE_WITH_PRIVATE(KiranSnIconMenu, kiran_sn_icon_menu, GTK_TYPE_MENU) @@ -175,6 +177,88 @@ create_widget_from_menuitem(DbusmenuMenuitem *item) return gmi; } +static void +kiran_sn_icon_menu_create_widget_from_dbusmenuitem(KiranSnIconMenu *menu, DbusmenuMenuitem *item) +{ + GtkWidget *gmi = create_widget_from_menuitem(item); + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), gmi); + gtk_widget_show(gmi); + + g_signal_connect(gmi, + "activate", + G_CALLBACK(activate_cb), + item); +} + +static void +kiran_sn_icon_menu_remove_widget_all(KiranSnIconMenu *menu) +{ + GList *child; + GList *container_children = gtk_container_get_children(GTK_CONTAINER(menu)); + for (child = container_children; child; child = child->next) + { + gtk_container_remove(GTK_CONTAINER(menu), GTK_WIDGET(child->data)); + gtk_widget_destroy(GTK_WIDGET(child->data)); + } +} + +static void +property_changed_cb(DbusmenuMenuitem *item, gchar *property, GVariant *value, gpointer user_data) +{ + GList *child; + KiranSnIconMenu *menu = KIRAN_SN_ICON_MENU(user_data); + + kiran_sn_icon_menu_remove_widget_all(menu); + + // 遍历MenuItem,以找到root + DbusmenuMenuitem *root = dbusmenu_menuitem_get_parent(item); + gboolean is_root = dbusmenu_menuitem_get_root(root); + + while (!is_root) + { + root = dbusmenu_menuitem_get_parent(root); + is_root = dbusmenu_menuitem_get_root(root); + } + + GList *dbus_menuitem_children = dbusmenu_menuitem_get_children(root); + for (child = dbus_menuitem_children; child; child = child->next) + { + kiran_sn_icon_menu_create_widget_from_dbusmenuitem(menu,child->data); + } +} + +static void +submenu_get_children_to_connect(DbusmenuMenuitem *submenu, gpointer user_data) +{ + KiranSnIconMenu *menu = KIRAN_SN_ICON_MENU(user_data); + GList *submenu_children = dbusmenu_menuitem_get_children(submenu); + GList *submenu_child; + for (submenu_child = submenu_children; submenu_child; submenu_child = submenu_child->next) + { + const gchar *children_display = dbusmenu_menuitem_property_get(submenu_child->data, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY); + if (g_strcmp0(children_display, "submenu") == 0) + { + submenu_get_children_to_connect(submenu_child->data, menu); + } + + gboolean is_connected = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(submenu_child->data), DATA_KEY_SIGNAL_PROPERTY_CHANGED_IS_CONNECTED)); + if (!is_connected) + { + g_signal_connect(submenu_child->data, DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(property_changed_cb), menu); + g_object_set_data(G_OBJECT(submenu_child->data), DATA_KEY_SIGNAL_PROPERTY_CHANGED_IS_CONNECTED, GINT_TO_POINTER(TRUE)); + } + } +} + +/** + * NOTE: + * dbus_menuitem 移除某个menuitem时,没有类似REMOVE_MENUITEM的信号, + * 而是会触发 DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED 信号。 + * 新增时会触发 DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM 信号 + * + * 因此layout_updated_cb中还会处理menuitem变化的情况 + */ static void layout_updated_cb(DbusmenuClient *client, gpointer user_data) @@ -182,21 +266,29 @@ layout_updated_cb(DbusmenuClient *client, KiranSnIconMenu *menu; DbusmenuMenuitem *root = dbusmenu_client_get_root(client); GList *child; - GList *children = dbusmenu_menuitem_get_children(root); menu = KIRAN_SN_ICON_MENU(user_data); - for (child = children; child; child = child->next) + kiran_sn_icon_menu_remove_widget_all(menu); + + GList *dbus_menuitem_children = dbusmenu_menuitem_get_children(root); + for (child = dbus_menuitem_children; child; child = child->next) { - GtkWidget *gmi = create_widget_from_menuitem(child->data); + kiran_sn_icon_menu_create_widget_from_dbusmenuitem(menu,child->data); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), gmi); - gtk_widget_show(gmi); + // NOTE:只修改一个属性,可能会触发多个 PROPERTY_CHANGE 信号 + const gchar *children_display = dbusmenu_menuitem_property_get(child->data, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY); + if (g_strcmp0(children_display, "submenu") == 0) + { + submenu_get_children_to_connect(child->data, menu); + } - g_signal_connect(gmi, - "activate", - G_CALLBACK(activate_cb), - child->data); + gboolean is_connected = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(child->data), DATA_KEY_SIGNAL_PROPERTY_CHANGED_IS_CONNECTED)); + if (!is_connected) + { + g_signal_connect(child->data, DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(property_changed_cb), menu); + g_object_set_data(G_OBJECT(child->data), DATA_KEY_SIGNAL_PROPERTY_CHANGED_IS_CONNECTED, GINT_TO_POINTER(TRUE)); + } } } -- 2.33.0