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



commit 1d1f9e5682c7f49234b958454241a3b50f70e4eb
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
    
    We can't get rid of the implementations in StContainer just yet,
    as StContainer still keeps its own child list. But this should
    lower the amount of code that has to be moved around when we
    remove StContainer.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=670034

 src/shell-generic-container.c |   10 +-
 src/st/st-container.c         |   26 +---
 src/st/st-container.h         |    4 -
 src/st/st-widget.c            |  290 ++++++++++++++++++++++++++++++++++++++++-
 src/st/st-widget.h            |    5 +
 5 files changed, 300 insertions(+), 35 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..50a9a99 100644
--- a/src/st/st-container.c
+++ b/src/st/st-container.c
@@ -183,8 +183,9 @@ st_container_get_children_list (StContainer *container)
 }
 
 static GList *
-st_container_real_get_focus_chain (StContainer *container)
+st_container_get_focus_chain (StWidget *widget)
 {
+  StContainer *container = ST_CONTAINER (widget);
   GList *chain, *children;
 
   chain = NULL;
@@ -199,23 +200,6 @@ st_container_real_get_focus_chain (StContainer *container)
   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 +633,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)
     {
@@ -752,7 +736,6 @@ st_container_class_init (StContainerClass *klass)
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
   StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
-  StContainerClass *container_class = ST_CONTAINER_CLASS (klass);
 
   g_type_class_add_private (klass, sizeof (StContainerPrivate));
 
@@ -760,7 +743,6 @@ st_container_class_init (StContainerClass *klass)
 
   actor_class->get_paint_volume = st_container_get_paint_volume;
 
+  widget_class->get_focus_chain = st_container_get_focus_chain;
   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 b8b61b0..2a81427 100644
--- a/src/st/st-widget.c
+++ b/src/st/st-widget.c
@@ -677,6 +677,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)
@@ -711,6 +717,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:
@@ -1581,20 +1588,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
@@ -2087,3 +2352,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 81dc6c6..10becf8 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;
@@ -151,6 +153,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]