[gnome-shell/wip/clutter-deprecation-fixes: 7/21] st-widget: Copy get_focus_chain and navigate_focus from StContainer



commit 0d1a0763eadb1e189d5a136d8c24f9a5a4869632
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Mon Feb 13 19:55:30 2012 -0500

    st-widget: Copy get_focus_chain and navigate_focus from StContainer
    
    This is a prerequisite to getting rid of StContainer
    
    https://bugzilla.gnome.org/show_bug.cgi?id=670034

 src/shell-generic-container.c |   10 +-
 src/st/st-container.c         |   38 +-----
 src/st/st-container.h         |    4 -
 src/st/st-widget.c            |  290 ++++++++++++++++++++++++++++++++++++++++-
 src/st/st-widget.h            |    5 +
 5 files changed, 297 insertions(+), 50 deletions(-)
---
diff --git a/src/shell-generic-container.c b/src/shell-generic-container.c
index a3fc1b6..382e79b 100644
--- a/src/shell-generic-container.c
+++ b/src/shell-generic-container.c
@@ -163,13 +163,13 @@ shell_generic_container_pick (ClutterActor        *actor,
 }
 
 static GList *
-shell_generic_container_get_focus_chain (StContainer *container)
+shell_generic_container_get_focus_chain (StWidget *widget)
 {
-  ShellGenericContainer *self = SHELL_GENERIC_CONTAINER (container);
+  ShellGenericContainer *self = SHELL_GENERIC_CONTAINER (widget);
   GList *children, *focus_chain;
 
   focus_chain = NULL;
-  for (children = st_container_get_children_list (container); children; children = children->next)
+  for (children = st_container_get_children_list (ST_CONTAINER (widget)); children; children = children->next)
     {
       ClutterActor *child = children->data;
 
@@ -252,7 +252,7 @@ shell_generic_container_class_init (ShellGenericContainerClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
-  StContainerClass *container_class = ST_CONTAINER_CLASS (klass);
+  StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
 
   gobject_class->finalize = shell_generic_container_finalize;
 
@@ -262,7 +262,7 @@ shell_generic_container_class_init (ShellGenericContainerClass *klass)
   actor_class->paint = shell_generic_container_paint;
   actor_class->pick = shell_generic_container_pick;
 
-  container_class->get_focus_chain = shell_generic_container_get_focus_chain;
+  widget_class->get_focus_chain = shell_generic_container_get_focus_chain;
 
   /**
    * ShellGenericContainer::get-preferred-width:
diff --git a/src/st/st-container.c b/src/st/st-container.c
index c249407..1e8380f 100644
--- a/src/st/st-container.c
+++ b/src/st/st-container.c
@@ -182,40 +182,6 @@ st_container_get_children_list (StContainer *container)
   return container->priv->children;
 }
 
-static GList *
-st_container_real_get_focus_chain (StContainer *container)
-{
-  GList *chain, *children;
-
-  chain = NULL;
-  for (children = container->priv->children; children; children = children->next)
-    {
-      ClutterActor *child = children->data;
-
-      if (CLUTTER_ACTOR_IS_VISIBLE (child))
-        chain = g_list_prepend (chain, child);
-    }
-
-  return g_list_reverse (chain);
-}
-
-/**
- * st_container_get_focus_chain:
- * @container: An #StContainer
- *
- * Gets a list of the focusable children of @container, in "Tab"
- * order. By default, this returns all visible
- * (as in CLUTTER_ACTOR_IS_VISIBLE()) children of @container.
- *
- * Returns: (element-type Clutter.Actor) (transfer container):
- *   @container's focusable children
- */
-GList *
-st_container_get_focus_chain (StContainer *container)
-{
-  return ST_CONTAINER_GET_CLASS (container)->get_focus_chain (container);
-}
-
 static gint
 sort_z_order (gconstpointer a,
               gconstpointer b)
@@ -649,7 +615,7 @@ st_container_navigate_focus (StWidget         *widget,
    * "first" being determined by @direction.)
    */
 
-  children = st_container_get_focus_chain (container);
+  children = st_widget_get_focus_chain (ST_WIDGET (container));
   if (direction == GTK_DIR_TAB_FORWARD ||
       direction == GTK_DIR_TAB_BACKWARD)
     {
@@ -761,6 +727,4 @@ st_container_class_init (StContainerClass *klass)
   actor_class->get_paint_volume = st_container_get_paint_volume;
 
   widget_class->navigate_focus = st_container_navigate_focus;
-
-  container_class->get_focus_chain = st_container_real_get_focus_chain;
 }
diff --git a/src/st/st-container.h b/src/st/st-container.h
index 023bef9..418ca43 100644
--- a/src/st/st-container.h
+++ b/src/st/st-container.h
@@ -47,16 +47,12 @@ struct _StContainer {
 
 struct _StContainerClass {
   StWidgetClass parent_class;
-
-  GList * (*get_focus_chain) (StContainer *container);
 };
 
 GType   st_container_get_type             (void) G_GNUC_CONST;
 
 void    st_container_destroy_children     (StContainer *container);
 
-GList * st_container_get_focus_chain      (StContainer *container);
-
 /* Only to be used by subclasses of StContainer */
 void    st_container_move_child           (StContainer  *container,
                                            ClutterActor *actor,
diff --git a/src/st/st-widget.c b/src/st/st-widget.c
index 9522ec6..afefcc7 100644
--- a/src/st/st-widget.c
+++ b/src/st/st-widget.c
@@ -772,6 +772,12 @@ st_widget_get_paint_volume (ClutterActor *self,
   return TRUE;
 }
 
+static GList *
+st_widget_real_get_focus_chain (StWidget *widget)
+{
+  return clutter_actor_get_children (CLUTTER_ACTOR (widget));
+}
+
 
 static void
 st_widget_class_init (StWidgetClass *klass)
@@ -809,6 +815,7 @@ st_widget_class_init (StWidgetClass *klass)
   klass->style_changed = st_widget_real_style_changed;
   klass->navigate_focus = st_widget_real_navigate_focus;
   klass->get_accessible_type = st_widget_accessible_get_type;
+  klass->get_focus_chain = st_widget_real_get_focus_chain;
 
   /**
    * StWidget:pseudo-class:
@@ -1952,20 +1959,278 @@ st_widget_get_can_focus (StWidget *widget)
   return widget->priv->can_focus;
 }
 
+/* filter @children to contain only only actors that overlap @rbox
+ * when moving in @direction. (Assuming no transformations.)
+ */
+static GList *
+filter_by_position (GList            *children,
+                    ClutterActorBox  *rbox,
+                    GtkDirectionType  direction)
+{
+  ClutterActorBox cbox;
+  GList *l, *ret;
+  ClutterActor *child;
+
+  for (l = children, ret = NULL; l; l = l->next)
+    {
+      child = l->data;
+      clutter_actor_get_allocation_box (child, &cbox);
+
+      /* Filter out children if they are in the wrong direction from
+       * @rbox, or if they don't overlap it. To account for floating-
+       * point imprecision, an actor is "down" (etc.) from an another
+       * actor even if it overlaps it by up to 0.1 pixels.
+       */
+      switch (direction)
+        {
+        case GTK_DIR_UP:
+          if (cbox.y2 > rbox->y1 + 0.1)
+            continue;
+          if (cbox.x1 >= rbox->x2 || cbox.x2 <= rbox->x1)
+            continue;
+          break;
+
+        case GTK_DIR_DOWN:
+          if (cbox.y1 < rbox->y2 - 0.1)
+            continue;
+          if (cbox.x1 >= rbox->x2 || cbox.x2 <= rbox->x1)
+            continue;
+          break;
+
+        case GTK_DIR_LEFT:
+          if (cbox.x2 > rbox->x1 + 0.1)
+            continue;
+          if (cbox.y1 >= rbox->y2 || cbox.y2 <= rbox->y1)
+            continue;
+          break;
+
+        case GTK_DIR_RIGHT:
+          if (cbox.x1 < rbox->x2 - 0.1)
+            continue;
+          if (cbox.y1 >= rbox->y2 || cbox.y2 <= rbox->y1)
+            continue;
+          break;
+
+        default:
+          g_return_val_if_reached (NULL);
+        }
+
+      ret = g_list_prepend (ret, child);
+    }
+
+  g_list_free (children);
+  return ret;
+}
+
+
+typedef struct {
+  GtkDirectionType direction;
+  ClutterActorBox box;
+} StWidgetChildSortData;
+
+static int
+sort_by_position (gconstpointer  a,
+                  gconstpointer  b,
+                  gpointer       user_data)
+{
+  ClutterActor *actor_a = (ClutterActor *)a;
+  ClutterActor *actor_b = (ClutterActor *)b;
+  StWidgetChildSortData *sort_data = user_data;
+  GtkDirectionType direction = sort_data->direction;
+  ClutterActorBox abox, bbox;
+  int ax, ay, bx, by;
+  int cmp, fmid;
+
+  /* Determine the relationship, relative to motion in @direction, of
+   * the center points of the two actors. Eg, for %GTK_DIR_UP, we
+   * return a negative number if @actor_a's center is below @actor_b's
+   * center, and postive if vice versa, which will result in an
+   * overall list sorted bottom-to-top.
+   */
+
+  clutter_actor_get_allocation_box (actor_a, &abox);
+  ax = (int)(abox.x1 + abox.x2) / 2;
+  ay = (int)(abox.y1 + abox.y2) / 2;
+  clutter_actor_get_allocation_box (actor_b, &bbox);
+  bx = (int)(bbox.x1 + bbox.x2) / 2;
+  by = (int)(bbox.y1 + bbox.y2) / 2;
+
+  switch (direction)
+    {
+    case GTK_DIR_UP:
+      cmp = by - ay;
+      break;
+    case GTK_DIR_DOWN:
+      cmp = ay - by;
+      break;
+    case GTK_DIR_LEFT:
+      cmp = bx - ax;
+      break;
+    case GTK_DIR_RIGHT:
+      cmp = ax - bx;
+      break;
+    default:
+      g_return_val_if_reached (0);
+    }
+
+  if (cmp)
+    return cmp;
+
+  /* If two actors have the same center on the axis being sorted,
+   * prefer the one that is closer to the center of the current focus
+   * actor on the other axis. Eg, for %GTK_DIR_UP, prefer whichever
+   * of @actor_a and @actor_b has a horizontal center closest to the
+   * current focus actor's horizontal center.
+   *
+   * (This matches GTK's behavior.)
+   */
+  switch (direction)
+    {
+    case GTK_DIR_UP:
+    case GTK_DIR_DOWN:
+      fmid = (int)(sort_data->box.x1 + sort_data->box.x2) / 2;
+      return abs (ax - fmid) - abs (bx - fmid);
+    case GTK_DIR_LEFT:
+    case GTK_DIR_RIGHT:
+      fmid = (int)(sort_data->box.y1 + sort_data->box.y2) / 2;
+      return abs (ay - fmid) - abs (by - fmid);
+    default:
+      g_return_val_if_reached (0);
+    }
+}
+
 static gboolean
 st_widget_real_navigate_focus (StWidget         *widget,
                                ClutterActor     *from,
                                GtkDirectionType  direction)
 {
-  if (widget->priv->can_focus &&
-      CLUTTER_ACTOR (widget) != from)
+  ClutterActor *widget_actor, *focus_child;
+  GList *children, *l;
+
+  widget_actor = CLUTTER_ACTOR (widget);
+  if (from == widget_actor)
+    return FALSE;
+
+  /* Figure out if @from is a descendant of @widget, and if so,
+   * set @focus_child to the immediate child of @widget that
+   * contains (or *is*) @from.
+   */
+  focus_child = from;
+  while (focus_child && clutter_actor_get_parent (focus_child) != widget_actor)
+    focus_child = clutter_actor_get_parent (focus_child);
+
+  if (widget->priv->can_focus)
     {
-      clutter_actor_grab_key_focus (CLUTTER_ACTOR (widget));
-      return TRUE;
+      if (!focus_child)
+        {
+          /* Accept focus from outside */
+          clutter_actor_grab_key_focus (widget_actor);
+          return TRUE;
+        }
+      else
+        {
+          /* Yield focus from within: since @widget itself is
+           * focusable we don't allow the focus to be navigated
+           * within @widget.
+           */
+          return FALSE;
+        }
+    }
+
+  /* See if we can navigate within @focus_child */
+  if (focus_child && ST_IS_WIDGET (focus_child))
+    {
+      if (st_widget_navigate_focus (ST_WIDGET (focus_child), from, direction, FALSE))
+        return TRUE;
+    }
+
+  /* At this point we know that we want to navigate focus to one of
+   * @widget's immediate children; the next one after @focus_child,
+   * or the first one if @focus_child is %NULL. (With "next" and
+   * "first" being determined by @direction.)
+   */
+
+  children = st_widget_get_focus_chain (widget);
+  if (direction == GTK_DIR_TAB_FORWARD ||
+      direction == GTK_DIR_TAB_BACKWARD)
+    {
+      if (direction == GTK_DIR_TAB_BACKWARD)
+        children = g_list_reverse (children);
+
+      if (focus_child)
+        {
+          /* Remove focus_child and any earlier children */
+          while (children && children->data != focus_child)
+            children = g_list_delete_link (children, children);
+          if (children)
+            children = g_list_delete_link (children, children);
+        }
+    }
+  else /* direction is an arrow key, not tab */
+    {
+      StWidgetChildSortData sort_data;
+
+      /* Compute the allocation box of the previous focused actor, in
+       * @widget's coordinate space. If there was no previous focus,
+       * use the coordinates of the appropriate edge of @widget.
+       *
+       * Note that all of this code assumes the actors are not
+       * transformed (or at most, they are all scaled by the same
+       * amount). If @widget or any of its children is rotated, or
+       * any child is inconsistently scaled, then the focus chain will
+       * probably be unpredictable.
+       */
+      if (focus_child)
+        {
+          clutter_actor_get_allocation_box (focus_child, &sort_data.box);
+        }
+      else
+        {
+          clutter_actor_get_allocation_box (CLUTTER_ACTOR (widget), &sort_data.box);
+          switch (direction)
+            {
+            case GTK_DIR_UP:
+              sort_data.box.y1 = sort_data.box.y2;
+              break;
+            case GTK_DIR_DOWN:
+              sort_data.box.y2 = sort_data.box.y1;
+              break;
+            case GTK_DIR_LEFT:
+              sort_data.box.x1 = sort_data.box.x2;
+              break;
+            case GTK_DIR_RIGHT:
+              sort_data.box.x2 = sort_data.box.x1;
+              break;
+            default:
+              g_warn_if_reached ();
+            }
+        }
+      sort_data.direction = direction;
+
+      if (focus_child)
+        children = filter_by_position (children, &sort_data.box, direction);
+      if (children)
+        children = g_list_sort_with_data (children, sort_by_position, &sort_data);
+    }
+
+  /* Now try each child in turn */
+  for (l = children; l; l = l->next)
+    {
+      if (ST_IS_WIDGET (l->data))
+        {
+          if (st_widget_navigate_focus (l->data, from, direction, FALSE))
+            {
+              g_list_free (children);
+              return TRUE;
+            }
+        }
     }
+
+  g_list_free (children);
   return FALSE;
 }
 
+
 /**
  * st_widget_navigate_focus:
  * @widget: the "top level" container
@@ -2527,3 +2792,20 @@ check_labels (StWidgetAccessible *widget_accessible,
                                    ATK_OBJECT (widget_accessible));
     }
 }
+
+/**
+ * st_widget_get_focus_chain:
+ * @widget: An #StWidget
+ *
+ * Gets a list of the focusable children of @widget, in "Tab"
+ * order. By default, this returns all visible
+ * (as in CLUTTER_ACTOR_IS_VISIBLE()) children of @widget.
+ *
+ * Returns: (element-type Clutter.Actor) (transfer container):
+ *   @widget's focusable children
+ */
+GList *
+st_widget_get_focus_chain (StWidget *widget)
+{
+  return ST_WIDGET_GET_CLASS (widget)->get_focus_chain (widget);
+}
diff --git a/src/st/st-widget.h b/src/st/st-widget.h
index 2ed46b3..e4a57fb 100644
--- a/src/st/st-widget.h
+++ b/src/st/st-widget.h
@@ -86,6 +86,8 @@ struct _StWidgetClass
                                     ClutterActor     *from,
                                     GtkDirectionType  direction);
   GType    (* get_accessible_type) (void);
+
+  GList *  (* get_focus_chain)     (StWidget         *widget);
 };
 
 GType st_widget_get_type (void) G_GNUC_CONST;
@@ -161,6 +163,9 @@ void                  st_widget_style_changed             (StWidget        *widg
 StThemeNode *         st_widget_get_theme_node            (StWidget        *widget);
 StThemeNode *         st_widget_peek_theme_node           (StWidget        *widget);
 
+GList *               st_widget_get_focus_chain           (StWidget        *widget);
+
+
 /* debug methods */
 char  *st_describe_actor       (ClutterActor *actor);
 void   st_set_slow_down_factor (gfloat factor);



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