[gtk/wip/otte/listview: 114/158] listbase: Move focus moving keybindings here



commit 367f44de2814e2cef3f39678c88fa094545e3c35
Author: Benjamin Otte <otte redhat com>
Date:   Thu Oct 24 06:49:38 2019 +0200

    listbase: Move focus moving keybindings here
    
    The focus tracker is not yet moved because that depends on scroll_to()
    support and we don't have that yet.
    Whoops.
    So we use a hack.

 gtk/gtkgridview.c        | 196 +++++--------------------------
 gtk/gtklistbase.c        | 298 +++++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtklistbaseprivate.h |   7 ++
 gtk/gtklistview.c        | 200 +++++--------------------------
 4 files changed, 364 insertions(+), 337 deletions(-)
---
diff --git a/gtk/gtkgridview.c b/gtk/gtkgridview.c
index af4465954d..007ae981dd 100644
--- a/gtk/gtkgridview.c
+++ b/gtk/gtkgridview.c
@@ -400,60 +400,46 @@ gtk_grid_view_set_anchor (GtkGridView *self,
     }
 }
 
-static gboolean
-gtk_grid_view_focus (GtkWidget        *widget,
-                     GtkDirectionType  direction)
+static guint
+gtk_grid_view_move_focus_along (GtkListBase *base,
+                                guint        pos,
+                                int          steps)
 {
-  GtkGridView *self = GTK_GRID_VIEW (widget);
-  GtkWidget *old_focus_child, *new_focus_child;
+  GtkGridView *self = GTK_GRID_VIEW (base);
 
-  old_focus_child = gtk_widget_get_focus_child (widget);
+  steps *= self->n_columns;
 
-  if (old_focus_child == NULL &&
-      (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_TAB_BACKWARD))
+  if (steps < 0)
     {
-      Cell *cell;
-      guint pos;
-
-      /* When tabbing into the listview, don't focus the first/last item,
-       * but keep the previously focused item
-       */
-      pos = gtk_list_item_tracker_get_position (self->item_manager, self->focus);
-      cell = gtk_list_item_manager_get_nth (self->item_manager, pos, NULL);
-      if (cell && gtk_widget_grab_focus (cell->parent.widget))
-        goto moved_focus;
+      if (pos >= self->n_columns)
+        pos -= MIN (pos, -steps);
+    }
+  else
+    {
+      guint n_items = self->model ? g_list_model_get_n_items (self->model) : 0;
+      if (n_items / self->n_columns > pos / self->n_columns)
+        pos += MIN (n_items - pos - 1, steps);
     }
 
-  if (!GTK_WIDGET_CLASS (gtk_grid_view_parent_class)->focus (widget, direction))
-    return FALSE;
+  return pos;
+}
 
-moved_focus:
-  new_focus_child = gtk_widget_get_focus_child (widget);
+static guint
+gtk_grid_view_move_focus_across (GtkListBase *base,
+                                 guint        pos,
+                                 int          steps)
+{
+  GtkGridView *self = GTK_GRID_VIEW (base);
 
-  if (old_focus_child != new_focus_child &&
-      GTK_IS_LIST_ITEM (new_focus_child))
+  if (steps < 0)
+    return pos - MIN (pos, -steps);
+  else
     {
-      GdkModifierType state;
-      GdkModifierType mask;
-      gboolean extend = FALSE, modify = FALSE;
-
-      if (old_focus_child && gtk_get_current_event_state (&state))
-        {
-          mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_MODIFY_SELECTION);
-          if ((state & mask) == mask)
-            modify = TRUE;
-          mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION);
-          if ((state & mask) == mask)
-            extend = TRUE;
-        }
-
-      gtk_list_base_select_item (GTK_LIST_BASE (self),
-                                 gtk_list_item_get_position (GTK_LIST_ITEM (new_focus_child)),
-                                 modify,
-                                 extend);
+      guint n_items = self->model ? g_list_model_get_n_items (self->model) : 0;
+      pos += MIN (n_items - pos - 1, steps);
     }
 
-  return TRUE;
+  return pos;
 }
 
 static void
@@ -1276,81 +1262,6 @@ gtk_grid_view_activate_item (GtkWidget  *widget,
   g_signal_emit (widget, signals[ACTIVATE], 0, pos);
 }
 
