listview: Add a focus tracker

This ensures that the row with the input focus always stays available,
even when scrolled out of view.
This commit is contained in:
Benjamin Otte 2019-10-16 14:57:39 +02:00 committed by Matthias Clasen
parent 4b6f3e0f2c
commit 2539828eac

View File

@ -73,6 +73,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
@ -662,9 +664,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 &&
@ -730,6 +748,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);
@ -931,6 +954,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,
@ -975,6 +1019,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
@ -1184,6 +1236,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;