[gtk/wip/matthiasc/focus2: 1/2] widget: Add a next-focus-child API



commit ea8894586298aebb82381e633940fbea7a5e7439
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Mar 2 14:35:14 2019 -0500

    widget: Add a next-focus-child API
    
    Move things around to make the focus chain introspectable.
    Use the next-focus-child api in the move-focus and
    focus implementations.
    
    We add a next_focus_child vfunc that containers can
    override to tweak the behavior.

 gtk/gtkwidget.c        |  27 ++++++++++---
 gtk/gtkwidget.h        |   6 +++
 gtk/gtkwidgetfocus.c   |  75 ++++++++++++++++++++++++++++++++++++-
 gtk/gtkwidgetprivate.h |   3 ++
 gtk/gtkwindow.c        | 100 +------------------------------------------------
 5 files changed, 106 insertions(+), 105 deletions(-)
---
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 0aaa77579c..912f9c8ce7 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -931,6 +931,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
   klass->mnemonic_activate = gtk_widget_real_mnemonic_activate;
   klass->grab_focus = gtk_widget_real_grab_focus;
   klass->focus = gtk_widget_real_focus;
+  klass->next_focus_child = gtk_widget_next_focus_child;
   klass->move_focus = gtk_widget_real_move_focus;
   klass->keynav_failed = gtk_widget_real_keynav_failed;
   klass->drag_begin = NULL;
