[gnome-builder] egg-state-machine: add support for enabling GSimpleActions



commit e76418772bd4259c9afdfb215b3d6ce520db570b
Author: Christian Hergert <christian hergert me>
Date:   Mon May 4 18:44:13 2015 -0700

    egg-state-machine: add support for enabling GSimpleActions
    
    This allows you to pin actions that should be enabled or disabled in a
    given state.
    
    Simply add the action to the state you care about, and set wether or not
    you want enabled to be inverted. Normally, the action will be enabled
    when you enter that state.

 contrib/egg/egg-state-machine.c |  103 ++++++++++++++++++++++++++++++++++++++-
 contrib/egg/egg-state-machine.h |    6 ++-
 tests/test-egg-state-machine.c  |   12 +++++
 3 files changed, 118 insertions(+), 3 deletions(-)
---
diff --git a/contrib/egg/egg-state-machine.c b/contrib/egg/egg-state-machine.c
index cba6d72..da38181 100644
--- a/contrib/egg/egg-state-machine.c
+++ b/contrib/egg/egg-state-machine.c
@@ -29,15 +29,28 @@ typedef struct
   /*
    * Containers for lazily bound signals and bindings.
    *
-   * Each is a GHashTable indexed by state name, containing another
-   * GHashTable indexed by source object.
+   * Each is a GHashTable indexed by state name, containing another GHashTable indexed by
+   * source object.
    */
   GHashTable *binding_sets_by_state;
   GHashTable *signal_groups_by_state;
 
+  /*
+   * Container for actions which should have sensitivity mutated during state transitions.
+   *
+   * GHashTable of GPtrArray of ActionState.
+   */
+  GHashTable *actions_by_state;
+
   gsize       sequence;
 } EggStateMachinePrivate;
 
+typedef struct
+{
+  GSimpleAction *action;
+  guint          invert_enabled : 1;
+} ActionState;
+
 G_DEFINE_TYPE_WITH_PRIVATE (EggStateMachine, egg_state_machine, G_TYPE_OBJECT)
 G_DEFINE_QUARK (EggStateMachineError, egg_state_machine_error)
 
