gridview: Add a focus tracker

... and use that to properly update selections when moving around with
the arrow keys.
This commit is contained in:
Benjamin Otte
2019-10-20 20:09:24 +02:00
committed by Matthias Clasen
parent e9cc7e0917
commit 26065eb200

View File

@@ -25,6 +25,7 @@
#include "gtkintl.h"
#include "gtklistitemfactory.h"
#include "gtklistitemmanagerprivate.h"
#include "gtkmain.h"
#include "gtkorientableprivate.h"
#include "gtkprivate.h"
#include "gtkscrollable.h"
@@ -78,6 +79,8 @@ struct _GtkGridView
guint anchor_ystart : 1;
/* the last item that was selected - basically the location to extend selections from */
GtkListItemTracker *selected;
/* the item that has input focus */
GtkListItemTracker *focus;
};
struct _Cell
@@ -396,6 +399,66 @@ gtk_grid_view_select_item (GtkGridView *self,
}
}
static gboolean
gtk_grid_view_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkGridView *self = GTK_GRID_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))
{
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 (!GTK_WIDGET_CLASS (gtk_grid_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))
{
gboolean extend = FALSE, modify = FALSE;
if (old_focus_child)
{
GdkSeat *seat = gdk_display_get_default_seat (gtk_widget_get_display (widget));
if (seat)
{
GdkDevice *keyboard = gdk_seat_get_keyboard (seat);
if (keyboard)
{
GdkModifierType state = gdk_device_get_modifier_state (keyboard);
extend = (state & GDK_SHIFT_MASK) != 0;
modify = (state & GDK_CONTROL_MASK) != 0;
}
}
}
gtk_grid_view_select_item (self,
gtk_list_item_get_position (GTK_LIST_ITEM (new_focus_child)),
modify,
extend);
}
return TRUE;
}
static gboolean
gtk_grid_view_adjustment_is_flipped (GtkGridView *self,
GtkOrientation orientation)
@@ -1041,6 +1104,11 @@ gtk_grid_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_grid_view_parent_class)->dispose (object);
@@ -1275,6 +1343,27 @@ gtk_grid_view_compute_scroll_align (GtkGridView *self,
}
}
static void
gtk_grid_view_update_focus_tracker (GtkGridView *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,
self->max_columns,
self->max_columns);
}
}
static void
gtk_grid_view_scroll_to_item (GtkWidget *widget,
const char *action_name,
@@ -1312,6 +1401,14 @@ gtk_grid_view_scroll_to_item (GtkWidget *widget,
&xalign, &xstart);
gtk_grid_view_set_anchor (self, pos, xalign, xstart, yalign, ystart);
/* 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_grid_view_update_focus_tracker (self);
}
static void
@@ -1339,6 +1436,7 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gpointer iface;
widget_class->focus = gtk_grid_view_focus;
widget_class->measure = gtk_grid_view_measure;
widget_class->size_allocate = gtk_grid_view_size_allocate;
@@ -1541,6 +1639,7 @@ gtk_grid_view_init (GtkGridView *self)
self->anchor_xstart = TRUE;
self->anchor_ystart = TRUE;
self->selected = gtk_list_item_tracker_new (self->item_manager);
self->focus = gtk_list_item_tracker_new (self->item_manager);
self->min_columns = 1;
self->max_columns = DEFAULT_MAX_COLUMNS;
@@ -1745,6 +1844,7 @@ gtk_grid_view_set_max_columns (GtkGridView *self,
self->anchor_xstart,
self->anchor_yalign,
self->anchor_ystart);
gtk_grid_view_update_focus_tracker (self);
gtk_widget_queue_resize (GTK_WIDGET (self));