[gtk/wip/otte/listview: 3/9] listview: Add a focus tracker



commit ec866664608112f3f676452312b5f65db6b1b2e9
Author: Benjamin Otte <otte redhat com>
Date:   Wed Oct 16 14:57:39 2019 +0200

    listview: Add a focus tracker
    
    This ensures that the row with the input focus always stays available,
    even when scrolled out of view.

 gtk/gtklistview.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)
---
diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c
index 1f199d40dd..56ce86797d 100644
--- a/gtk/gtklistview.c
+++ b/gtk/gtklistview.c
@@ -74,6 +74,8 @@ struct _GtkListView
   double anchor_align;
   /* the last item that was selected - basically the location to extend selections from */
   GtkListItemTracker *selected;
+  /* the item that has input focus */
+  GtkListItemTracker *focus;
 };
 
 struct _ListRow
@@ -663,9 +665,25 @@ gtk_list_view_focus (GtkWidget        *widget,
 
   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 &&
@@ -727,6 +745,11 @@ gtk_list_view_dispose (GObject *object)
       gtk_list_item_tracker_free (self->item_manager, self->selected);
       self->selected = NULL;
     }
+  if (self->focus)
+    {
+      gtk_list_item_tracker_free (self->item_manager, self->focus);
+      self->focus = NULL;
+    }
   g_clear_object (&self->item_manager);
 
   G_OBJECT_CLASS (gtk_list_view_parent_class)->dispose (object);
@@ -927,6 +950,27 @@ gtk_list_view_unselect_all (GtkWidget  *widget,
   gtk_selection_model_unselect_all (selection_model);
 }
 
+static void
+gtk_list_view_update_focus_tracker (GtkListView *self)
+{
+  GtkWidget *focus_child;
+  guint pos;
+
+  focus_child = gtk_widget_get_focus_child (GTK_WIDGET (self));
+  if (!GTK_IS_LIST_ITEM (focus_child))
+    return;
+
+  pos = gtk_list_item_get_position (GTK_LIST_ITEM (focus_child));
+  if (pos != gtk_list_item_tracker_get_position (self->item_manager, self->focus))
+    {
+      gtk_list_item_tracker_set_position (self->item_manager,
+                                          self->focus,
+                                          pos,
+                                          GTK_LIST_VIEW_EXTRA_ITEMS,
+                                          GTK_LIST_VIEW_EXTRA_ITEMS);
+    }
+}
+
 static void
 gtk_list_view_scroll_to_item (GtkWidget  *widget,
                               const char *action_name,
@@ -971,6 +1015,14 @@ gtk_list_view_scroll_to_item (GtkWidget  *widget,
       else
         gtk_list_view_set_anchor (self, pos, 1.0);
     }
+
+  /* HACK HACK HACK
+   *
+   * GTK has no way to track the focused child. But we now that when a listitem
+   * gets focus, it calls this action. So we update our focus tracker from here
+   * because it's the closest we can get to accurate tracking.
+   */
+  gtk_list_view_update_focus_tracker (self);
 }
 
 static void
@@ -1183,6 +1235,7 @@ static void
 gtk_list_view_init (GtkListView *self)
 {
   self->item_manager = gtk_list_item_manager_new (GTK_WIDGET (self), "row", ListRow, ListRowAugment, 
list_row_augment);
+  self->focus = gtk_list_item_tracker_new (self->item_manager);
   self->anchor = gtk_list_item_tracker_new (self->item_manager);
   self->selected = gtk_list_item_tracker_new (self->item_manager);
   self->orientation = GTK_ORIENTATION_VERTICAL;


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