@@ -55,6 +68,18 @@ enum {
 static GParamSpec *gParamSpecs [LAST_PROP];
 static guint gSignals [LAST_SIGNAL];
 
+static void
+action_state_free (gpointer data)
+{
+  ActionState *state = data;
+
+  if (state != NULL)
+    {
+      g_clear_object (&state->action);
+      g_slice_free (ActionState, state);
+    }
+}
+
 static gboolean
 egg_state_transition_accumulator (GSignalInvocationHint *hint,
                                   GValue                *return_value,
@@ -91,7 +116,9 @@ egg_state_machine_do_transition (EggStateMachine *self,
   EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
   GHashTableIter iter;
   const gchar *key;
+  GPtrArray *action_states;
   GHashTable *value;
+  gsize i;
 
   g_assert (EGG_IS_STATE_MACHINE (self));
   g_assert (new_state != NULL);
@@ -140,6 +167,35 @@ egg_state_machine_do_transition (EggStateMachine *self,
           egg_binding_set_set_source (binding_set, enabled ? instance : NULL);
         }
     }
+
+  /* apply GSimpleAction:enabled to non-matching states */
+  g_hash_table_iter_init (&iter, priv->actions_by_state);
+  while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&action_states))
+    {
+      if (g_strcmp0 (key, priv->state) == 0)
+        continue;
+
+      for (i = 0; i < action_states->len; i++)
+        {
+          ActionState *action_state;
+
+          action_state = g_ptr_array_index (action_states, i);
+          g_simple_action_set_enabled (action_state->action, action_state->invert_enabled);
+        }
+    }
+
+  /* apply GSimpleAction:enabled to matching state */
+  action_states = g_hash_table_lookup (priv->actions_by_state, priv->state);
+  if (action_states != NULL)
+    {
+      for (i = 0; i < action_states->len; i++)
+        {
+          ActionState *action_state;
+
+          action_state = g_ptr_array_index (action_states, i);
+          g_simple_action_set_enabled (action_state->action, !action_state->invert_enabled);
+        }
+    }
 }
 
 /**
@@ -222,6 +278,7 @@ egg_state_machine_finalize (GObject *object)
   g_clear_pointer (&priv->state, g_free);
   g_clear_pointer (&priv->binding_sets_by_state, g_hash_table_unref);
   g_clear_pointer (&priv->signal_groups_by_state, g_hash_table_unref);
+  g_clear_pointer (&priv->actions_by_state, g_hash_table_unref);
 
   G_OBJECT_CLASS (egg_state_machine_parent_class)->finalize (object);
 }
@@ -328,6 +385,12 @@ egg_state_machine_init (EggStateMachine *self)
                            g_str_equal,
                            g_free,
                            (GDestroyNotify)g_hash_table_destroy);
+
+  priv->actions_by_state =
+    g_hash_table_new_full (g_str_hash,
+                           g_str_equal,
+                           g_free,
+                           (GDestroyNotify)g_ptr_array_unref);
 }
 
 static void
@@ -491,6 +554,42 @@ egg_state_machine_bind (EggStateMachine *self,
     egg_binding_set_set_source (binding_set, source);
 }
 
+void
+egg_state_machine_add_action (EggStateMachine *self,
+                              const gchar     *state,
+                              GSimpleAction   *action,
+                              gboolean         invert_enabled)
+{
+  EggStateMachinePrivate *priv = egg_state_machine_get_instance_private (self);
+  ActionState *action_state;
+  GPtrArray *actions;
+  gboolean enabled;
+
+  g_return_if_fail (EGG_IS_STATE_MACHINE (self));
+  g_return_if_fail (state != NULL);
+  g_return_if_fail (G_IS_SIMPLE_ACTION (action));
+
+  action_state = g_slice_new0 (ActionState);
+  action_state->action = g_object_ref (action);
+  action_state->invert_enabled = invert_enabled;
+
+  actions = g_hash_table_lookup (priv->actions_by_state, state);
+
+  if (actions == NULL)
+    {
+      actions = g_ptr_array_new_with_free_func (action_state_free);
+      g_hash_table_insert (priv->actions_by_state, g_strdup (state), actions);
+    }
+
+  g_ptr_array_add (actions, action_state);
+
+  enabled = (g_strcmp0 (state, priv->state) == 0);
+  if (invert_enabled)
+    enabled = !enabled;
+
+  g_simple_action_set_enabled (action, enabled);
+}
+
 EggStateMachine *
 egg_state_machine_new (void)
 {
diff --git a/contrib/egg/egg-state-machine.h b/contrib/egg/egg-state-machine.h
index 5b38700..6a8e55c 100644
--- a/contrib/egg/egg-state-machine.h
+++ b/contrib/egg/egg-state-machine.h
@@ -19,7 +19,7 @@
 #ifndef EGG_STATE_MACHINE_H
 #define EGG_STATE_MACHINE_H
 
-#include <glib-object.h>
+#include <gio/gio.h>
 
 G_BEGIN_DECLS
 
@@ -74,6 +74,10 @@ void                egg_state_machine_connect_object (EggStateMachine  *self,
                                                       GCallback         callback,
                                                       gpointer          user_data,
                                                       GConnectFlags     flags);
+void                egg_state_machine_add_action     (EggStateMachine  *self,
+                                                      const gchar      *state,
+                                                      GSimpleAction    *action,
+                                                      gboolean          invert_sensitive);
 
 G_END_DECLS
 
diff --git a/tests/test-egg-state-machine.c b/tests/test-egg-state-machine.c
index b9e01e4..d221503 100644
--- a/tests/test-egg-state-machine.c
+++ b/tests/test-egg-state-machine.c
@@ -207,6 +207,7 @@ test_state_machine_basic (void)
 {
   EggStateMachine *machine;
   EggStateTransition ret;
+  GSimpleAction *action;
   TestObject *dummy;
   TestObject *obj1;
   TestObject *obj2;
@@ -215,6 +216,7 @@ test_state_machine_basic (void)
   machine = egg_state_machine_new ();
   g_object_add_weak_pointer (G_OBJECT (machine), (gpointer *)&machine);
 
+  action = g_simple_action_new ("my-action", NULL);
   dummy = g_object_new (TEST_TYPE_OBJECT, NULL);
   obj1 = g_object_new (TEST_TYPE_OBJECT, NULL);
   obj2 = g_object_new (TEST_TYPE_OBJECT, NULL);
@@ -231,14 +233,20 @@ test_state_machine_basic (void)
   egg_state_machine_bind (machine, "state1", obj1, "string", dummy, "string", 0);
   egg_state_machine_bind (machine, "state2", obj2, "string", dummy, "string", 0);
 
+  egg_state_machine_add_action (machine, "state1", action, FALSE);
+
   g_signal_connect (machine, "transition", G_CALLBACK (transition_cb), NULL);
 
+  g_assert_cmpint (g_action_get_enabled (G_ACTION (action)), ==, FALSE);
+
   ret = egg_state_machine_transition (machine, "state1", &error);
   g_assert_no_error (error);
   g_assert_cmpint (ret, ==, EGG_STATE_TRANSITION_SUCCESS);
   g_assert_cmpint (dummy->obj1_count, ==, 0);
   g_assert_cmpint (dummy->obj2_count, ==, 0);
 
+  g_assert_cmpint (g_action_get_enabled (G_ACTION (action)), ==, TRUE);
+
   g_signal_emit_by_name (obj1, "frobnicate");
   g_assert_cmpint (dummy->obj1_count, ==, 1);
   g_assert_cmpint (dummy->obj2_count, ==, 0);
@@ -251,6 +259,8 @@ test_state_machine_basic (void)
   g_assert_no_error (error);
   g_assert_cmpint (ret, ==, EGG_STATE_TRANSITION_SUCCESS);
 
+  g_assert_cmpint (g_action_get_enabled (G_ACTION (action)), ==, FALSE);
+
   g_signal_emit_by_name (obj1, "frobnicate");
   g_assert_cmpint (dummy->obj1_count, ==, 1);
   g_assert_cmpint (dummy->obj2_count, ==, 0);
@@ -287,6 +297,8 @@ test_state_machine_basic (void)
 
   g_object_unref (machine);
   g_assert (machine == NULL);
+
+  g_clear_object (&action);
 }
 
 gint


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