commit bcca07fdc49189e59ff4b0222dc7c48ab5f3fbab
parent 1bb5070e109c023e719c0922bf1a4baa2d41b45a
Author: Janne Veteläinen <janne.vetelainen@elisanet.fi>
Date: Fri, 17 May 2024 01:29:38 +0300
Freeze menu updates when popover is visible
Diffstat:
4 files changed, 136 insertions(+), 26 deletions(-)
diff --git a/systray/sndbusmenu.c b/systray/sndbusmenu.c
@@ -18,6 +18,7 @@ struct _SnDbusmenu {
GDBusProxy *proxy;
uint32_t revision;
+ gboolean reschedule;
};
G_DEFINE_FINAL_TYPE(SnDbusmenu, sn_dbusmenu, G_TYPE_OBJECT)
@@ -305,21 +306,64 @@ on_layout_updated(GDBusProxy *proxy, GAsyncResult *res, void *data)
return;
}
- GVariant *layout;
- GVariant *menuitems;
+ gboolean isvisible = sn_item_get_popover_visible(dbusmenu->snitem);
+ char status[1024];
- layout = g_variant_get_child_value(retvariant, 1);
- menuitems = g_variant_get_child_value(layout, 2);
+ if (isvisible)
+ strcpy(status, "visible");
+ else
+ strcpy(status, "notvisibile");
- GMenu *newmenu = create_menumodel(menuitems, dbusmenu);
- sn_item_set_menu_model(dbusmenu->snitem, newmenu);
+ if (isvisible) {
+ dbusmenu->reschedule = TRUE;
+ g_debug("Popover was visible, couldn't update menu %s", dbusmenu->busname);
+ } else {
+
+ g_debug("Running menu update: name %s, revision %i\n \
+ popover status: %s",
+ dbusmenu->busname,
+ dbusmenu->revision,
+ status);
+
+ GVariant *layout;
+ GVariant *menuitems;
+
+ layout = g_variant_get_child_value(retvariant, 1);
+ menuitems = g_variant_get_child_value(layout, 2);
+
+ GMenu *newmenu = create_menumodel(menuitems, dbusmenu);
+ sn_item_set_menu_model(dbusmenu->snitem, newmenu);
+ g_variant_unref(menuitems);
+ g_variant_unref(layout);
+ }
- g_variant_unref(menuitems);
- g_variant_unref(layout);
g_variant_unref(retvariant);
g_object_unref(dbusmenu);
}
+void
+reschedule_update(SnItem *snitem, GParamSpec *pspec, void *data)
+{
+ SnDbusmenu *dbusmenu = SN_DBUSMENU(data);
+
+ gboolean popover_visible = sn_item_get_popover_visible(snitem);
+ if (popover_visible || !dbusmenu->reschedule)
+ return;
+
+
+ dbusmenu->reschedule = FALSE;
+
+ g_debug("sending reschedule call %s", dbusmenu->busname);
+ g_dbus_proxy_call(dbusmenu->proxy,
+ "GetLayout",
+ g_variant_new("(iias)", 0, -1, NULL),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ (GAsyncReadyCallback)on_layout_updated,
+ g_object_ref(dbusmenu));
+}
+
// We just rebuild the entire menu every time...
// ItemsPropertiesUpdated signal would carry data of
@@ -333,17 +377,26 @@ on_menuproxy_signal(GDBusProxy *proxy,
void *data)
{
SnDbusmenu *dbusmenu = SN_DBUSMENU(data);
+
+ // Stop updates when popover is visible
+ gboolean popover_visible = sn_item_get_popover_visible(dbusmenu->snitem);
+
if (strcmp(signal, "LayoutUpdated") == 0) {
- uint32_t revision = UINT32_MAX;
+ uint32_t revision;
int32_t parentid;
g_variant_get(params, "(ui)", &revision, &parentid);
if (dbusmenu->revision != UINT32_MAX && revision <= dbusmenu->revision) {
- // g_debug("%s got %s, but menurevision didn't change. Ignoring\n", snitem->busname, signal);
+ g_debug("early return on signal %s, reason: revision", dbusmenu->busname);
+ return;
+ } else if (popover_visible) {
+ g_debug("early return on signal %s, reason: popover visible", dbusmenu->busname);
+ dbusmenu->reschedule = TRUE;
return;
- } else {
- dbusmenu->revision = revision;
}
+ dbusmenu->revision = revision;
+
+ g_debug("sending menuupdate call %s", dbusmenu->busname);
g_dbus_proxy_call(dbusmenu->proxy,
"GetLayout",
g_variant_new("(iias)", 0, -1, NULL),
@@ -354,6 +407,12 @@ on_menuproxy_signal(GDBusProxy *proxy,
g_object_ref(dbusmenu));
} else if (strcmp(signal, "ItemsPropertiesUpdated") == 0) {
+ if (popover_visible) {
+ g_debug("early return on signal %s, reason: popover visible", dbusmenu->busname);
+ dbusmenu->reschedule = TRUE;
+ return;
+ }
+ g_debug("sending menuupdate call %s", dbusmenu->busname);
g_dbus_proxy_call(dbusmenu->proxy,
"GetLayout",
g_variant_new("(iias)", 0, -1, NULL),
@@ -524,6 +583,7 @@ menuproxy_ready_cb(GObject *obj, GAsyncResult *res, void *data)
static void
sn_dbusmenu_init(SnDbusmenu *self)
{
+ self->reschedule = FALSE;
}
static void
@@ -543,6 +603,8 @@ sn_dbusmenu_constructed(GObject *obj)
g_object_ref(self));
g_dbus_node_info_unref(nodeinfo);
+ g_signal_connect(self->snitem, "notify::menuvisible", G_CALLBACK(reschedule_update), self);
+
G_OBJECT_CLASS(sn_dbusmenu_parent_class)->constructed(obj);
}
diff --git a/systray/snhost.c b/systray/snhost.c
@@ -147,8 +147,10 @@ sn_host_unregister_item(SnHost *self, SnItem *snitem)
self->trayitems = g_slist_remove(self->trayitems, snitem);
+ g_object_ref(snitem);
gtk_box_remove(GTK_BOX(self), GTK_WIDGET(snitem));
g_object_run_dispose(G_OBJECT(snitem));
+ g_object_unref(snitem);
self->nitems = self->nitems - 1;
diff --git a/systray/snitem.c b/systray/snitem.c
@@ -29,6 +29,7 @@ struct _SnItem
gboolean ready;
gboolean exiting;
+ gboolean menu_visible;
};
G_DEFINE_FINAL_TYPE(SnItem, sn_item, GTK_TYPE_WIDGET)
@@ -41,6 +42,7 @@ enum
PROP_PROXY,
PROP_ACTIONGROUP,
PROP_DBUSMENU,
+ PROP_MENUVISIBLE,
N_PROPERTIES
};
@@ -384,6 +386,22 @@ sn_item_proxy_ready_handler(GObject *obj, GAsyncResult *res, void *data)
g_object_unref(self);
}
+void
+sn_item_popup(SnItem *self)
+{
+ g_object_set(self, "menuvisible", TRUE, NULL);
+ g_debug("popping up %s", self->busname);
+ gtk_popover_popup(GTK_POPOVER(self->popovermenu));
+}
+
+void
+sn_item_notify_closed(GtkPopover *popover, void *data)
+{
+ SnItem *self = SN_ITEM(data);
+ g_object_set(self, "menuvisible", FALSE, NULL);
+}
+
+
static void
sn_item_leftclick_handler(GtkGestureClick *click,
int n_press,
@@ -428,8 +446,7 @@ sn_item_rightclick_handler_helper(GObject *obj, GAsyncResult *res, void *data)
g_strrstr(err->message, "error occured in AboutToShow") == 0) {
g_error_free(err);
- gtk_popover_popup(GTK_POPOVER(self->popovermenu));
-
+ sn_item_popup(self);
// Report rest of possible errors
} else if (err) {
g_warning("%s\n", err->message);
@@ -438,7 +455,7 @@ sn_item_rightclick_handler_helper(GObject *obj, GAsyncResult *res, void *data)
} else {
g_variant_unref(val);
- gtk_popover_popup(GTK_POPOVER(self->popovermenu));
+ sn_item_popup(self);
}
g_object_unref(self);
@@ -470,13 +487,14 @@ sn_item_rightclick_handler(GtkGestureClick *click,
g_object_unref(menuproxy);
}
-static void sn_item_measure(GtkWidget *widget,
- GtkOrientation orientation,
- int for_size,
- int *minimum,
- int *natural,
- int *minimum_baseline,
- int *natural_baseline)
+static void
+sn_item_measure(GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
{
SnItem *self = SN_ITEM(widget);
@@ -496,10 +514,11 @@ static void sn_item_measure(GtkWidget *widget,
}
}
-static void sn_item_size_allocate(GtkWidget *widget,
- int width,
- int height,
- int baseline)
+static void
+sn_item_size_allocate(GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
{
SnItem *self = SN_ITEM(widget);
gtk_widget_size_allocate(self->image, &(GtkAllocation) {0, 0, width, height}, -1);
@@ -530,6 +549,9 @@ sn_item_set_property(GObject *object, uint property_id, const GValue *value, GPa
case PROP_DBUSMENU:
self->dbusmenu = g_value_get_object(value);
break;
+ case PROP_MENUVISIBLE:
+ self->menu_visible = g_value_get_boolean(value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
@@ -556,6 +578,9 @@ sn_item_get_property(GObject *object, uint property_id, GValue *value, GParamSpe
case PROP_DBUSMENU:
g_value_set_object(value, self->dbusmenu);
break;
+ case PROP_MENUVISIBLE:
+ g_value_set_boolean(value, self->menu_visible);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
}
@@ -608,6 +633,13 @@ sn_item_class_init(SnItemClass *klass)
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_MENUVISIBLE] =
+ g_param_spec_boolean("menuvisible", NULL, NULL,
+ FALSE,
+ G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
widget_class->measure = sn_item_measure;
widget_class->size_allocate = sn_item_size_allocate;
@@ -659,6 +691,8 @@ sn_item_init(SnItem *self)
gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(rightclick), 3);
g_signal_connect(rightclick, "pressed", G_CALLBACK(sn_item_rightclick_handler), self);
gtk_widget_add_controller(widget, GTK_EVENT_CONTROLLER(rightclick));
+
+ g_signal_connect(self->popovermenu, "closed", G_CALLBACK(sn_item_notify_closed), self);
}
static void
@@ -687,6 +721,7 @@ sn_item_dispose(GObject *obj)
{
SnItem *self = SN_ITEM(obj);
self->exiting = TRUE;
+ gtk_popover_popdown(GTK_POPOVER(self->popovermenu));
g_debug("Disposing snitem %s %s",
self->busname, self->busobj);
@@ -756,6 +791,16 @@ sn_item_get_busname(SnItem *self)
return busname;
}
+gboolean
+sn_item_get_popover_visible(SnItem *self)
+{
+ gboolean visible;
+
+ g_object_get(self, "menuvisible", &visible, NULL);
+
+ return visible;
+}
+
SnItem*
sn_item_new(const char *busname, const char *busobj, int iconsize)
{
diff --git a/systray/snitem.h b/systray/snitem.h
@@ -16,6 +16,7 @@ SnItem* sn_item_new (const char *busname,
void sn_item_set_menu_model (SnItem *widget, GMenu *menu);
void sn_item_add_action (SnItem *self, GSimpleAction *action);
char* sn_item_get_busname (SnItem *self);
+gboolean sn_item_get_popover_visible (SnItem *self);
G_END_DECLS