From 1a320a9aa3baa956a30c901e7d86722d35efb971 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 21 May 2023 17:40:39 -0400 Subject: [PATCH] gridview: Make focus movement section-aware Make up-down focus movements preserve the column across section boundaries. This code does the simplest thing that can work. If this turns out to be a problem, it can be made smarter. Note that the code currently prefers to jump over short sections to preserve the column. If there are many short sections, this not be desired, and we may want to make it tweakable. --- gtk/gtkgridview.c | 81 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/gtk/gtkgridview.c b/gtk/gtkgridview.c index 1dfeb8503f..70af4bbc00 100644 --- a/gtk/gtkgridview.c +++ b/gtk/gtkgridview.c @@ -619,26 +619,76 @@ gtk_grid_view_get_items_in_rect (GtkListBase *base, return result; } +static unsigned int +find_previous_item_in_column (GtkGridView *self, + unsigned int position) +{ + unsigned int col, pos; + + if (position == 0) + return position; + + col = gtk_grid_view_get_column_for_position (self->item_manager, + self->n_columns, + position); + + pos = position; + do + { + pos--; + + if (col == gtk_grid_view_get_column_for_position (self->item_manager, + self->n_columns, + pos)) + return pos; + + } while (pos > 0); + + return position; +} + +static unsigned int +find_next_item_in_column (GtkGridView *self, + unsigned int position) +{ + unsigned int col; + unsigned int n_items; + + n_items = g_list_model_get_n_items (G_LIST_MODEL (gtk_list_item_manager_get_model (self->item_manager))); + + col = gtk_grid_view_get_column_for_position (self->item_manager, + self->n_columns, + position); + + for (unsigned int p = position + 1; p < n_items; p++) + { + if (col == gtk_grid_view_get_column_for_position (self->item_manager, + self->n_columns, + p)) + return p; + } + + return position; +} + static guint gtk_grid_view_move_focus_along (GtkListBase *base, guint pos, int steps) { GtkGridView *self = GTK_GRID_VIEW (base); + unsigned int prev_pos = pos; - steps *= self->n_columns; + for (unsigned int i = 0; i < abs (steps); i++) + { + if (steps < 0) + pos = find_previous_item_in_column (self, pos); + else + pos = find_next_item_in_column (self, pos); + } - if (steps < 0) - { - if (pos >= self->n_columns) - pos -= MIN (pos, -steps); - } - else - { - guint n_items = gtk_list_base_get_n_items (base); - if (n_items / self->n_columns > pos / self->n_columns) - pos += MIN (n_items - pos - 1, steps); - } + if (prev_pos == pos) + gtk_widget_keynav_failed (GTK_WIDGET (self), steps < 0 ? GTK_DIR_UP : GTK_DIR_DOWN); return pos; } @@ -648,14 +698,19 @@ gtk_grid_view_move_focus_across (GtkListBase *base, guint pos, int steps) { + unsigned int prev_pos = pos; + if (steps < 0) - return pos - MIN (pos, -steps); + pos = pos - MIN (pos, -steps); else { guint n_items = gtk_list_base_get_n_items (base); pos += MIN (n_items - pos - 1, steps); } + if (prev_pos == pos) + gtk_widget_keynav_failed (GTK_WIDGET (base), steps < 0 ? GTK_DIR_LEFT : GTK_DIR_RIGHT); + return pos; }