[gnome-shell] [StWidget] add (optional) hover tracking



commit f9e4385e02fc55aac5317f68641d4095d90246d6
Author: Dan Winship <danw gnome org>
Date:   Fri Mar 19 13:47:34 2010 -0400

    [StWidget] add (optional) hover tracking
    
    If track-hover is set, update the hover property automatically, and
    the "hover" pseudo class to match, as StClickable used to do. (Remove
    the corresponding code in StClickable). Tweak the tooltip handling to
    use track-hover, which also makes it slightly more reliable in the
    presence of reactive children, etc.

 js/ui/appDisplay.js     |    2 +
 js/ui/workspacesView.js |    5 +-
 src/st/st-clickable.c   |   65 +++----------
 src/st/st-widget.c      |  244 ++++++++++++++++++++++++++++++++++++++++++++---
 src/st/st-widget.h      |    8 ++
 5 files changed, 257 insertions(+), 67 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 9ec427f..a8f878e 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -536,6 +536,8 @@ AppWellIcon.prototype = {
     },
 
     _onMenuPoppedDown: function() {
+        this.actor.sync_hover();
+
         if (this._didActivateWindow)
             return;
         if (!this._setWindowSelection)
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index 5712509..f8d92df 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -484,10 +484,7 @@ NewWorkspaceArea.prototype = {
     },
 
     setStyle: function(isHover) {
-        if (isHover)
-            this._child1.add_style_pseudo_class('hover');
-        else
-            this._child1.remove_style_pseudo_class('hover');
+        this._child1.set_hover(isHover);
     }
 };
 
diff --git a/src/st/st-clickable.c b/src/st/st-clickable.c
index ee52e37..2ff8758 100644
--- a/src/st/st-clickable.c
+++ b/src/st/st-clickable.c
@@ -16,7 +16,6 @@ G_DEFINE_TYPE (StClickable, st_clickable, ST_TYPE_BIN);
 struct _StClickablePrivate {
   gboolean active;
   gboolean held;
-  gboolean hover;
   gboolean pressed;
 
   guint initiating_button;
@@ -33,7 +32,6 @@ enum {
   PROP_0,
 
   PROP_ACTIVE,
-  PROP_HOVER,
   PROP_PRESSED,
 };
 
@@ -46,11 +44,6 @@ sync_pseudo_class (StClickable *self)
     st_widget_add_style_pseudo_class (ST_WIDGET (self), "pressed");
   else
     st_widget_remove_style_pseudo_class (ST_WIDGET (self), "pressed");
-
-  if (self->priv->hover)
-    st_widget_add_style_pseudo_class (ST_WIDGET (self), "hover");
-  else
-    st_widget_remove_style_pseudo_class (ST_WIDGET (self), "hover");
 }
 
 static void
@@ -65,17 +58,6 @@ set_active (StClickable  *self,
 }
 
 static void
