[gnome-builder] libide/core: add IdeActionMixin



commit ea86dcb7c324cd29f492f6484f346168f58bf1dc
Author: Christian Hergert <chergert redhat com>
Date:   Thu Jul 28 00:09:18 2022 -0700

    libide/core: add IdeActionMixin
    
    The goal with this is to make it a lot easier to do actions from classes
    even if they aren't an IdeActionGroup along with being able to possibly
    swtich from IDE_DEFINE_ACION_GROUP() to wrapping the mixin if really
    necessary to be an action group.

 src/libide/core/ide-action-muxer.c | 198 +++++++++++++++++++++++++++++++++++++
 src/libide/core/ide-action-muxer.h |  72 +++++++++-----
 2 files changed, 248 insertions(+), 22 deletions(-)
---
diff --git a/src/libide/core/ide-action-muxer.c b/src/libide/core/ide-action-muxer.c
index b3e60b8b8..42ac2b879 100644
--- a/src/libide/core/ide-action-muxer.c
+++ b/src/libide/core/ide-action-muxer.c
@@ -50,6 +50,8 @@ static void action_group_iface_init (GActionGroupInterface *iface);
 G_DEFINE_FINAL_TYPE_WITH_CODE (IdeActionMuxer, ide_action_muxer, G_TYPE_OBJECT,
                                G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, action_group_iface_init))
 
+static GQuark mixin_quark;
+
 static void
 prefixed_action_group_finalize (gpointer data)
 {
@@ -866,3 +868,199 @@ ide_action_muxer_connect_actions (IdeActionMuxer  *self,
         ide_action_muxer_add_action (self, instance, iter);
     }
 }