@@ -5430,8 +5431,19 @@ gtk_widget_real_focus (GtkWidget         *widget,
   else
     {
       /* Try focusing any of the child widgets, depending on the given direction */
-      if (gtk_widget_focus_move (widget, direction))
-        return TRUE;
+      GtkWidget *focus_child = gtk_widget_get_focus_child (widget);
+      GtkWidget *next_focus;
+
+      if (focus_child)
+        next_focus = gtk_widget_get_next_focus (focus_child, direction);
+      else
+        next_focus = gtk_widget_get_next_focus (widget, direction);
+
+      if (gtk_widget_is_ancestor (next_focus, widget))
+        {
+          gtk_widget_grab_focus (next_focus);
+          return TRUE;
+        }
     }
 
   return FALSE;
@@ -5441,10 +5453,15 @@ static void
 gtk_widget_real_move_focus (GtkWidget         *widget,
                             GtkDirectionType   direction)
 {
-  GtkWidget *toplevel = _gtk_widget_get_toplevel (widget);
+  GtkWidget *focus_child;
+  GtkWidget *next_focus;
 
-  if (widget != toplevel && GTK_IS_WINDOW (toplevel))
-    g_signal_emit (toplevel, widget_signals[MOVE_FOCUS], 0, direction);
+  focus_child = gtk_root_get_focus (gtk_widget_get_root (widget));
+  if (focus_child)
+    next_focus = gtk_widget_get_next_focus (focus_child, direction);
+  else
+    next_focus = gtk_widget_get_next_focus (widget, direction);
+  gtk_widget_grab_focus (next_focus);
 }
 
 static gboolean
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index db4a81272c..c9d7d566aa 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -280,6 +280,9 @@ struct _GtkWidgetClass
   void     (* grab_focus)               (GtkWidget           *widget);
   gboolean (* focus)                    (GtkWidget           *widget,
                                          GtkDirectionType     direction);
+  GtkWidget * (* next_focus_child)      (GtkWidget           *widget,
+                                         GtkWidget           *child,
+                                         GtkDirectionType     direction);
 
   /* keyboard navigation */
   void     (* move_focus)               (GtkWidget           *widget,
@@ -480,6 +483,9 @@ gboolean   gtk_widget_has_visible_focus   (GtkWidget           *widget);
 GDK_AVAILABLE_IN_ALL
 void       gtk_widget_grab_focus          (GtkWidget           *widget);
 GDK_AVAILABLE_IN_ALL
+GtkWidget *gtk_widget_get_next_focus      (GtkWidget           *widget,
+                                           GtkDirectionType     direction);
+GDK_AVAILABLE_IN_ALL
 void       gtk_widget_set_focus_on_click  (GtkWidget           *widget,
                                            gboolean             focus_on_click);
 GDK_AVAILABLE_IN_ALL
diff --git a/gtk/gtkwidgetfocus.c b/gtk/gtkwidgetfocus.c
index c29adfdbe0..61c0239508 100644
--- a/gtk/gtkwidgetfocus.c
+++ b/gtk/gtkwidgetfocus.c
@@ -422,7 +422,8 @@ gtk_widget_focus_sort (GtkWidget        *widget,
            child != NULL;
            child = _gtk_widget_get_next_sibling (child))
         {
-          if (_gtk_widget_get_realized (child))
+          if (_gtk_widget_get_realized (child) &&
+              gtk_widget_get_sensitive (child))
             g_ptr_array_add (focus_order, child);
         }
     }
@@ -483,3 +484,75 @@ gtk_widget_focus_move (GtkWidget        *widget,
 
   return ret;
 }
+
+/**
+ * gtk_widget_get_next_focus:
+ * @widget: a #GtkWidget
+ * @direction: diretion to move in
+ *
+ * Finds the widget that would get focused if @widget was
+ * the focus widget, and focus was moved in @direcion.
+ *
+ * Returns: (transfer none): the next focus widget
+ */
+GtkWidget *
+gtk_widget_get_next_focus (GtkWidget        *widget,
+                           GtkDirectionType  dir)
+{
+  GtkWidget *prev;
+  GtkWidget *next;
+
+  prev = NULL;
+  do {
+    next = GTK_WIDGET_GET_CLASS (widget)->next_focus_child (widget, prev, dir);
+    if (next == NULL)
+      {
+        prev = widget;
+        widget = gtk_widget_get_parent (widget);
+      }
+    else if (gtk_widget_get_can_focus (next))
+      {
+        return next;
+      }
+    else
+      {
+        widget = next;
+        prev = NULL;
+      }
+  } while (widget);
+
+  return NULL;
+}
+
+GtkWidget *
+gtk_widget_next_focus_child (GtkWidget        *widget,
+                             GtkWidget        *focus_child,
+                             GtkDirectionType  direction)
+{
+  GPtrArray *focus_order;
+  int i;
+  GtkWidget *next_child = NULL;
+
+  focus_order = g_ptr_array_new ();
+  gtk_widget_focus_sort (widget, direction, focus_order);
+
+  for (i = 0; i < focus_order->len; i++)
+    {
+      GtkWidget *child = g_ptr_array_index (focus_order, i);
+
+      if (focus_child)
+        {
+          if (focus_child == child)
+            focus_child = NULL;
+        }
+      else
+        {
+          next_child = child;
+          break;
+        }
+    }
+
+  g_ptr_array_unref (focus_order);
+
+  return next_child;
+}
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 6510565ddb..ec4ca27fae 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -319,6 +319,9 @@ void              gtk_widget_focus_sort                    (GtkWidget        *wi
                                                             GPtrArray        *focus_order);
 gboolean          gtk_widget_focus_move                    (GtkWidget        *widget,
                                                             GtkDirectionType  direction);
+GtkWidget *       gtk_widget_next_focus_child              (GtkWidget        *widget,
+                                                            GtkWidget        *child,
+                                                            GtkDirectionType  direction);
 void              gtk_widget_get_surface_allocation         (GtkWidget *widget,
                                                             GtkAllocation *allocation);
 
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 575bc80f89..36ac49ff95 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -436,10 +436,6 @@ static void gtk_window_remove             (GtkContainer      *container,
 static void gtk_window_forall             (GtkContainer   *container,
                                           GtkCallback     callback,
                                           gpointer        callback_data);
-static gint gtk_window_focus              (GtkWidget        *widget,
-                                          GtkDirectionType  direction);
-static void gtk_window_move_focus         (GtkWidget         *widget,
-                                           GtkDirectionType   dir);
 static void gtk_window_real_set_focus     (GtkWindow         *window,
                                           GtkWidget         *focus);
 
@@ -804,8 +800,6 @@ gtk_window_class_init (GtkWindowClass *klass)
   widget_class->realize = gtk_window_realize;
   widget_class->unrealize = gtk_window_unrealize;
   widget_class->size_allocate = gtk_window_size_allocate;
-  widget_class->focus = gtk_window_focus;
-  widget_class->move_focus = gtk_window_move_focus;
   widget_class->measure = gtk_window_measure;
   widget_class->state_flags_changed = gtk_window_state_flags_changed;
   widget_class->style_updated = gtk_window_style_updated;
@@ -5750,7 +5744,7 @@ gtk_window_show (GtkWidget *widget)
       if (priv->initial_focus)
         gtk_window_set_focus (window, priv->initial_focus);
       else
-        gtk_window_move_focus (widget, GTK_DIR_TAB_FORWARD);
+        gtk_widget_child_focus (widget, GTK_DIR_TAB_FORWARD);
     }
   
   if (priv->modal)
@@ -7209,98 +7203,6 @@ gtk_window_forall (GtkContainer *container,
     (* callback) (priv->title_box, callback_data);
 }
 
-static gboolean
-gtk_window_focus (GtkWidget        *widget,
-                 GtkDirectionType  direction)
-{
-  GtkWindow *window = GTK_WINDOW (widget);
-  GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
-  GtkBin *bin;
-  GtkContainer *container;
-  GtkWidget *child;
-  GtkWidget *old_focus_child;
-  GtkWidget *parent;
-
-  if (!_gtk_widget_is_toplevel (widget))
-    return GTK_WIDGET_CLASS (gtk_window_parent_class)->focus (widget, direction);
-
-  container = GTK_CONTAINER (widget);
-  bin = GTK_BIN (widget);
-
-  old_focus_child = gtk_widget_get_focus_child (widget);
-
-  /* We need a special implementation here to deal properly with wrapping
-   * around in the tab chain without the danger of going into an
-   * infinite loop.
-   */
-  if (old_focus_child)
-    {
-      if (gtk_widget_child_focus (old_focus_child, direction))
-       return TRUE;
-    }
-
-  if (priv->focus_widget)
-    {
-      if (direction == GTK_DIR_LEFT ||
-         direction == GTK_DIR_RIGHT ||
-         direction == GTK_DIR_UP ||
-         direction == GTK_DIR_DOWN)
-       {
-         return FALSE;
-       }
-      
-      /* Wrapped off the end, clear the focus setting for the toplpevel */
-      parent = _gtk_widget_get_parent (priv->focus_widget);
-      while (parent)
-       {
-          gtk_widget_set_focus_child (parent, NULL);
-         parent = _gtk_widget_get_parent (parent);
-       }
-      
-      gtk_window_set_focus (GTK_WINDOW (container), NULL);
-    }
-
-  /* Now try to focus the first widget in the window,
-   * taking care to hook titlebar widgets into the
-   * focus chain.
-  */
-  if (priv->title_box != NULL &&
-      old_focus_child != NULL &&
-      priv->title_box != old_focus_child)
-    child = priv->title_box;
-  else
-    child = gtk_bin_get_child (bin);
-
-  if (child)
-    {
-      if (gtk_widget_child_focus (child, direction))
-        return TRUE;
-      else if (priv->title_box != NULL &&
-               priv->title_box != child &&
-               gtk_widget_child_focus (priv->title_box, direction))
-        return TRUE;
-
-    }
-
-  return FALSE;
-}
-
-static void
-gtk_window_move_focus (GtkWidget        *widget,
-                       GtkDirectionType  dir)
-{
-  if (!_gtk_widget_is_toplevel (widget))
-    {
-      GTK_WIDGET_CLASS (gtk_window_parent_class)->move_focus (widget, dir);
-      return;
-    }
-
-  gtk_widget_child_focus (widget, dir);
-
-  if (!gtk_widget_get_focus_child (widget))
-    gtk_window_set_focus (GTK_WINDOW (widget), NULL);
-}
-
 static void
 unset_focus_widget (GtkWindow *window)
 {


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