-static void
-gtk_grid_view_move_cursor (GtkWidget *widget,
-                           GVariant  *args,
-                           gpointer   unused)
-{
-  GtkGridView *self = GTK_GRID_VIEW (widget);
-  int amount;
-  guint orientation;
-  guint pos, n_items;
-  gboolean select, modify, extend;
-
-  g_variant_get (args, "(ubbbi)", &orientation, &select, &modify, &extend, &amount);
-
-  if (gtk_list_base_get_orientation (GTK_LIST_BASE (self)) == orientation)
-    amount *= self->n_columns;
-
-  if (orientation == GTK_ORIENTATION_HORIZONTAL &&
-      gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
-    amount = -amount;
-
-  pos = gtk_list_item_tracker_get_position (self->item_manager, self->focus);
-  n_items = self->model ? g_list_model_get_n_items (self->model) : 0;
-  if (pos >= n_items || /* no focused item */
-      (amount < 0 && pos < -amount))
-    return;
-  if (amount > 0 && amount > n_items - pos)
-    {
-      /* pressing down with no item below the current item is more complicated
-       * because we want to move to the last row if we're not there yet */
-      if (pos / self->n_columns < (n_items - 1) / self->n_columns)
-        amount = n_items - pos - 1;
-      else
-        return;
-    }
-
-  gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), pos + amount, select, modify, extend);
-}
-
-static void
-gtk_grid_view_move_cursor_to_start (GtkWidget *widget,
-                                    GVariant  *args,
-                                    gpointer   unused)
-{
-  GtkGridView *self = GTK_GRID_VIEW (widget);
-  gboolean select, modify, extend;
-
-  if (self->model == NULL || g_list_model_get_n_items (self->model) == 0)
-    return;
-
-  g_variant_get (args, "(bbb)", &select, &modify, &extend);
-
-  gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), 0, select, modify, extend);
-}
-
-static void
-gtk_grid_view_move_cursor_to_end (GtkWidget *widget,
-                                  GVariant  *args,
-                                  gpointer   unused)
-{
-  GtkGridView *self = GTK_GRID_VIEW (widget);
-  gboolean select, modify, extend;
-  guint n_items;
-
-  if (self->model == NULL)
-    return;
-
-  n_items = g_list_model_get_n_items (self->model);
-  if (n_items == 0)
-    return;
-
-  g_variant_get (args, "(bbb)", &select, &modify, &extend);
-
-  gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), n_items - 1, select, modify, extend);
-}
-
 static void
 gtk_grid_view_move_cursor_page_up (GtkWidget *widget,
                                    GVariant  *args,
@@ -1469,32 +1380,6 @@ gtk_grid_view_add_custom_move_binding (GtkBindingSet      *binding_set,
                                   NULL, NULL);
 }
 