+
+void
+ide_action_mixin_init (IdeActionMixin *mixin,
+                       GObjectClass   *object_class)
+{
+  g_return_if_fail (mixin != NULL);
+  g_return_if_fail (G_IS_OBJECT_CLASS (object_class));
+
+  if (!mixin_quark)
+    mixin_quark = g_quark_from_static_string ("ide-action-mixin");
+
+  mixin->object_class = object_class;
+}
+
+/**
+ * ide_action_mixin_install_action: (skip)
+ * @mixin: an `IdeActionMixin`
+ * @action_name: a prefixed action name, such as "clipboard.paste"
+ * @parameter_type: (nullable): the parameter type
+ * @activate: (scope notified): callback to use when the action is activated
+ *
+ * This should be called at class initialization time to specify
+ * actions to be added for all instances of this class.
+ *
+ * Actions installed by this function are stateless. The only state
+ * they have is whether they are enabled or not.
+ */
+void
+ide_action_mixin_install_action (IdeActionMixin        *mixin,
+                                 const char            *action_name,
+                                 const char            *parameter_type,
+                                 IdeActionActivateFunc  activate)
+{
+  IdeAction *action;
+
+  g_return_if_fail (mixin != NULL);
+  g_return_if_fail (G_IS_OBJECT_CLASS (mixin->object_class));
+
+  action = g_new0 (IdeAction, 1);
+  action->owner = G_OBJECT_CLASS_TYPE (mixin->object_class);
+  action->name = g_intern_string (action_name);
+  if (parameter_type != NULL)
+    action->parameter_type = g_variant_type_new (parameter_type);
+  action->activate = (IdeActionActivateFunc)activate;
+  action->position = ++mixin->n_actions;
+  action->next = mixin->actions;
+  mixin->actions = action;
+}
+
+static const GVariantType *
+determine_type (GParamSpec *pspec)
+{
+  if (G_TYPE_IS_ENUM (pspec->value_type))
+    return G_VARIANT_TYPE_STRING;
+
+  switch (pspec->value_type)
+    {
+    case G_TYPE_BOOLEAN:
+      return G_VARIANT_TYPE_BOOLEAN;
+
+    case G_TYPE_INT:
+      return G_VARIANT_TYPE_INT32;
+
+    case G_TYPE_UINT:
+      return G_VARIANT_TYPE_UINT32;
+
+    case G_TYPE_DOUBLE:
+    case G_TYPE_FLOAT:
+      return G_VARIANT_TYPE_DOUBLE;
+
+    case G_TYPE_STRING:
+      return G_VARIANT_TYPE_STRING;
+
+    default:
+      g_critical ("Unable to use ide_action_mixin_install_property_action with property '%s:%s' of type 
'%s'",
+                  g_type_name (pspec->owner_type), pspec->name, g_type_name (pspec->value_type));
+      return NULL;
+    }
+}
+
+/**
+ * ide_action_mixin_install_property_action: (skip)
+ * @mixin: an `IdeActionMixin`
+ * @action_name: name of the action
+ * @property_name: name of the property in instances of @mixin
+ *   or any parent class.
+ *
+ * Installs an action called @action_name on @mmixin and
+ * binds its state to the value of the @property_name property.
+ *
+ * This function will perform a few santity checks on the property selected
+ * via @property_name. Namely, the property must exist, must be readable,
+ * writable and must not be construct-only. There are also restrictions
+ * on the type of the given property, it must be boolean, int, unsigned int,
+ * double or string. If any of these conditions are not met, a critical
+ * warning will be printed and no action will be added.
+ *
+ * The state type of the action matches the property type.
+ *
+ * If the property is boolean, the action will have no parameter and
+ * toggle the property value. Otherwise, the action will have a parameter
+ * of the same type as the property.
+ */
+void
+ide_action_mixin_install_property_action (IdeActionMixin *mixin,
+                                          const char     *action_name,
+                                          const char     *property_name)
+{
+  const GVariantType *state_type;
+  IdeAction *action;
+  GParamSpec *pspec;
+
+  g_return_if_fail (mixin != NULL);
+  g_return_if_fail (action_name != NULL);
+  g_return_if_fail (property_name != NULL);
+  g_return_if_fail (G_IS_OBJECT_CLASS (mixin->object_class));
+
+  if (!(pspec = g_object_class_find_property (mixin->object_class, property_name)))
+    {
+      g_critical ("Attempted to use non-existent property '%s:%s' for 
ide_action_mixin_install_property_action",
+                  G_OBJECT_CLASS_NAME (mixin->object_class), property_name);
+      return;
+    }
+
+  if (~pspec->flags & G_PARAM_READABLE || ~pspec->flags & G_PARAM_WRITABLE || pspec->flags & 
G_PARAM_CONSTRUCT_ONLY)
+    {
+      g_critical ("Property '%s:%s' used with ide_action_mixin_install_property_action must be readable, 
writable, and not construct-only",
+                  G_OBJECT_CLASS_NAME (mixin->object_class), property_name);
+      return;
+    }
+
+  state_type = determine_type (pspec);
+
+  if (!state_type)
+    return;
+
+  action = g_new0 (IdeAction, 1);
+  action->owner = G_TYPE_FROM_CLASS (mixin->object_class);
+  action->name = g_intern_string (action_name);
+  action->pspec = pspec;
+  action->state_type = state_type;
+  if (action->pspec->value_type != G_TYPE_BOOLEAN)
+    action->parameter_type = action->state_type;
+  action->position = ++mixin->n_actions;
+  action->next = mixin->actions;
+
+  mixin->actions = action;
+}
+
+/**
+ * ide_action_muxer_get_action_muxer: (skip)
+ * @self: a #IdeActionMuxer
+ *
+ * Returns: (transfer none) (nullable): an #IdeActionMuxer or %NULL
+ */
+IdeActionMuxer *
+ide_action_mixin_get_action_muxer (gpointer instance)
+{
+  return g_object_get_qdata (instance, mixin_quark);
+}
+
+void
+ide_action_mixin_set_enabled (gpointer    instance,
+                              const char *action_name,
+                              gboolean    enabled)
+{
+  IdeActionMuxer *muxer;
+
+  g_return_if_fail (G_IS_OBJECT (instance));
+  g_return_if_fail (action_name != NULL);
+
+  muxer = ide_action_mixin_get_action_muxer (instance);
+
+  for (const IdeAction *iter = muxer->actions; iter; iter = iter->next)
+    {
+      if (g_strcmp0 (iter->name, action_name) == 0)
+        {
+          ide_action_muxer_set_enabled (muxer, iter, enabled);
+          break;
+        }
+    }
+}
+
+void
+ide_action_mixin_constructed (const IdeActionMixin *mixin,
+                              gpointer              instance)
+{
+  IdeActionMuxer *muxer;
+
+  g_return_if_fail (mixin != NULL);
+  g_return_if_fail (G_IS_OBJECT (instance));
+
+  muxer = ide_action_muxer_new ();
+  g_object_set_qdata_full (instance, mixin_quark, muxer, g_object_unref);
+  ide_action_muxer_connect_actions (muxer, instance, mixin->actions);
+}
diff --git a/src/libide/core/ide-action-muxer.h b/src/libide/core/ide-action-muxer.h
index b7a258488..00b14f438 100644
--- a/src/libide/core/ide-action-muxer.h
+++ b/src/libide/core/ide-action-muxer.h
@@ -37,43 +37,71 @@ typedef void (*IdeActionActivateFunc) (gpointer    instance,
 typedef struct _IdeAction
 {
   const struct _IdeAction *next;
-  const char                *name;
-  GType                      owner;
-  const GVariantType        *parameter_type;
-  const GVariantType        *state_type;
-  GParamSpec                *pspec;
+  const char              *name;
+  GType                    owner;
+  const GVariantType      *parameter_type;
+  const GVariantType      *state_type;
+  GParamSpec              *pspec;
   IdeActionActivateFunc    activate;
-  guint                      position;
+  guint                    position;
 } IdeAction;
 
+typedef struct _IdeActionMixin
+{
+  GObjectClass    *object_class;
+  const IdeAction *actions;
+  guint            n_actions;
+} IdeActionMixin;
+
 #define IDE_TYPE_ACTION_MUXER (ide_action_muxer_get_type())
 
 IDE_AVAILABLE_IN_ALL
 G_DECLARE_FINAL_TYPE (IdeActionMuxer, ide_action_muxer, IDE, ACTION_MUXER, GObject)
 
 IDE_AVAILABLE_IN_ALL
-IdeActionMuxer  *ide_action_muxer_new                 (void);
+IdeActionMuxer  *ide_action_mixin_get_action_muxer        (gpointer               instance);
+IDE_AVAILABLE_IN_ALL
+void             ide_action_mixin_init                    (IdeActionMixin        *mixin,
+                                                           GObjectClass          *object_class);
+IDE_AVAILABLE_IN_ALL
+void             ide_action_mixin_constructed             (const IdeActionMixin  *mixin,
+                                                           gpointer               instance);
+IDE_AVAILABLE_IN_ALL
+void             ide_action_mixin_set_enabled             (gpointer               instance,
+                                                           const char            *action,
+                                                           gboolean               enabled);
+IDE_AVAILABLE_IN_ALL
+void             ide_action_mixin_install_action          (IdeActionMixin        *mixin,
+                                                           const char            *action_name,
+                                                           const char            *parameter_type,
+                                                           IdeActionActivateFunc  activate);
+IDE_AVAILABLE_IN_ALL
+void             ide_action_mixin_install_property_action (IdeActionMixin        *mixin,
+                                                           const char            *action_name,
+                                                           const char            *property_name);
+IDE_AVAILABLE_IN_ALL
+IdeActionMuxer  *ide_action_muxer_new                     (void);
 IDE_AVAILABLE_IN_ALL
-void             ide_action_muxer_remove_all          (IdeActionMuxer  *self);
+void             ide_action_muxer_remove_all              (IdeActionMuxer        *self);
 IDE_AVAILABLE_IN_ALL
-void             ide_action_muxer_insert_action_group (IdeActionMuxer  *self,
-                                                       const char      *prefix,
-                                                       GActionGroup    *action_group);
+void             ide_action_muxer_insert_action_group     (IdeActionMuxer        *self,
+                                                           const char            *prefix,
+                                                           GActionGroup          *action_group);
 IDE_AVAILABLE_IN_ALL
-void             ide_action_muxer_remove_action_group (IdeActionMuxer  *self,
-                                                       const char      *prefix);
+void             ide_action_muxer_remove_action_group     (IdeActionMuxer        *self,
+                                                           const char            *prefix);
 IDE_AVAILABLE_IN_ALL
-char           **ide_action_muxer_list_groups         (IdeActionMuxer  *self);
+char           **ide_action_muxer_list_groups             (IdeActionMuxer        *self);
 IDE_AVAILABLE_IN_ALL
-GActionGroup    *ide_action_muxer_get_action_group    (IdeActionMuxer  *self,
-                                                       const char      *prefix);
+GActionGroup    *ide_action_muxer_get_action_group        (IdeActionMuxer        *self,
+                                                           const char            *prefix);
 IDE_AVAILABLE_IN_ALL
-void             ide_action_muxer_set_enabled         (IdeActionMuxer  *self,
-                                                       const IdeAction *action,
-                                                       gboolean         enabled);
+void             ide_action_muxer_set_enabled             (IdeActionMuxer        *self,
+                                                           const IdeAction       *action,
+                                                           gboolean               enabled);
 IDE_AVAILABLE_IN_ALL
-void             ide_action_muxer_connect_actions     (IdeActionMuxer  *self,
-                                                       gpointer         instance,
-                                                       const IdeAction *actions);
+void             ide_action_muxer_connect_actions         (IdeActionMuxer        *self,
+                                                           gpointer               instance,
+                                                           const IdeAction       *actions);
 
 G_END_DECLS


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