[gtk+] switch: Add a delayed state capability



commit 7b11f761b310a0ea29ac8d28d7236c5101468ee6
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Mar 29 13:40:08 2014 -0400

    switch: Add a delayed state capability
    
    This commit makes it possible for GtkSwitch to indicate when
    the underlying state changes with a delay, causing the switch
    to temporarily go 'out of sync' with the underlying change.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=725648

 gtk/gtkswitch.c |  154 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 gtk/gtkswitch.h |    8 +++-
 2 files changed, 149 insertions(+), 13 deletions(-)
---
diff --git a/gtk/gtkswitch.c b/gtk/gtkswitch.c
index 354e9e5..72599dd 100644
--- a/gtk/gtkswitch.c
+++ b/gtk/gtkswitch.c
@@ -32,6 +32,9 @@
  * #GtkSwitch is a widget that has two states: on or off. The user can control
  * which state should be active by clicking the empty area, or by dragging the
  * handle.
+ *
+ * GtkSwitch can also handle situations where the underlying state changes with
+ * a delay. See #GtkSwitch::state-set for details.
  */
 
 #include "config.h"
@@ -64,6 +67,7 @@ struct _GtkSwitchPrivate
   gint drag_start;
   gint drag_threshold;
 
+  guint state                 : 1;
   guint is_active             : 1;
   guint is_dragging           : 1;
   guint in_press              : 1;