-set_hover (StClickable  *self,
-           gboolean      hover)
-{
-  if (self->priv->hover == hover)
-    return;
-  self->priv->hover = hover;
-  sync_pseudo_class (self);
-  g_object_notify (G_OBJECT (self), "hover");
-}
-
-static void
 set_pressed (StClickable  *self,
              gboolean      pressed)
 {
@@ -102,21 +84,18 @@ st_clickable_enter_event (ClutterActor         *actor,
                           ClutterCrossingEvent *event)
 {
   StClickable *self = ST_CLICKABLE (actor);
-
-  if (st_clickable_contains (self, event->related))
-    return TRUE;
-  if (!st_clickable_contains (self, event->source))
-    return TRUE;
+  gboolean result;
 
   g_object_freeze_notify (G_OBJECT (actor));
 
-  if (self->priv->held)
-    set_pressed (self, TRUE);
-  set_hover (self, TRUE);
+  result = CLUTTER_ACTOR_CLASS (st_clickable_parent_class)->enter_event (actor, event);
+
+  /* We can't just assume get_hover() is TRUE; see st_widget_enter(). */
+  set_pressed (self, self->priv->held && st_widget_get_hover (ST_WIDGET (actor)));
 
   g_object_thaw_notify (G_OBJECT (actor));
 
-  return TRUE;
+  return result;
 }
 
 static gboolean
@@ -124,14 +103,18 @@ st_clickable_leave_event (ClutterActor         *actor,
                               ClutterCrossingEvent *event)
 {
   StClickable *self = ST_CLICKABLE (actor);
+  gboolean result;
 
-  if (st_clickable_contains (self, event->related))
-    return TRUE;
+  g_object_freeze_notify (G_OBJECT (actor));
 
-  set_hover (self, FALSE);
-  set_pressed (self, FALSE);
+  result = CLUTTER_ACTOR_CLASS (st_clickable_parent_class)->leave_event (actor, event);
 
-  return TRUE;
+  /* As above, we can't just assume get_hover() is FALSE. */
+  set_pressed (self, self->priv->held && st_widget_get_hover (ST_WIDGET (actor)));
+
+  g_object_thaw_notify (G_OBJECT (actor));
+
+  return result;
 }
 
 static gboolean
@@ -243,9 +226,6 @@ st_clickable_get_property (GObject         *object,
     case PROP_PRESSED:
       g_value_set_boolean (value, self->priv->pressed);
       break;
-    case PROP_HOVER:
-      g_value_set_boolean (value, self->priv->hover);
-      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -298,20 +278,6 @@ st_clickable_class_init (StClickableClass *klass)
                                                          G_PARAM_READWRITE));
 
   /**
-   * StClickable:hover
-   *
-   * This property tracks whether the mouse is over the button; note this
-   * state is independent of whether the button is pressed.
-   */
-  g_object_class_install_property (gobject_class,
-                                   PROP_HOVER,
-                                   g_param_spec_boolean ("hover",
-                                                         "Hovering state",
-                                                         "Whether the mouse is over the button",
-                                                         FALSE,
-                                                         G_PARAM_READABLE));
-
-  /**
    * StClickable:pressed
    *
    * This property tracks whether the button should have a "pressed in"
@@ -333,4 +299,5 @@ st_clickable_init (StClickable *self)
 {
   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ST_TYPE_CLICKABLE,
                                             StClickablePrivate);
+  st_widget_set_track_hover (ST_WIDGET (self), TRUE);
 }
diff --git a/src/st/st-widget.c b/src/st/st-widget.c
index 8a3b034..01bee64 100644
--- a/src/st/st-widget.c
+++ b/src/st/st-widget.c
@@ -76,6 +76,8 @@ struct _StWidgetPrivate
   gboolean      is_style_dirty : 1;
   gboolean      draw_bg_color : 1;
   gboolean      draw_border_internal : 1;
+  gboolean      track_hover : 1;
+  gboolean      hover : 1;
 
   StTooltip    *tooltip;
 
@@ -101,11 +103,11 @@ enum
   PROP_PSEUDO_CLASS,
   PROP_STYLE_CLASS,
   PROP_STYLE,
-
   PROP_STYLABLE,
-
   PROP_HAS_TOOLTIP,
-  PROP_TOOLTIP_TEXT
+  PROP_TOOLTIP_TEXT,
+  PROP_TRACK_HOVER,
+  PROP_HOVER
 };
 
 enum
@@ -167,6 +169,14 @@ st_widget_set_property (GObject      *gobject,
       st_widget_set_tooltip_text (actor, g_value_get_string (value));
       break;
 
+    case PROP_TRACK_HOVER:
+      st_widget_set_track_hover (actor, g_value_get_boolean (value));
+      break;
+
+    case PROP_HOVER:
+      st_widget_set_hover (actor, g_value_get_boolean (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
       break;
@@ -212,6 +222,14 @@ st_widget_get_property (GObject    *gobject,
       g_value_set_string (value, st_widget_get_tooltip_text (actor));
       break;
 
+    case PROP_TRACK_HOVER:
+      g_value_set_boolean (value, priv->track_hover);
+      break;
+
+    case PROP_HOVER:
+      g_value_set_boolean (value, priv->hover);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
       break;
@@ -1126,14 +1144,33 @@ st_widget_get_theme_node (StWidget *widget)
 }
 
 static gboolean
+actor_contains (ClutterActor *widget,
+                ClutterActor *other)
+{
+  while (other != NULL && other != widget)
+    other = clutter_actor_get_parent (other);
+  return other != NULL;
+}
+
+static gboolean
 st_widget_enter (ClutterActor         *actor,
                  ClutterCrossingEvent *event)
 {
   StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 
-
-  if (priv->has_tooltip)
-    st_widget_show_tooltip ((StWidget*) actor);
+  if (priv->track_hover)
+    {
+      if (actor_contains (actor, event->source))
+        st_widget_set_hover (ST_WIDGET (actor), TRUE);
+      else
+        {
+          /* The widget has a grab and is being told about an
+           * enter-event outside its hierarchy. Hopefully we already
+           * got a leave-event, but if not, handle it now.
+           */
+          st_widget_set_hover (ST_WIDGET (actor), FALSE);
+        }
+    }
 
   if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event)
     return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event (actor, event);
