From 69a780babbf29e35cbf3663375f7c2eca97e7b67 Mon Sep 17 00:00:00 2001 From: Jehan Date: Fri, 10 Feb 2023 16:20:24 +0100 Subject: [PATCH] app: add proxy support to GimpAction/GimpMenu. The proxy properly shows a color area or viewable (depending on context), and this is properly updated when the associated properties are changed. The label change is also properly updated in the label part of the proxy. Very relevant actions to test the proxy items are dynamically updated are generated actions such as the "file-open-recent-*" actions (with both label and viewable updates), or again the Edit > "Fill with (FG|BG Color)|Pattern" (with color/pattern updates). --- app/widgets/gimpaction.c | 92 ++++++++++++++++++++++++++++++++-------- app/widgets/gimpmenu.c | 1 + 2 files changed, 75 insertions(+), 18 deletions(-) diff --git a/app/widgets/gimpaction.c b/app/widgets/gimpaction.c index 7a1a6e2d05..ac3aa839c3 100644 --- a/app/widgets/gimpaction.c +++ b/app/widgets/gimpaction.c @@ -56,6 +56,8 @@ typedef struct _GimpActionPrivate GimpActionPrivate; struct _GimpActionPrivate { GimpContext *context; + /* This recursive pointer is needed for the finalize(). */ + GimpAction *action; gboolean sensitive; gchar *disable_reason; @@ -64,6 +66,8 @@ struct _GimpActionPrivate GimpViewable *viewable; PangoEllipsizeMode ellipsize; gint max_width_chars; + + GList *proxies; }; @@ -78,6 +82,9 @@ static void gimp_action_tooltip_notify (GimpAction *action, const GParamSpec *pspec, gpointer data); +static void gimp_action_proxy_destroy (GtkWidget *proxy, + GimpAction *action); + G_DEFINE_INTERFACE (GimpAction, gimp_action, GTK_TYPE_ACTION) @@ -162,9 +169,11 @@ gimp_action_init (GimpAction *action) priv = GET_PRIVATE (action); + priv->action = action; priv->sensitive = TRUE; priv->ellipsize = PANGO_ELLIPSIZE_NONE; priv->max_width_chars = -1; + priv->proxies = NULL; g_signal_connect (action, "notify::label", G_CALLBACK (gimp_action_label_notify), @@ -524,6 +533,7 @@ gimp_action_get_display_accels (GimpAction *action) GSList * gimp_action_get_proxies (GimpAction *action) { + /* TODO GAction: how exactly are these proxies set in the GtkAction API? */ return gtk_action_get_proxies ((GtkAction *) action); } @@ -727,14 +737,15 @@ gimp_action_set_property (GObject *object, if (set_proxy) { - GSList *list; + /* Set or update the proxy rendering. */ + for (GList *list = priv->proxies; list; list = list->next) + gimp_action_set_proxy (GIMP_ACTION (object), list->data); - for (list = gimp_action_get_proxies (GIMP_ACTION (object)); + /* TODO GAction: remove when port complete. */ + for (GSList *list = gimp_action_get_proxies (GIMP_ACTION (object)); list; list = g_slist_next (list)) - { - gimp_action_set_proxy (GIMP_ACTION (object), list->data); - } + gimp_action_set_proxy (GIMP_ACTION (object), list->data); } } @@ -743,6 +754,7 @@ gimp_action_set_proxy (GimpAction *action, GtkWidget *proxy) { GimpActionPrivate *priv = GET_PRIVATE (action); + GtkWidget *child; if (! GTK_IS_MENU_ITEM (proxy)) return; @@ -825,20 +837,27 @@ gimp_action_set_proxy (GimpAction *action, } } - { - GtkWidget *child = gtk_bin_get_child (GTK_BIN (proxy)); - if (GTK_IS_BOX (child)) - child = g_object_get_data (G_OBJECT (proxy), "gimp-menu-item-label"); + child = gtk_bin_get_child (GTK_BIN (proxy)); - if (GTK_IS_LABEL (child)) - { - GtkLabel *label = GTK_LABEL (child); + if (GTK_IS_BOX (child)) + child = g_object_get_data (G_OBJECT (proxy), "gimp-menu-item-label"); - gtk_label_set_ellipsize (label, priv->ellipsize); - gtk_label_set_max_width_chars (label, priv->max_width_chars); - } - } + if (GTK_IS_LABEL (child)) + { + GtkLabel *label = GTK_LABEL (child); + + gtk_label_set_ellipsize (label, priv->ellipsize); + gtk_label_set_max_width_chars (label, priv->max_width_chars); + } + + if (! g_list_find (priv->proxies, proxy)) + { + priv->proxies = g_list_prepend (priv->proxies, proxy); + g_signal_connect (proxy, "destroy", + gimp_action_proxy_destroy, + action); + } } @@ -876,6 +895,17 @@ gimp_action_private_finalize (GimpActionPrivate *priv) g_clear_pointer (&priv->color, g_free); g_clear_object (&priv->viewable); + for (GList *iter = priv->proxies; iter; iter = iter->next) + /* TODO GAction: if an action associated to a proxy menu item disappears, + * shouldn't we also destroy the item itself (not just disconnect it)? It + * would now point to a non-existing action. + */ + g_signal_handlers_disconnect_by_func (iter->data, + gimp_action_proxy_destroy, + priv->action); + g_list_free (priv->proxies); + priv->proxies = NULL; + g_slice_free (GimpActionPrivate, priv); } @@ -884,9 +914,26 @@ gimp_action_label_notify (GimpAction *action, const GParamSpec *pspec, gpointer data) { - GSList *list; + for (GList *iter = GET_PRIVATE (action)->proxies; iter; iter = iter->next) + { + if (GTK_IS_MENU_ITEM (iter->data)) + { + GtkWidget *child = gtk_bin_get_child (GTK_BIN (iter->data)); - for (list = gimp_action_get_proxies (action); + if (GTK_IS_BOX (child)) + { + child = g_object_get_data (G_OBJECT (iter->data), + "gimp-menu-item-label"); + + if (GTK_IS_LABEL (child)) + gtk_label_set_text (GTK_LABEL (child), + gimp_action_get_label (action)); + } + } + } + + /* TODO GAction: this will have to be removed after the port is complete. */ + for (GSList *list = gimp_action_get_proxies (action); list; list = g_slist_next (list)) { @@ -921,3 +968,12 @@ gimp_action_tooltip_notify (GimpAction *action, gimp_action_set_proxy_tooltip (action, list->data); } } + +static void +gimp_action_proxy_destroy (GtkWidget *proxy, + GimpAction *action) +{ + GimpActionPrivate *priv = GET_PRIVATE (action); + + priv->proxies = g_list_remove (priv->proxies, proxy); +} diff --git a/app/widgets/gimpmenu.c b/app/widgets/gimpmenu.c index 52dde1e4ec..75ae3d8899 100644 --- a/app/widgets/gimpmenu.c +++ b/app/widgets/gimpmenu.c @@ -662,6 +662,7 @@ gimp_menu_add_action (GimpMenu *menu, } gtk_actionable_set_action_name (GTK_ACTIONABLE (item), action_name); + gimp_action_set_proxy (GIMP_ACTION (action), item); g_signal_connect_object (action, "notify::label", G_CALLBACK (gimp_menu_action_notify_label),