-static void
-gtk_grid_view_add_move_binding (GtkBindingSet  *binding_set,
-                                guint           keyval,
-                                GtkOrientation  orientation,
-                                int             amount)
-{
-  gtk_binding_entry_add_callback (binding_set,
-                                  keyval,
-                                  GDK_CONTROL_MASK,
-                                  gtk_grid_view_move_cursor,
-                                  g_variant_new ("(ubbbi)", orientation, FALSE, FALSE, FALSE, amount),
-                                  NULL, NULL);
-  gtk_binding_entry_add_callback (binding_set,
-                                  keyval,
-                                  GDK_SHIFT_MASK,
-                                  gtk_grid_view_move_cursor,
-                                  g_variant_new ("(ubbbi)", orientation, TRUE, FALSE, TRUE, amount),
-                                  NULL, NULL);
-  gtk_binding_entry_add_callback (binding_set,
-                                  keyval,
-                                  GDK_CONTROL_MASK | GDK_SHIFT_MASK,
-                                  gtk_grid_view_move_cursor,
-                                  g_variant_new ("(ubbbi)", orientation, TRUE, TRUE, TRUE, amount),
-                                  NULL, NULL);
-}
-
 static void
 gtk_grid_view_class_init (GtkGridViewClass *klass)
 {
@@ -1508,8 +1393,9 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
   list_base_class->list_item_augment_size = sizeof (CellAugment);
   list_base_class->list_item_augment_func = cell_augment;
   list_base_class->adjustment_value_changed = gtk_grid_view_adjustment_value_changed;
+  list_base_class->move_focus_along = gtk_grid_view_move_focus_along;
+  list_base_class->move_focus_across = gtk_grid_view_move_focus_across;
 
-  widget_class->focus = gtk_grid_view_focus;
   widget_class->measure = gtk_grid_view_measure;
   widget_class->size_allocate = gtk_grid_view_size_allocate;
 
@@ -1621,32 +1507,12 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
 
   binding_set = gtk_binding_set_by_class (klass);
 
-  gtk_grid_view_add_move_binding (binding_set, GDK_KEY_Up, GTK_ORIENTATION_VERTICAL, -1);
-  gtk_grid_view_add_move_binding (binding_set, GDK_KEY_KP_Up, GTK_ORIENTATION_VERTICAL, -1);
-  gtk_grid_view_add_move_binding (binding_set, GDK_KEY_Down, GTK_ORIENTATION_VERTICAL, 1);
-  gtk_grid_view_add_move_binding (binding_set, GDK_KEY_KP_Down, GTK_ORIENTATION_VERTICAL, 1);
-  gtk_grid_view_add_move_binding (binding_set, GDK_KEY_Left, GTK_ORIENTATION_HORIZONTAL, -1);
-  gtk_grid_view_add_move_binding (binding_set, GDK_KEY_KP_Left, GTK_ORIENTATION_HORIZONTAL, -1);
-  gtk_grid_view_add_move_binding (binding_set, GDK_KEY_Right, GTK_ORIENTATION_HORIZONTAL, 1);
-  gtk_grid_view_add_move_binding (binding_set, GDK_KEY_KP_Right, GTK_ORIENTATION_HORIZONTAL, 1);
-
-  gtk_grid_view_add_custom_move_binding (binding_set, GDK_KEY_Home, gtk_grid_view_move_cursor_to_start);
-  gtk_grid_view_add_custom_move_binding (binding_set, GDK_KEY_KP_Home, gtk_grid_view_move_cursor_to_start);
-  gtk_grid_view_add_custom_move_binding (binding_set, GDK_KEY_End, gtk_grid_view_move_cursor_to_end);
-  gtk_grid_view_add_custom_move_binding (binding_set, GDK_KEY_KP_End, gtk_grid_view_move_cursor_to_end);
   gtk_grid_view_add_custom_move_binding (binding_set, GDK_KEY_Page_Up, gtk_grid_view_move_cursor_page_up);
   gtk_grid_view_add_custom_move_binding (binding_set, GDK_KEY_KP_Page_Up, gtk_grid_view_move_cursor_page_up);
   gtk_grid_view_add_custom_move_binding (binding_set, GDK_KEY_Page_Down, 
gtk_grid_view_move_cursor_page_down);
   gtk_grid_view_add_custom_move_binding (binding_set, GDK_KEY_KP_Page_Down, 
gtk_grid_view_move_cursor_page_down);
 
-  gtk_binding_entry_add_action (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, "list.select-all", NULL);
-  gtk_binding_entry_add_action (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK, "list.select-all", NULL);
-
-  gtk_binding_entry_add_action (binding_set, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, 
"list.unselect-all", NULL);
-  gtk_binding_entry_add_action (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK, "list.unselect-all", NULL);
-
   gtk_widget_class_set_css_name (widget_class, I_("flowbox"));
-
 }
 
 static void
diff --git a/gtk/gtklistbase.c b/gtk/gtklistbase.c
index 38fad7320a..9ab59fc783 100644
--- a/gtk/gtklistbase.c
+++ b/gtk/gtklistbase.c
@@ -22,6 +22,7 @@
 #include "gtklistbaseprivate.h"
 
 #include "gtkadjustment.h"
+#include "gtkbindings.h"
 #include "gtkintl.h"
 #include "gtkorientableprivate.h"
 #include "gtkscrollable.h"
@@ -93,6 +94,68 @@ gtk_list_base_clear_adjustment (GtkListBase    *self,
   g_clear_object (&priv->adjustment[orientation]);
 }
 
