[gtk/widget-class-actions: 1/2] Allow registering actions per-class



commit 606e11b755eb3e437f1719b154bc440281bac356
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Jun 14 12:12:10 2019 +0000

    Allow registering actions per-class
    
    Add a facility to register and install actions.
    To register them, call gtk_widget_class_install_action
    in class_init. To install them, call
    gtk_widget_add_class_actions in init. This adds
    one or more action groups to the widgets action
    muxer. There's also some convenience api to
    notify about action state changes.

 gtk/gtkwidget.c | 385 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkwidget.h |  30 +++++
 2 files changed, 415 insertions(+)
---
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 0bc88b06b2..9e4b3dcda4 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -494,6 +494,14 @@ typedef struct {
   GDestroyNotify        destroy_notify;
 } GtkWidgetTemplate;
 
+typedef struct {
+  char *prefix;
+  char *name;
+  GtkWidgetActionActivate activate;
+  GtkWidgetActionQuery query;
+  GtkWidgetActionChange change;
+} GtkWidgetAction;
+
 struct _GtkWidgetClassPrivate
 {
   GtkWidgetTemplate *template;
@@ -501,6 +509,7 @@ struct _GtkWidgetClassPrivate
   AtkRole accessible_role;
   const char *css_name;
   GType layout_manager_type;
+  GPtrArray *actions;
 };
 
 enum {
@@ -13479,3 +13488,379 @@ gtk_widget_should_layout (GtkWidget *widget)
   return TRUE;
 }
 