@@ -1147,8 +1184,11 @@ st_widget_leave (ClutterActor         *actor,
 {
   StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 
-  if (priv->has_tooltip)
-    st_tooltip_hide (priv->tooltip);
+  if (priv->track_hover)
+    {
+      if (!actor_contains (actor, event->related))
+        st_widget_set_hover (ST_WIDGET (actor), FALSE);
+    }
 
   if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event)
     return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event (actor, event);
@@ -1267,8 +1307,9 @@ st_widget_class_init (StWidgetClass *klass)
   /**
    * StWidget:has-tooltip:
    *
-   * Determines whether the widget has a tooltip. If set to TRUE, causes the
-   * widget to monitor enter and leave events (i.e. sets the widget reactive).
+   * Determines whether the widget has a tooltip. If set to %TRUE, causes the
+   * widget to monitor hover state (i.e. sets #ClutterActor:reactive and
+   * #StWidget:track-hover).
    */
   pspec = g_param_spec_boolean ("has-tooltip",
                                 "Has Tooltip",
@@ -1293,6 +1334,40 @@ st_widget_class_init (StWidgetClass *klass)
   g_object_class_install_property (gobject_class, PROP_TOOLTIP_TEXT, pspec);
 
   /**
+   * StWidget:track-hover:
+   *
+   * Determines whether the widget tracks pointer hover state. If
+   * %TRUE (and the widget is visible and reactive), the
+   * #StWidget:hover property and "hover" style pseudo class will be
+   * adjusted automatically as the pointer moves in and out of the
+   * widget.
+   */
+  pspec = g_param_spec_boolean ("track-hover",
+                                "Track hover",
+                                "Determines whether the widget tracks hover state",
+                                FALSE,
+                                ST_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class,
+                                   PROP_TRACK_HOVER,
+                                   pspec);
+
+  /**
+   * StWidget:hover:
+   *
+   * Whether or not the pointer is currently hovering over the widget. This is
+   * only tracked automatically if #StWidget:track-hover is %TRUE, but you can
+   * adjust it manually in any case.
+   */
+  pspec = g_param_spec_boolean ("hover",
+                                "Hover",
+                                "Whether the pointer is hovering over the widget",
+                                FALSE,
+                                ST_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class,
+                                   PROP_HOVER,
+                                   pspec);
+
+  /**
    * StWidget::style-changed:
    *
    * Emitted when the style information that the widget derives from the
@@ -1794,10 +1869,10 @@ st_widget_set_direction (StWidget *self, StTextDirection dir)
  *
  * Enables tooltip support on the #StWidget.
  *
- * Note that setting has-tooltip to %TRUE will cause the widget to be set
- * reactive. If you no longer need tooltip support and do not need the widget
- * to be reactive, you need to set ClutterActor::reactive to FALSE.
- *
+ * Note that setting has-tooltip to %TRUE will cause
+ * #ClutterActor:reactive and #StWidget:track-hover to be set %TRUE as
+ * well, but you must clear these flags yourself (if appropriate) when
+ * setting it %FALSE.
  */
 void
 st_widget_set_has_tooltip (StWidget *widget,
@@ -1814,6 +1889,7 @@ st_widget_set_has_tooltip (StWidget *widget,
   if (has_tooltip)
     {
       clutter_actor_set_reactive ((ClutterActor*) widget, TRUE);
+      st_widget_set_track_hover (widget, TRUE);
 
       if (!priv->tooltip)
         {
@@ -1948,3 +2024,143 @@ st_widget_hide_tooltip (StWidget *widget)
   if (widget->priv->tooltip)
     st_tooltip_hide (widget->priv->tooltip);
 }
+
+/**
+ * st_widget_set_track_hover:
+ * @widget: A #StWidget
+ * @track_hover: %TRUE if the widget should track the pointer hover state
+ *
+ * Enables hover tracking on the #StWidget.
+ *
+ * If hover tracking is enabled, and the widget is visible and
+ * reactive, then @widget's #StWidget:hover property will be updated
+ * automatically to reflect whether the pointer is in @widget (or one
+ * of its children), and @widget's #StWidget:pseudo-class will have
+ * the "hover" class added and removed from it accordingly.
+ *
+ * Note that currently it is not possible to correctly track the hover
+ * state when another actor has a pointer grab. You can use
+ * st_widget_sync_hover() to update the property manually in this
+ * case.
+ */
+void
+st_widget_set_track_hover (StWidget *widget,
+                           gboolean  track_hover)
+{
+  StWidgetPrivate *priv;
+
+  g_return_if_fail (ST_IS_WIDGET (widget));
+
+  priv = widget->priv;
+
+  if (priv->track_hover != track_hover)
+    {
+      priv->track_hover = track_hover;
+      g_object_notify (G_OBJECT (widget), "track-hover");
+
+      if (priv->track_hover)
+        st_widget_sync_hover (widget);
+    }
+}
+
+/**
+ * st_widget_get_track_hover:
+ * @widget: A #StWidget
+ *
+ * Returns the current value of the track-hover property. See
+ * st_tooltip_set_track_hover() for more information.
+ *
+ * Returns: current value of track-hover on @widget
+ */
+gboolean
+st_widget_get_track_hover (StWidget *widget)
+{
+  g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
+
+  return widget->priv->track_hover;
+}
+
+/**
+ * st_widget_set_hover:
+ * @widget: A #StWidget
+ * @hover: whether the pointer is hovering over the widget
+ *
+ * Sets @widget's hover property and adds or removes "hover" from its
+ * pseudo class accordingly. If #StWidget:has-tooltip is %TRUE, this
+ * will also show or hide the tooltip, as appropriate.
+ *
+ * If you have set #StWidget:track-hover, you should not need to call
+ * this directly. You can call st_widget_sync_hover() if the hover
+ * state might be out of sync due to another actor's pointer grab.
+ */
+void
+st_widget_set_hover (StWidget *widget,
+                     gboolean  hover)
+{
+  StWidgetPrivate *priv;
+
+  g_return_if_fail (ST_IS_WIDGET (widget));
+
+  priv = widget->priv;
+
+  if (priv->hover != hover)
+    {
+      priv->hover = hover;
+      if (priv->hover)
+        {
+          st_widget_add_style_pseudo_class (widget, "hover");
+          if (priv->has_tooltip)
+            st_widget_show_tooltip (widget);
+        }
+      else
+        {
+          st_widget_remove_style_pseudo_class (widget, "hover");
+          if (priv->has_tooltip)
+            st_widget_hide_tooltip (widget);
+        }
+      g_object_notify (G_OBJECT (widget), "hover");
+    }
+}
+
+/**
+ * st_widget_sync_hover:
+ * @widget: A #StWidget
+ *
+ * Sets @widget's hover state according to the current pointer
+ * position. This can be used to ensure that it is correct after
+ * (or during) a pointer grab.
+ */
+void
+st_widget_sync_hover (StWidget *widget)
+{
+  ClutterDeviceManager *device_manager;
+  ClutterInputDevice *pointer;
+  ClutterActor *actor;
+
+  device_manager = clutter_device_manager_get_default ();
+  pointer = clutter_device_manager_get_core_device (device_manager,
+                                                    CLUTTER_POINTER_DEVICE);
+  actor = clutter_input_device_get_pointer_actor (pointer);
+
+  while (actor && actor != (ClutterActor *)widget)
+    actor = clutter_actor_get_parent (actor);
+
+  st_widget_set_hover (widget, actor == (ClutterActor *)widget);
+}
+
+/**
+ * st_widget_get_hover:
+ * @widget: A #StWidget
+ *
+ * If #StWidget:track-hover is set, this returns whether the pointer
+ * is currently over the widget.
+ *
+ * Returns: current value of hover on @widget
+ */
+gboolean
+st_widget_get_hover (StWidget *widget)
+{
+  g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
+
+  return widget->priv->hover;
+}
diff --git a/src/st/st-widget.h b/src/st/st-widget.h
index 660c5e5..b6e2766 100644
--- a/src/st/st-widget.h
+++ b/src/st/st-widget.h
@@ -120,6 +120,14 @@ const gchar*          st_widget_get_tooltip_text          (StWidget        *widg
 void                  st_widget_show_tooltip              (StWidget        *widget);
 void                  st_widget_hide_tooltip              (StWidget        *widget);
 
+void                  st_widget_set_track_hover           (StWidget        *widget,
+                                                           gboolean         track_hover);
+gboolean              st_widget_get_track_hover           (StWidget        *widget);
+void                  st_widget_set_hover                 (StWidget        *widget,
+                                                           gboolean         hover);
+void                  st_widget_sync_hover                (StWidget        *widget);
+gboolean              st_widget_get_hover                 (StWidget        *widget);
+
 void                  st_widget_ensure_style              (StWidget        *widget);
 
 StTextDirection       st_widget_get_default_direction     (void);



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