+/*
+ * gtk_list_base_move_focus_along:
+ * @self: a #GtkListBase
+ * @pos: position from which to move focus
+ * @steps: steps to move focus - negative numbers
+ *     move focus backwards
+ *
+ * Moves focus @steps in the direction of the list.
+ * If focus cannot be moved, @pos is returned.
+ * If focus should be moved out of the widget, %GTK_INVALID_LIST_POSITION
+ * is returned.
+ *
+ * Returns: new focus position
+ **/
+static guint
+gtk_list_base_move_focus_along (GtkListBase *self,
+                                guint        pos,
+                                int          steps)
+{
+  return GTK_LIST_BASE_GET_CLASS (self)->move_focus_along (self, pos, steps);
+}
+
+/*
+ * gtk_list_base_move_focus_across:
+ * @self: a #GtkListBase
+ * @pos: position from which to move focus
+ * @steps: steps to move focus - negative numbers
+ *     move focus backwards
+ *
+ * Moves focus @steps in the direction across the list.
+ * If focus cannot be moved, @pos is returned.
+ * If focus should be moved out of the widget, %GTK_INVALID_LIST_POSITION
+ * is returned.
+ *
+ * Returns: new focus position
+ **/
+static guint
+gtk_list_base_move_focus_across (GtkListBase *self,
+                                 guint        pos,
+                                 int          steps)
+{
+  return GTK_LIST_BASE_GET_CLASS (self)->move_focus_across (self, pos, steps);
+}
+
+static guint
+gtk_list_base_move_focus (GtkListBase    *self,
+                          guint           pos,
+                          GtkOrientation  orientation,
+                          int             steps)
+{
+  GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL &&
+      gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL)
+    steps = -steps;
+
+  if (orientation == priv->orientation)
+    return gtk_list_base_move_focus_along (self, pos, steps);
+  else
+    return gtk_list_base_move_focus_across (self, pos, steps);
+}
+
 /*
  * gtk_list_base_select_item:
  * @self: a %GtkListBase
@@ -185,6 +248,108 @@ gtk_list_base_select_item (GtkListBase *self,
                                       0, 0);
 }
 
+static guint
+gtk_list_base_get_n_items (GtkListBase *self)
+{
+  GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+  GtkSelectionModel *model;
+
+  model = gtk_list_item_manager_get_model (priv->item_manager);
+  if (model == NULL)
+    return 0;
+
+  return g_list_model_get_n_items (G_LIST_MODEL (model));
+}
+
+guint
+gtk_list_base_get_focus_position (GtkListBase *self)
+{
+#if 0
+  GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+
+  return gtk_list_item_tracker_get_position (priv->item_manager, priv->focus);
+#else
+  GtkWidget *focus_child = gtk_widget_get_focus_child (GTK_WIDGET (self));
+  if (focus_child)
+    return gtk_list_item_get_position (GTK_LIST_ITEM (focus_child));
+  else
+    return GTK_INVALID_LIST_POSITION;
+#endif
+}
+
+static gboolean
+gtk_list_base_focus (GtkWidget        *widget,
+                     GtkDirectionType  direction)
+{
+  GtkListBase *self = GTK_LIST_BASE (widget);
+  guint old, pos, n_items;
+
+  pos = gtk_list_base_get_focus_position (self);
+  n_items = gtk_list_base_get_n_items (self);
+  old = pos;
+
+  if (pos >= n_items)
+    {
+      if (n_items == 0)
+        return FALSE;
+
+      pos = 0;
+    }
+  else if (gtk_widget_get_focus_child (widget) == NULL)
+    {
+      /* Focus was outside the list, just grab the old focus item
+       * while keeping the selection intact.
+       */
+      return gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), pos, FALSE, FALSE, FALSE);
+    }
+  else
+    {
+      switch (direction)
+        {
+        case GTK_DIR_TAB_FORWARD:
+          pos++;
+          if (pos >= n_items)
+            return FALSE;
+          break;
+
+        case GTK_DIR_TAB_BACKWARD:
+          if (pos == 0)
+            return FALSE;
+          pos--;
+          break;
+
+        case GTK_DIR_UP:
+          pos = gtk_list_base_move_focus (self, pos, GTK_ORIENTATION_VERTICAL, -1);
+          break;
+
+        case GTK_DIR_DOWN:
+          pos = gtk_list_base_move_focus (self, pos, GTK_ORIENTATION_VERTICAL, 1);
+          break;
+
+        case GTK_DIR_LEFT:
+          pos = gtk_list_base_move_focus (self, pos, GTK_ORIENTATION_HORIZONTAL, -1);
+          break;
+
+        case GTK_DIR_RIGHT:
+          pos = gtk_list_base_move_focus (self, pos, GTK_ORIENTATION_HORIZONTAL, 1);
+          break;
+
+        default:
+          g_assert_not_reached ();
+          return TRUE;
+        }
+    }
+
+  if (old != pos)
+    {
+      return gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), pos, TRUE, FALSE, FALSE);
+    }
+  else
+    {
+      return TRUE;
+    }
+}
+
 static void
 gtk_list_base_dispose (GObject *object)
 {
@@ -378,13 +543,126 @@ gtk_list_base_unselect_all (GtkWidget  *widget,
   gtk_selection_model_unselect_all (selection_model);
 }
 
+static void
+gtk_list_base_move_cursor_to_start (GtkWidget *widget,
+                                    GVariant  *args,
+                                    gpointer   unused)
+{
+  GtkListBase *self = GTK_LIST_BASE (widget);
+  gboolean select, modify, extend;
+
+  if (gtk_list_base_get_n_items (self) == 0)
+    return;
+
+  g_variant_get (args, "(bbb)", &select, &modify, &extend);
+
+  gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), 0, select, modify, extend);
+}
+
+static void
+gtk_list_base_move_cursor_to_end (GtkWidget *widget,
+                                  GVariant  *args,
+                                  gpointer   unused)
+{
+  GtkListBase *self = GTK_LIST_BASE (widget);
+  gboolean select, modify, extend;
+  guint n_items;
+
+  n_items = gtk_list_base_get_n_items (self);
+  if (n_items == 0)
+    return;
+
+  g_variant_get (args, "(bbb)", &select, &modify, &extend);
+
+  gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), n_items - 1, select, modify, extend);
+}
+
+static void
+gtk_list_base_move_cursor (GtkWidget *widget,
+                           GVariant  *args,
+                           gpointer   unused)
+{
+  GtkListBase *self = GTK_LIST_BASE (widget);
+  int amount;
+  guint orientation;
+  guint pos;
+  gboolean select, modify, extend;
+
+  g_variant_get (args, "(ubbbi)", &orientation, &select, &modify, &extend, &amount);
+ 
+  pos = gtk_list_base_get_focus_position (self);
+  pos = gtk_list_base_move_focus (self, pos, orientation, amount);
+
+  gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), pos, select, modify, extend);
+}
+
+static void
+gtk_list_base_add_move_binding (GtkBindingSet  *binding_set,
+                                guint           keyval,
+                                GtkOrientation  orientation,
+                                int             amount)
+{
+  gtk_binding_entry_add_callback (binding_set,
+                                  keyval,
+                                  GDK_CONTROL_MASK,
+                                  gtk_list_base_move_cursor,
+                                  g_variant_new ("(ubbbi)", orientation, FALSE, FALSE, FALSE, amount),
+                                  NULL, NULL);
+  gtk_binding_entry_add_callback (binding_set,
+                                  keyval,
+                                  GDK_SHIFT_MASK,
+                                  gtk_list_base_move_cursor,
+                                  g_variant_new ("(ubbbi)", orientation, TRUE, FALSE, TRUE, amount),
+                                  NULL, NULL);
+  gtk_binding_entry_add_callback (binding_set,
+                                  keyval,
+                                  GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+                                  gtk_list_base_move_cursor,
+                                  g_variant_new ("(ubbbi)", orientation, TRUE, TRUE, TRUE, amount),
+                                  NULL, NULL);
+}
+
+static void
+gtk_list_base_add_custom_move_binding (GtkBindingSet      *binding_set,
+                                       guint               keyval,
+                                       GtkBindingCallback  callback)
+{
+  gtk_binding_entry_add_callback (binding_set,
+                                  keyval,
+                                  0,
+                                  callback,
+                                  g_variant_new ("(bbb)", TRUE, FALSE, FALSE),
+                                  NULL, NULL);
+  gtk_binding_entry_add_callback (binding_set,
+                                  keyval,
+                                  GDK_CONTROL_MASK,
+                                  callback,
+                                  g_variant_new ("(bbb)", FALSE, FALSE, FALSE),
+                                  NULL, NULL);
+  gtk_binding_entry_add_callback (binding_set,
+                                  keyval,
+                                  GDK_SHIFT_MASK,
+                                  callback,
+                                  g_variant_new ("(bbb)", TRUE, FALSE, TRUE),
+                                  NULL, NULL);
+  gtk_binding_entry_add_callback (binding_set,
+                                  keyval,
+                                  GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+                                  callback,
+                                  g_variant_new ("(bbb)", TRUE, TRUE, TRUE),
+                                  NULL, NULL);
+}
+
 static void
 gtk_list_base_class_init (GtkListBaseClass *klass)
 {
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GtkBindingSet *binding_set;
   gpointer iface;
 
+  widget_class->focus = gtk_list_base_focus;
+
   gobject_class->dispose = gtk_list_base_dispose;
   gobject_class->get_property = gtk_list_base_get_property;
   gobject_class->set_property = gtk_list_base_set_property;
@@ -465,6 +743,26 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
                                    NULL,
                                    gtk_list_base_unselect_all);
 