@@ -75,6 +79,7 @@ enum
 {
   PROP_0,
   PROP_ACTIVE,
+  PROP_STATE,
   PROP_RELATED_ACTION,
   PROP_USE_ACTION_APPEARANCE,
   LAST_PROP,
@@ -85,6 +90,7 @@ enum
 enum
 {
   ACTIVATE,
+  STATE_SET,
   LAST_SIGNAL
 };
 
@@ -729,6 +735,10 @@ gtk_switch_set_property (GObject      *gobject,
       gtk_switch_set_active (sw, g_value_get_boolean (value));
       break;
 
+    case PROP_STATE:
+      gtk_switch_set_state (sw, g_value_get_boolean (value));
+      break;
+
     case PROP_RELATED_ACTION:
       gtk_switch_set_related_action (sw, g_value_get_object (value));
       break;
@@ -764,6 +774,10 @@ gtk_switch_get_property (GObject    *gobject,
       g_value_set_boolean (value, priv->is_active);
       break;
 
+    case PROP_STATE:
+      g_value_set_boolean (value, priv->state);
+      break;
+
     case PROP_RELATED_ACTION:
       g_value_set_object (value, priv->action);
       break;
@@ -803,6 +817,22 @@ gtk_switch_dispose (GObject *object)
   G_OBJECT_CLASS (gtk_switch_parent_class)->dispose (object);
 }
 
+static gboolean
+state_set (GtkSwitch *sw, gboolean state)
+{
+  if (sw->priv->action_helper)
+    gtk_action_helper_activate (sw->priv->action_helper);
+
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+  if (sw->priv->action)
+    gtk_action_activate (sw->priv->action);
+  G_GNUC_END_IGNORE_DEPRECATIONS;
+
+  gtk_switch_set_state (sw, state);
+
+  return TRUE;
+}
+
 static void
 gtk_switch_class_init (GtkSwitchClass *klass)
 {
@@ -836,6 +866,21 @@ gtk_switch_class_init (GtkSwitchClass *klass)
                           FALSE,
                           GTK_PARAM_READWRITE);
 
+  /**
+   * GtkSwitch:state:
+   *
+   * The backend state that is controlled by the switch. 
+   * See #GtkSwitch::state-set for details.
+   *
+   * Since: 3.14
+   */
+  switch_props[PROP_STATE] =
+    g_param_spec_boolean ("state",
+                          P_("State"),
+                          P_("The backend state"),
+                          FALSE,
+                          GTK_PARAM_READWRITE);
+
   gobject_class->set_property = gtk_switch_set_property;
   gobject_class->get_property = gtk_switch_get_property;
   gobject_class->dispose = gtk_switch_dispose;
@@ -857,6 +902,7 @@ gtk_switch_class_init (GtkSwitchClass *klass)
   widget_class->leave_notify_event = gtk_switch_leave;
 
   klass->activate = gtk_switch_activate;
+  klass->state_set = state_set;
 
   /**
    * GtkSwitch:slider-width:
@@ -890,6 +936,38 @@ gtk_switch_class_init (GtkSwitchClass *klass)
                   G_TYPE_NONE, 0);
   widget_class->activate_signal = signals[ACTIVATE];
 
+  /**
+   * GtkSwitch::state-set:
+   * @widget: the object on which the signal was emitted
+   *
+   * The ::state-set signal on GtkSwitch is emitted to change the underlying
+   * state. It is emitted when the user changes the switch position. The
+   * default handler keeps the state in sync with the #GtkState:active
+   * property.
+   *
+   * To implement delayed state change, applications can connect to this signal,
+   * initiate the change of the underlying state, and call gtk_switch_set_state()
+   * when the underlying state change is complete. The signal handler should
+   * return %TRUE to prevent the default handler from running.
+   *
+   * Visually, the underlying state is represented by the trough color of
+   * the switch, while the #GtkSwitch:active property is represented by the
+   * position of the switch.
+   *
+   * Returns: %TRUE to stop the signal emission
+   *
+   * Since: 3.14
+   */
+  signals[STATE_SET] =
+    g_signal_new (I_("state-set"),
+                  G_OBJECT_CLASS_TYPE (gobject_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GtkSwitchClass, state_set),
+                  _gtk_boolean_handled_accumulator, NULL,
+                  _gtk_marshal_BOOLEAN__BOOLEAN,
+                  G_TYPE_BOOLEAN, 1,
+                  G_TYPE_BOOLEAN);
+
   g_object_class_override_property (gobject_class, PROP_ACTION_NAME, "action-name");
   g_object_class_override_property (gobject_class, PROP_ACTION_TARGET, "action-target");
 
@@ -945,27 +1023,17 @@ gtk_switch_set_active (GtkSwitch *sw,
   if (priv->is_active != is_active)
     {
       AtkObject *accessible;
+      gboolean handled;
 
       priv->is_active = is_active;
 
       g_object_notify_by_pspec (G_OBJECT (sw), switch_props[PROP_ACTIVE]);
 
-      if (priv->action_helper)
-        gtk_action_helper_activate (priv->action_helper);
-
-      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
-      if (priv->action)
-        gtk_action_activate (priv->action);
-      G_GNUC_END_IGNORE_DEPRECATIONS;
+      g_signal_emit (sw, signals[STATE_SET], 0, is_active, &handled);
 
       accessible = gtk_widget_get_accessible (GTK_WIDGET (sw));
       atk_object_notify_state_change (accessible, ATK_STATE_CHECKED, priv->is_active);
 
-      if (priv->is_active)
-        gtk_widget_set_state_flags (GTK_WIDGET (sw), GTK_STATE_FLAG_ACTIVE, FALSE);
-      else
-        gtk_widget_unset_state_flags (GTK_WIDGET (sw), GTK_STATE_FLAG_ACTIVE);
-
       gtk_widget_queue_draw (GTK_WIDGET (sw));
     }
 }
@@ -988,6 +1056,68 @@ gtk_switch_get_active (GtkSwitch *sw)
   return sw->priv->is_active;
 }
 
+/**
+ * gtk_switch_set_state:
+ * @sw: a #GtkSwitch
+ * @state: the new state
+ *
+ * Sets the underlying state of the #GtkSwitch.
+ *
+ * Normally, this is the same as #GtkSwitch:active, unless the switch
+ * is set up for delayed state changes. This function is typically
+ * called from a #GtkSwitch::state-set signal handler.
+ *
+ * See #GtkSwitch::state-set for details.
+ *
+ * Since: 3.14
+ */
+void
+gtk_switch_set_state (GtkSwitch *sw,
+                      gboolean   state)
+{
+  g_return_if_fail (GTK_IS_SWITCH (sw));
+
+  state = state != FALSE;
+
+  if (sw->priv->state == state)
+    return;
+
+  sw->priv->state = state;
+
+  /* This will be a no-op if we're switching the state in response
+   * to a UI change. We're setting active anyway, to catch 'spontaneous'
+   * state changes.
+   */
+  gtk_switch_set_active (sw, state);
+
+  if (state)
+    gtk_widget_set_state_flags (GTK_WIDGET (sw), GTK_STATE_FLAG_ACTIVE, FALSE);
+  else
+    gtk_widget_unset_state_flags (GTK_WIDGET (sw), GTK_STATE_FLAG_ACTIVE);
+
+  g_object_notify (G_OBJECT (sw), "state");
+
+  gtk_widget_queue_draw (GTK_WIDGET (sw));
+}
+
+/**
+ * gtk_switch_get_state:
+ * @sw: a #GtkSwitch
+ *
+ * Gets the underlying state of the #GtkSwitch.
+ *
+ * Returns: the underlying state
+ *
+ * Since: 3.14
+ */
+gboolean
+gtk_switch_get_state (GtkSwitch *sw)
+{
+  g_return_val_if_fail (GTK_IS_SWITCH (sw), FALSE);
+
+  return sw->priv->state;
+}
+
 static void
 gtk_switch_update (GtkActivatable *activatable,
                    GtkAction      *action,
diff --git a/gtk/gtkswitch.h b/gtk/gtkswitch.h
index 70cfd97..0e561f8 100644
--- a/gtk/gtkswitch.h
+++ b/gtk/gtkswitch.h
@@ -72,6 +72,7 @@ struct _GtkSwitchClass
 
   void (* activate) (GtkSwitch *sw);
 
+  gboolean (* state_set) (GtkSwitch *sw, gboolean state);
   /*< private >*/
 
   void (* _switch_padding_1) (void);
@@ -79,7 +80,6 @@ struct _GtkSwitchClass
   void (* _switch_padding_3) (void);
   void (* _switch_padding_4) (void);
   void (* _switch_padding_5) (void);
-  void (* _switch_padding_6) (void);
 };
 
 GDK_AVAILABLE_IN_ALL
@@ -94,6 +94,12 @@ void            gtk_switch_set_active   (GtkSwitch *sw,
 GDK_AVAILABLE_IN_ALL
 gboolean        gtk_switch_get_active   (GtkSwitch *sw);
 
+GDK_AVAILABLE_IN_3_14
+void            gtk_switch_set_state   (GtkSwitch *sw,
+                                        gboolean   state);
+GDK_AVAILABLE_IN_3_14
+gboolean        gtk_switch_get_state   (GtkSwitch *sw);
+
 G_END_DECLS
 
 #endif /* __GTK_SWITCH_H__ */


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