+
+enum {
+  PROP_WIDGET = 1,
+  PROP_PREFIX
+};
+
+typedef struct {
+  GObject parent;
+
+  GtkWidget *widget;
+  char *prefix;
+} GtkWidgetClassActionGroup;
+
+typedef struct {
+  GObjectClass parent_class;
+} GtkWidgetClassActionGroupClass;
+
+static void gtk_widget_class_action_group_iface_init (GActionGroupInterface *iface);
+
+GType  gtk_widget_class_action_group_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE_WITH_CODE (GtkWidgetClassActionGroup, gtk_widget_class_action_group, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, 
gtk_widget_class_action_group_iface_init))
+
+static char **
+gtk_widget_class_action_group_list_actions (GActionGroup *action_group)
+{
+  GtkWidgetClassActionGroup *group = (GtkWidgetClassActionGroup*)action_group;
+  GtkWidgetClass *class = GTK_WIDGET_GET_CLASS (group->widget);
+  GtkWidgetClassPrivate *priv = class->priv;
+  GPtrArray *actions;
+  int i;
+
+  actions = g_ptr_array_new ();
+ 
+  for (i = 0; i < priv->actions->len; i++)
+    {
+      GtkWidgetAction *action = g_ptr_array_index (priv->actions, i);
+
+      if (strcmp (group->prefix, action->prefix) == 0)
+        g_ptr_array_add (actions, g_strdup (action->name));
+    }
+
+  g_ptr_array_add (actions, NULL);
+
+  return (char **)g_ptr_array_free (actions, FALSE);
+}
+
+static gboolean
+gtk_widget_class_action_group_query_action (GActionGroup        *action_group,
+                                            const gchar         *action_name,
+                                            gboolean            *enabled,
+                                            const GVariantType **parameter_type,
+                                            const GVariantType **state_type,
+                                            GVariant           **state_hint,
+                                            GVariant           **state)
+{
+  GtkWidgetClassActionGroup *group = (GtkWidgetClassActionGroup*)action_group;
+  GtkWidgetClass *class = GTK_WIDGET_GET_CLASS (group->widget);
+  GtkWidgetClassPrivate *priv = class->priv;
+  int i;
+
+  for (i = 0; i < priv->actions->len; i++)
+    {
+      GtkWidgetAction *action = g_ptr_array_index (priv->actions, i);
+
+      if (strcmp (action->prefix, group->prefix) == 0 &&
+          strcmp (action->name, action_name) == 0)
+        {
+          return action->query (group->widget,
+                                action->name,
+                                enabled,
+                                parameter_type,
+                                state_type,
+                                state_hint,
+                                state);
+        }
+    }
+
+  return FALSE;
+}
+
+static void
+gtk_widget_class_action_group_change_action_state (GActionGroup *action_group,
+                                                   const gchar  *action_name,
+                                                   GVariant     *value)
+{
+  GtkWidgetClassActionGroup *group = (GtkWidgetClassActionGroup*)action_group;
+  GtkWidgetClass *class = GTK_WIDGET_GET_CLASS (group->widget);
+  GtkWidgetClassPrivate *priv = class->priv;
+  int i;
+
+  for (i = 0; i < priv->actions->len; i++)
+    {
+      GtkWidgetAction *action = g_ptr_array_index (priv->actions, i);
+
+      if (strcmp (action->prefix, group->prefix) == 0 &&
+          strcmp (action->name, action_name) == 0)
+        {
+          action->change (group->widget, action->name, value);
+        }
+    }
+}
+
+static void
+gtk_widget_class_action_group_activate_action (GActionGroup *action_group,
+                                               const  char  *action_name,
+                                               GVariant     *parameter)
+{
+  GtkWidgetClassActionGroup *group = (GtkWidgetClassActionGroup*)action_group;
+  GtkWidgetClass *class = GTK_WIDGET_GET_CLASS (group->widget);
+  GtkWidgetClassPrivate *priv = class->priv;
+  int i;
+
+  for (i = 0; i < priv->actions->len; i++)
+    {
+      GtkWidgetAction *action = g_ptr_array_index (priv->actions, i);
+
+      if (strcmp (action->prefix, group->prefix) == 0 &&
+          strcmp (action->name, action_name) == 0)
+        {
+          action->activate (group->widget, action->name, parameter);
+        }
+    }
+}
+
+static void
+gtk_widget_class_action_group_iface_init (GActionGroupInterface *iface)
+{
+  iface->list_actions = gtk_widget_class_action_group_list_actions;
+  iface->query_action = gtk_widget_class_action_group_query_action;
+  iface->change_action_state = gtk_widget_class_action_group_change_action_state;
+  iface->activate_action = gtk_widget_class_action_group_activate_action;
+}
+
+static void
+gtk_widget_class_action_group_init (GtkWidgetClassActionGroup *group)
+{
+}
+
+static void
+gtk_widget_class_action_group_set_property (GObject      *object,
+                                            guint         prop_id,
+                                            const GValue *value,
+                                            GParamSpec   *pspec)
+{
+  GtkWidgetClassActionGroup *group = (GtkWidgetClassActionGroup *)object;
+
+  switch (prop_id)
+    {
+    case PROP_WIDGET:
+      group->widget = g_value_get_object (value);
+      break;
+
+    case PROP_PREFIX:
+      group->prefix = g_value_dup_string (value);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+gtk_widget_class_action_group_get_property (GObject    *object,
+                                            guint       prop_id,
+                                            GValue     *value,
+                                            GParamSpec *pspec)
+{
+  GtkWidgetClassActionGroup *group = (GtkWidgetClassActionGroup *)object;
+
+  switch (prop_id)
+    {
+    case PROP_WIDGET:
+      g_value_set_object (value, group->widget);
+      break;
+
+    case PROP_PREFIX:
+      g_value_set_string (value, group->prefix);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+static void
+gtk_widget_class_action_group_finalize (GObject *object)
+{
+  GtkWidgetClassActionGroup *group = (GtkWidgetClassActionGroup*)object;
+
+  g_free (group->prefix);
+
+  G_OBJECT_CLASS (gtk_widget_class_action_group_parent_class)->finalize (object);
+}
+
+static void
+gtk_widget_class_action_group_class_init (GtkWidgetClassActionGroupClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->set_property = gtk_widget_class_action_group_set_property;
+  object_class->get_property = gtk_widget_class_action_group_get_property;
+  object_class->finalize = gtk_widget_class_action_group_finalize;
+
+  g_object_class_install_property (object_class, PROP_WIDGET,
+      g_param_spec_object ("widget",
+                           "The widget",
+                           "The widget to which this action group belongs",
+                           GTK_TYPE_WIDGET,
+                           G_PARAM_READWRITE |
+                           G_PARAM_CONSTRUCT_ONLY |
+                           G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_PREFIX,
+      g_param_spec_string ("prefix",
+                           "The prefix",
+                           "The prefix for actions in this group",
+                           NULL,
+                           G_PARAM_READWRITE |
+                           G_PARAM_CONSTRUCT_ONLY |
+                           G_PARAM_STATIC_STRINGS));
+}
+
+static GActionGroup *
+gtk_widget_class_action_group_new (GtkWidget  *widget,
+                                   const char *prefix)
+{
+  return (GActionGroup *)g_object_new (gtk_widget_class_action_group_get_type (),
+                                       "widget", widget,
+                                       "prefix", prefix,
+                                       NULL);
+}
+
+void
+gtk_widget_class_install_action (GtkWidgetClass          *widget_class,
+                                 const char              *prefixed_name,
+                                 GtkWidgetActionActivate  activate,
+                                 GtkWidgetActionQuery     query)
+{
+  GtkWidgetClassPrivate *priv = widget_class->priv;
+  GtkWidgetAction *action;
+  int i;
+  char *p;
+  char *prefix;
+  char *name;
+
+  g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class));
+
+  p = strchr (prefixed_name, '.');
+  if (p == 0)
+    {
+      g_warning ("Action name %s does not contain a '.'", prefixed_name);
+      return;
+    }
+  prefix = g_strndup (prefixed_name, p - prefixed_name);
+  name = g_strdup (p + 1);
+
+  if (priv->actions == NULL)
+    priv->actions = g_ptr_array_new ();
+
+  for (i = 0; i < priv->actions->len; i++)
+    {
+      action = g_ptr_array_index (priv->actions, i);
+
+      if (strcmp (action->prefix, prefix) == 0 &&
+          strcmp (action->name, name) == 0)
+        {
+          g_warning ("Duplicate action name %s.%s", prefix, name);
+          g_free (prefix);
+          g_free (name);
+          return;
+        }
+    }
+
+  action = g_new0 (GtkWidgetAction, 1);
+  action->prefix = prefix;
+  action->name = name;
+  action->activate = activate;
+  action->query = query;
+
+  g_ptr_array_add (priv->actions, action);
+}
+
+void
+gtk_widget_add_class_actions (GtkWidget *widget)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_GET_CLASS (widget);
+  GtkWidgetClassPrivate *priv = widget_class->priv;
+  int i;
+  GHashTable *prefixes;
+
+  if (priv->actions == NULL)
+    {
+      g_warning ("No class actions registered for %s", G_OBJECT_TYPE_NAME (widget));
+      return;
+    }
+
+  prefixes = g_hash_table_new (g_str_hash, g_str_equal);
+
+  for (i = 0; i < priv->actions->len; i++)
+    {
+      GtkWidgetAction *action = g_ptr_array_index (priv->actions, i);
+      GActionGroup *group;
+      const char *prefix = action->prefix;
+
+      if (!g_hash_table_contains (prefixes, prefix))
+        {
+          g_hash_table_add (prefixes, prefix);
+          gtk_widget_insert_action_group (widget, prefix, gtk_widget_class_action_group_new (widget, 
prefix));
+        }
+    }
+
+  g_hash_table_unref (prefixes);
+}
+
+void
+gtk_widget_notify_class_action_enabled (GtkWidget *widget,
+                                        const char *prefixed_name)
+{
+  GActionGroup *group;
+  gboolean enabled;
+  char *p;
+  char *prefix;
+  const char *name;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  group = gtk_widget_get_action_group (widget, prefix);
+
+  p = strchr (prefixed_name, '.');
+  if (p == 0)
+    {
+      g_warning ("Action name %s does not contain a '.'", prefixed_name);
+      return;
+    }
+  prefix = g_strndup (prefixed_name, p - prefixed_name);
+  name = p + 1;
+
+  group = gtk_widget_get_action_group (widget, prefix);
+  g_return_if_fail (group != NULL);
+
+  enabled = g_action_group_get_action_enabled (group, name);
+  g_action_group_action_enabled_changed (group, name, enabled);
+
+  g_free (prefix);
+}
+
+void
+gtk_widget_notify_class_action_state (GtkWidget  *widget,
+                                      const char *prefixed_name)
+{
+  GActionGroup *group;
+  GVariant *state;
+  char *p;
+  char *prefix;
+  const char *name;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  p = strchr (prefixed_name, '.');
+  if (p == 0)
+    {
+      g_warning ("Action name %s does not contain a '.'", prefixed_name);
+      return;
+    }
+  prefix = g_strndup (prefixed_name, p - prefixed_name);
+  name = p + 1;
+
+  group = gtk_widget_get_action_group (widget, prefix);
+
+  g_return_if_fail (group != NULL);
+
+  state = g_action_group_get_action_state (group, name);
+  g_action_group_action_state_changed (group, name, state);
+
+  g_free (prefix);
+}
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index bb2766078b..0863853ba2 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -1030,6 +1030,36 @@ GDK_AVAILABLE_IN_ALL
 gboolean                gtk_widget_should_layout        (GtkWidget   *widget);
 
 
+typedef void (*GtkWidgetActionActivate) (GtkWidget *widget,
+                                         const char *action_name,
+                                         GVariant  *parameter);
+typedef gboolean (* GtkWidgetActionQuery) (GtkWidget *widget,
+                                           const char *action_name,
+                                           gboolean  *enabled,
+                                           const GVariantType **parameter_type,
+                                           const GVariantType **state_type,
+                                           GVariant           **state_hint,
+                                           GVariant           **state);
+typedef void (*GtkWidgetActionChange) (GtkWidget  *widget,
+                                       const char *action_name,
+                                       GVariant   *state);
+
+GDK_AVAILABLE_IN_ALL
+void                    gtk_widget_class_install_action (GtkWidgetClass *widget_class,
+                                                         const char     *prefixed_name,
+                                                         GtkWidgetActionActivate activate,
+                                                         GtkWidgetActionQuery    query);
+
+GDK_AVAILABLE_IN_ALL
+void                    gtk_widget_add_class_actions (GtkWidget *widget);
+GDK_AVAILABLE_IN_ALL
+void                    gtk_widget_notify_class_action_enabled (GtkWidget  *widget,
+                                                                const char *prefixed_name);
+GDK_AVAILABLE_IN_ALL
+void                    gtk_widget_notify_class_action_state (GtkWidget  *widget,
+                                                              const char *prefixed_name);
+
+
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkWidget, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkRequisition, gtk_requisition_free)
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]