+  binding_set = gtk_binding_set_by_class (klass);
+
+  gtk_list_base_add_move_binding (binding_set, GDK_KEY_Up, GTK_ORIENTATION_VERTICAL, -1);
+  gtk_list_base_add_move_binding (binding_set, GDK_KEY_KP_Up, GTK_ORIENTATION_VERTICAL, -1);
+  gtk_list_base_add_move_binding (binding_set, GDK_KEY_Down, GTK_ORIENTATION_VERTICAL, 1);
+  gtk_list_base_add_move_binding (binding_set, GDK_KEY_KP_Down, GTK_ORIENTATION_VERTICAL, 1);
+  gtk_list_base_add_move_binding (binding_set, GDK_KEY_Left, GTK_ORIENTATION_HORIZONTAL, -1);
+  gtk_list_base_add_move_binding (binding_set, GDK_KEY_KP_Left, GTK_ORIENTATION_HORIZONTAL, -1);
+  gtk_list_base_add_move_binding (binding_set, GDK_KEY_Right, GTK_ORIENTATION_HORIZONTAL, 1);
+  gtk_list_base_add_move_binding (binding_set, GDK_KEY_KP_Right, GTK_ORIENTATION_HORIZONTAL, 1);
+
+  gtk_list_base_add_custom_move_binding (binding_set, GDK_KEY_Home, gtk_list_base_move_cursor_to_start);
+  gtk_list_base_add_custom_move_binding (binding_set, GDK_KEY_KP_Home, gtk_list_base_move_cursor_to_start);
+  gtk_list_base_add_custom_move_binding (binding_set, GDK_KEY_End, gtk_list_base_move_cursor_to_end);
+  gtk_list_base_add_custom_move_binding (binding_set, GDK_KEY_KP_End, gtk_list_base_move_cursor_to_end);
+
+  gtk_binding_entry_add_action (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, "list.select-all", NULL);
+  gtk_binding_entry_add_action (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK, "list.select-all", NULL);
+  gtk_binding_entry_add_action (binding_set, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, 
"list.unselect-all", NULL);
+  gtk_binding_entry_add_action (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK, "list.unselect-all", NULL);
 }
 
 static void
