diff --git a/gtk/gtkgridview.c b/gtk/gtkgridview.c index 40ff8cddd9..5e2d0791b7 100644 --- a/gtk/gtkgridview.c +++ b/gtk/gtkgridview.c @@ -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));