diff --git a/gtk/gtklistbaseprivate.h b/gtk/gtklistbaseprivate.h
index e6b070c508..189538a700 100644
--- a/gtk/gtklistbaseprivate.h
+++ b/gtk/gtklistbaseprivate.h
@@ -41,10 +41,17 @@ struct _GtkListBaseClass
 
   void                 (* adjustment_value_changed)             (GtkListBase            *self,
                                                                  GtkOrientation          orientation);
+  guint                (* move_focus_along)                     (GtkListBase            *self,
+                                                                 guint                   pos,
+                                                                 int                     steps);
+  guint                (* move_focus_across)                    (GtkListBase            *self,
+                                                                 guint                   pos,
+                                                                 int                     steps);
 };
 
 GtkOrientation         gtk_list_base_get_orientation            (GtkListBase            *self);
 #define gtk_list_base_get_opposite_orientation(self) 
OPPOSITE_ORIENTATION(gtk_list_base_get_orientation(self))
+guint                  gtk_list_base_get_focus_position         (GtkListBase            *self);
 GtkListItemManager *   gtk_list_base_get_manager                (GtkListBase            *self);
 GtkScrollablePolicy    gtk_list_base_get_scroll_policy          (GtkListBase            *self,
                                                                  GtkOrientation          orientation);
diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c
index a57a647b22..033f4eab8f 100644
--- a/gtk/gtklistview.c
+++ b/gtk/gtklistview.c
@@ -393,6 +393,32 @@ gtk_list_view_adjustment_value_changed (GtkListBase    *base,
   gtk_widget_queue_allocate (GTK_WIDGET (self));
 }
 
+static guint
+gtk_list_view_move_focus_along (GtkListBase *base,
+                                guint        pos,
+                                int          steps)
+{
+  GtkListView *self = GTK_LIST_VIEW (base);
+
+  if (steps < 0)
+    return pos - MIN (pos, -steps);
+  else
+    {
+      guint n_items = self->model ? g_list_model_get_n_items (self->model) : 0;
+      pos += MIN (n_items - pos - 1, steps);
+    }
+
+  return pos;
+}
+
+static guint
+gtk_list_view_move_focus_across (GtkListBase *base,
+                                 guint        pos,
+                                 int          steps)
+{
+  return pos;
+}
+
 static int 
 gtk_list_view_update_adjustments (GtkListView    *self,
                                   GtkOrientation  orientation)
@@ -689,62 +715,6 @@ gtk_list_view_size_allocate (GtkWidget *widget,
     }
 }
 
-static gboolean
-gtk_list_view_focus (GtkWidget        *widget,
-                     GtkDirectionType  direction)
-{
-  GtkListView *self = GTK_LIST_VIEW (widget);
-  GtkWidget *old_focus_child, *new_focus_child;
-
-  old_focus_child = gtk_widget_get_focus_child (widget);
-
-  if (old_focus_child == NULL &&
-      (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_TAB_BACKWARD))
-    {
-      ListRow *row;
-      guint pos;
-
-      /* When tabbing into the listview, don't focus the first/last item,
-       * but keep the previously focused item
-       */
-      pos = gtk_list_item_tracker_get_position (self->item_manager, self->focus);
-      row = gtk_list_item_manager_get_nth (self->item_manager, pos, NULL);
-      if (row && gtk_widget_grab_focus (row->parent.widget))
-        goto moved_focus;
-    }
-
-  if (!GTK_WIDGET_CLASS (gtk_list_view_parent_class)->focus (widget, direction))
-    return FALSE;
-
-moved_focus:
-  new_focus_child = gtk_widget_get_focus_child (widget);
-
-  if (old_focus_child != new_focus_child &&
-      GTK_IS_LIST_ITEM (new_focus_child))
-    {
-      GdkModifierType state;
-      GdkModifierType mask;
-      gboolean extend = FALSE, modify = FALSE;
-
-      if (old_focus_child && gtk_get_current_event_state (&state))
-        {
-          mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_MODIFY_SELECTION);
-          if ((state & mask) == mask)
-            modify = TRUE;
-          mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION);
-          if ((state & mask) == mask)
-            extend = TRUE;
-        }
-
-      gtk_list_base_select_item (GTK_LIST_BASE (self),
-                                 gtk_list_item_get_position (GTK_LIST_ITEM (new_focus_child)),
-                                 modify,
-                                 extend);
-    }
-
-  return TRUE;
-}
-
 static void
 gtk_list_view_dispose (GObject *object)
 {
@@ -962,76 +932,6 @@ gtk_list_view_activate_item (GtkWidget  *widget,
   g_signal_emit (widget, signals[ACTIVATE], 0, pos);
 }
 
-static void
-gtk_list_view_move_cursor (GtkWidget *widget,
-                           GVariant  *args,
-                           gpointer   unused)
-{
-  GtkListView *self = GTK_LIST_VIEW (widget);
-  int amount;
-  guint orientation;
-  guint pos, new_pos, n_items;
-  gboolean select, modify, extend;
-
-  g_variant_get (args, "(ubbbi)", &orientation, &select, &modify, &extend, &amount);
-
-  if (gtk_list_base_get_orientation (GTK_LIST_BASE (self)) != orientation)
-    return;
-
-  if (orientation == GTK_ORIENTATION_HORIZONTAL &&
-      gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
-    amount = -amount;
-
-  pos = gtk_list_item_tracker_get_position (self->item_manager, self->focus);
-  n_items = self->model ? g_list_model_get_n_items (self->model) : 0;
-  if (pos >= n_items)
-    return;
-
-  new_pos = pos + amount;
-  /* This overflow check only works reliably for amount == 1 */
-  if (new_pos >= n_items)
-    return;
-
-  gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), new_pos, select, modify, extend);
-}
-
-static void
-gtk_list_view_move_cursor_to_start (GtkWidget *widget,
-                                    GVariant  *args,
-                                    gpointer   unused)
-{
-  GtkListView *self = GTK_LIST_VIEW (widget);
-  gboolean select, modify, extend;
-
-  if (self->model == NULL || g_list_model_get_n_items (self->model) == 0)
-    return;
-
-  g_variant_get (args, "(bbb)", &select, &modify, &extend);
-
-  gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), 0, select, modify, extend);
-}
-
-static void
-gtk_list_view_move_cursor_to_end (GtkWidget *widget,
-                                  GVariant  *args,
-                                  gpointer   unused)
-{
-  GtkListView *self = GTK_LIST_VIEW (widget);
-  gboolean select, modify, extend;
-  guint n_items;
-
-  if (self->model == NULL)
-    return;
-
-  n_items = g_list_model_get_n_items (self->model);
-  if (n_items == 0)
-    return;
-
-  g_variant_get (args, "(bbb)", &select, &modify, &extend);
-
-  gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), n_items - 1, select, modify, extend);
-}
-
 static void
 gtk_list_view_move_cursor_page_up (GtkWidget *widget,
                                    GVariant  *args,
@@ -1144,32 +1044,6 @@ gtk_list_view_add_custom_move_binding (GtkBindingSet      *binding_set,
                                   NULL, NULL);
 }
 
-static void
-gtk_list_view_add_move_binding (GtkBindingSet  *binding_set,
-                                guint           keyval,
-                                GtkOrientation  orientation,
-                                int             amount)
-{
-  gtk_binding_entry_add_callback (binding_set,
-                                  keyval,
-                                  GDK_CONTROL_MASK,
-                                  gtk_list_view_move_cursor,
-                                  g_variant_new ("(ubbbi)", orientation, FALSE, FALSE, FALSE, amount),
-                                  NULL, NULL);
-  gtk_binding_entry_add_callback (binding_set,
-                                  keyval,
-                                  GDK_SHIFT_MASK,
-                                  gtk_list_view_move_cursor,
-                                  g_variant_new ("(ubbbi)", orientation, TRUE, FALSE, TRUE, amount),
-                                  NULL, NULL);
-  gtk_binding_entry_add_callback (binding_set,
-                                  keyval,
-                                  GDK_CONTROL_MASK | GDK_SHIFT_MASK,
-                                  gtk_list_view_move_cursor,
-                                  g_variant_new ("(ubbbi)", orientation, TRUE, TRUE, TRUE, amount),
-                                  NULL, NULL);
-}
-
 static void
 gtk_list_view_class_init (GtkListViewClass *klass)
 {
@@ -1183,10 +1057,11 @@ gtk_list_view_class_init (GtkListViewClass *klass)
   list_base_class->list_item_augment_size = sizeof (ListRowAugment);
   list_base_class->list_item_augment_func = list_row_augment;
   list_base_class->adjustment_value_changed = gtk_list_view_adjustment_value_changed;
+  list_base_class->move_focus_along = gtk_list_view_move_focus_along;
+  list_base_class->move_focus_across = gtk_list_view_move_focus_across;
 
   widget_class->measure = gtk_list_view_measure;
   widget_class->size_allocate = gtk_list_view_size_allocate;
-  widget_class->focus = gtk_list_view_focus;
 
   gobject_class->dispose = gtk_list_view_dispose;
   gobject_class->get_property = gtk_list_view_get_property;
@@ -1280,30 +1155,11 @@ gtk_list_view_class_init (GtkListViewClass *klass)
 
   binding_set = gtk_binding_set_by_class (klass);
 
-  gtk_list_view_add_move_binding (binding_set, GDK_KEY_Up, GTK_ORIENTATION_VERTICAL, -1);
-  gtk_list_view_add_move_binding (binding_set, GDK_KEY_KP_Up, GTK_ORIENTATION_VERTICAL, -1);
-  gtk_list_view_add_move_binding (binding_set, GDK_KEY_Down, GTK_ORIENTATION_VERTICAL, 1);
-  gtk_list_view_add_move_binding (binding_set, GDK_KEY_KP_Down, GTK_ORIENTATION_VERTICAL, 1);
-  gtk_list_view_add_move_binding (binding_set, GDK_KEY_Left, GTK_ORIENTATION_HORIZONTAL, -1);
-  gtk_list_view_add_move_binding (binding_set, GDK_KEY_KP_Left, GTK_ORIENTATION_HORIZONTAL, -1);
-  gtk_list_view_add_move_binding (binding_set, GDK_KEY_Right, GTK_ORIENTATION_HORIZONTAL, 1);
-  gtk_list_view_add_move_binding (binding_set, GDK_KEY_KP_Right, GTK_ORIENTATION_HORIZONTAL, 1);
-
-  gtk_list_view_add_custom_move_binding (binding_set, GDK_KEY_Home, gtk_list_view_move_cursor_to_start);
-  gtk_list_view_add_custom_move_binding (binding_set, GDK_KEY_KP_Home, gtk_list_view_move_cursor_to_start);
-  gtk_list_view_add_custom_move_binding (binding_set, GDK_KEY_End, gtk_list_view_move_cursor_to_end);
-  gtk_list_view_add_custom_move_binding (binding_set, GDK_KEY_KP_End, gtk_list_view_move_cursor_to_end);
   gtk_list_view_add_custom_move_binding (binding_set, GDK_KEY_Page_Up, gtk_list_view_move_cursor_page_up);
   gtk_list_view_add_custom_move_binding (binding_set, GDK_KEY_KP_Page_Up, gtk_list_view_move_cursor_page_up);
   gtk_list_view_add_custom_move_binding (binding_set, GDK_KEY_Page_Down, 
gtk_list_view_move_cursor_page_down);
   gtk_list_view_add_custom_move_binding (binding_set, GDK_KEY_KP_Page_Down, 
gtk_list_view_move_cursor_page_down);
 
-  gtk_binding_entry_add_action (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, "list.select-all", NULL);
-  gtk_binding_entry_add_action (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK, "list.select-all", NULL);
-
-  gtk_binding_entry_add_action (binding_set, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, 
"list.unselect-all", NULL);
-  gtk_binding_entry_add_action (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK, "list.unselect-all", NULL);
-
   gtk_widget_class_set_css_name (widget_class, I_("list"));
 }
 


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