From 045cea31e5e466fb6dea36ee9fd0a52018c09922 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 24 Oct 2019 06:49:38 +0200 Subject: [PATCH] listbase: Move focus moving keybindings here The focus tracker is not yet moved because that depends on scroll_to() support and we don't have that yet. Whoops. So we use a hack. --- gtk/gtkgridview.c | 202 +++++---------------------- gtk/gtklistbase.c | 293 +++++++++++++++++++++++++++++++++++++++ gtk/gtklistbaseprivate.h | 7 + gtk/gtklistview.c | 207 ++++----------------------- 4 files changed, 359 insertions(+), 350 deletions(-) diff --git a/gtk/gtkgridview.c b/gtk/gtkgridview.c index e726c500de..423e2e38fd 100644 --- a/gtk/gtkgridview.c +++ b/gtk/gtkgridview.c @@ -399,64 +399,46 @@ gtk_grid_view_set_anchor (GtkGridView *self, } } -static gboolean -gtk_grid_view_focus (GtkWidget *widget, - GtkDirectionType direction) +static guint +gtk_grid_view_move_focus_along (GtkListBase *base, + guint pos, + int steps) { - GtkGridView *self = GTK_GRID_VIEW (widget); - GtkWidget *old_focus_child, *new_focus_child; + GtkGridView *self = GTK_GRID_VIEW (base); - old_focus_child = gtk_widget_get_focus_child (widget); + steps *= self->n_columns; - if (old_focus_child == NULL && - (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_TAB_BACKWARD)) + if (steps < 0) { - 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 (pos >= self->n_columns) + pos -= MIN (pos, -steps); + } + else + { + guint n_items = self->model ? g_list_model_get_n_items (self->model) : 0; + if (n_items / self->n_columns > pos / self->n_columns) + pos += MIN (n_items - pos - 1, steps); } - if (!GTK_WIDGET_CLASS (gtk_grid_view_parent_class)->focus (widget, direction)) - return FALSE; + return pos; +} -moved_focus: - new_focus_child = gtk_widget_get_focus_child (widget); +static guint +gtk_grid_view_move_focus_across (GtkListBase *base, + guint pos, + int steps) +{ + GtkGridView *self = GTK_GRID_VIEW (base); - if (old_focus_child != new_focus_child && - GTK_IS_LIST_ITEM (new_focus_child)) + if (steps < 0) + return pos - MIN (pos, -steps); + else { - 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_list_base_select_item (GTK_LIST_BASE (self), - gtk_list_item_get_position (GTK_LIST_ITEM (new_focus_child)), - modify, - extend); + guint n_items = self->model ? g_list_model_get_n_items (self->model) : 0; + pos += MIN (n_items - pos - 1, steps); } - return TRUE; + return pos; } static void @@ -1279,87 +1261,6 @@ gtk_grid_view_activate_item (GtkWidget *widget, g_signal_emit (widget, signals[ACTIVATE], 0, pos); } -static gboolean -gtk_grid_view_move_cursor (GtkWidget *widget, - GVariant *args, - gpointer unused) -{ - GtkGridView *self = GTK_GRID_VIEW (widget); - int amount; - guint orientation; - guint pos, n_items; - gboolean select, modify, extend; - - g_variant_get (args, "(ubbbi)", &orientation, &select, &modify, &extend, &amount); - - if (gtk_list_base_get_orientation (GTK_LIST_BASE (self)) == orientation) - amount *= self->n_columns; - - if (orientation == GTK_ORIENTATION_HORIZONTAL && - gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) - amount = -amount; - - pos = gtk_list_item_tracker_get_position (self->item_manager, self->focus); - n_items = self->model ? g_list_model_get_n_items (self->model) : 0; - if (pos >= n_items || /* no focused item */ - (amount < 0 && pos < -amount)) - return TRUE; - if (amount > 0 && amount > n_items - pos) - { - /* pressing down with no item below the current item is more complicated - * because we want to move to the last row if we're not there yet */ - if (pos / self->n_columns < (n_items - 1) / self->n_columns) - amount = n_items - pos - 1; - else - return TRUE; - } - - gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), pos + amount, select, modify, extend); - - return TRUE; -} - -static gboolean -gtk_grid_view_move_cursor_to_start (GtkWidget *widget, - GVariant *args, - gpointer unused) -{ - GtkGridView *self = GTK_GRID_VIEW (widget); - gboolean select, modify, extend; - - if (self->model == NULL || g_list_model_get_n_items (self->model) == 0) - return TRUE; - - g_variant_get (args, "(bbb)", &select, &modify, &extend); - - gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), 0, select, modify, extend); - - return TRUE; -} - -static gboolean -gtk_grid_view_move_cursor_to_end (GtkWidget *widget, - GVariant *args, - gpointer unused) -{ - GtkGridView *self = GTK_GRID_VIEW (widget); - gboolean select, modify, extend; - guint n_items; - - if (self->model == NULL) - return TRUE; - - n_items = g_list_model_get_n_items (self->model); - if (n_items == 0) - return TRUE; - - g_variant_get (args, "(bbb)", &select, &modify, &extend); - - gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), n_items - 1, select, modify, extend); - - return TRUE; -} - static gboolean gtk_grid_view_move_cursor_page_up (GtkWidget *widget, GVariant *args, @@ -1478,29 +1379,6 @@ gtk_grid_view_add_custom_move_binding (GtkWidgetClass *widget_class, "(bbb)", TRUE, TRUE, TRUE); } -static void -gtk_grid_view_add_move_binding (GtkWidgetClass *widget_class, - guint keyval, - GtkOrientation orientation, - int amount) -{ - gtk_widget_class_add_binding (widget_class, - keyval, - GDK_CONTROL_MASK, - gtk_grid_view_move_cursor, - "(ubbbi)", orientation, FALSE, FALSE, FALSE, amount); - gtk_widget_class_add_binding (widget_class, - keyval, - GDK_SHIFT_MASK, - gtk_grid_view_move_cursor, - "(ubbbi)", orientation, TRUE, FALSE, TRUE, amount); - gtk_widget_class_add_binding (widget_class, - keyval, - GDK_CONTROL_MASK | GDK_SHIFT_MASK, - gtk_grid_view_move_cursor, - "(ubbbi)", orientation, TRUE, TRUE, TRUE, amount); -} - static void gtk_grid_view_class_init (GtkGridViewClass *klass) { @@ -1513,8 +1391,9 @@ gtk_grid_view_class_init (GtkGridViewClass *klass) list_base_class->list_item_augment_size = sizeof (CellAugment); list_base_class->list_item_augment_func = cell_augment; list_base_class->adjustment_value_changed = gtk_grid_view_adjustment_value_changed; + list_base_class->move_focus_along = gtk_grid_view_move_focus_along; + list_base_class->move_focus_across = gtk_grid_view_move_focus_across; - widget_class->focus = gtk_grid_view_focus; widget_class->measure = gtk_grid_view_measure; widget_class->size_allocate = gtk_grid_view_size_allocate; @@ -1624,31 +1503,12 @@ gtk_grid_view_class_init (GtkGridViewClass *klass) "u", gtk_grid_view_scroll_to_item); - gtk_grid_view_add_move_binding (widget_class, GDK_KEY_Up, GTK_ORIENTATION_VERTICAL, -1); - gtk_grid_view_add_move_binding (widget_class, GDK_KEY_KP_Up, GTK_ORIENTATION_VERTICAL, -1); - gtk_grid_view_add_move_binding (widget_class, GDK_KEY_Down, GTK_ORIENTATION_VERTICAL, 1); - gtk_grid_view_add_move_binding (widget_class, GDK_KEY_KP_Down, GTK_ORIENTATION_VERTICAL, 1); - gtk_grid_view_add_move_binding (widget_class, GDK_KEY_Left, GTK_ORIENTATION_HORIZONTAL, -1); - gtk_grid_view_add_move_binding (widget_class, GDK_KEY_KP_Left, GTK_ORIENTATION_HORIZONTAL, -1); - gtk_grid_view_add_move_binding (widget_class, GDK_KEY_Right, GTK_ORIENTATION_HORIZONTAL, 1); - gtk_grid_view_add_move_binding (widget_class, GDK_KEY_KP_Right, GTK_ORIENTATION_HORIZONTAL, 1); - - gtk_grid_view_add_custom_move_binding (widget_class, GDK_KEY_Home, gtk_grid_view_move_cursor_to_start); - gtk_grid_view_add_custom_move_binding (widget_class, GDK_KEY_KP_Home, gtk_grid_view_move_cursor_to_start); - gtk_grid_view_add_custom_move_binding (widget_class, GDK_KEY_End, gtk_grid_view_move_cursor_to_end); - gtk_grid_view_add_custom_move_binding (widget_class, GDK_KEY_KP_End, gtk_grid_view_move_cursor_to_end); gtk_grid_view_add_custom_move_binding (widget_class, GDK_KEY_Page_Up, gtk_grid_view_move_cursor_page_up); gtk_grid_view_add_custom_move_binding (widget_class, GDK_KEY_KP_Page_Up, gtk_grid_view_move_cursor_page_up); gtk_grid_view_add_custom_move_binding (widget_class, GDK_KEY_Page_Down, gtk_grid_view_move_cursor_page_down); gtk_grid_view_add_custom_move_binding (widget_class, GDK_KEY_KP_Page_Down, gtk_grid_view_move_cursor_page_down); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_a, GDK_CONTROL_MASK, "list.select-all", NULL); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_slash, GDK_CONTROL_MASK, "list.select-all", NULL); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "list.unselect-all", NULL); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_backslash, GDK_CONTROL_MASK, "list.unselect-all", NULL); - gtk_widget_class_set_css_name (widget_class, I_("flowbox")); - } static void diff --git a/gtk/gtklistbase.c b/gtk/gtklistbase.c index 38fad7320a..624907814a 100644 --- a/gtk/gtklistbase.c +++ b/gtk/gtklistbase.c @@ -93,6 +93,68 @@ gtk_list_base_clear_adjustment (GtkListBase *self, g_clear_object (&priv->adjustment[orientation]); } +/* + * gtk_list_base_move_focus_along: + * @self: a #GtkListBase + * @pos: position from which to move focus + * @steps: steps to move focus - negative numbers + * move focus backwards + * + * Moves focus @steps in the direction of the list. + * If focus cannot be moved, @pos is returned. + * If focus should be moved out of the widget, %GTK_INVALID_LIST_POSITION + * is returned. + * + * Returns: new focus position + **/ +static guint +gtk_list_base_move_focus_along (GtkListBase *self, + guint pos, + int steps) +{ + return GTK_LIST_BASE_GET_CLASS (self)->move_focus_along (self, pos, steps); +} + +/* + * gtk_list_base_move_focus_across: + * @self: a #GtkListBase + * @pos: position from which to move focus + * @steps: steps to move focus - negative numbers + * move focus backwards + * + * Moves focus @steps in the direction across the list. + * If focus cannot be moved, @pos is returned. + * If focus should be moved out of the widget, %GTK_INVALID_LIST_POSITION + * is returned. + * + * Returns: new focus position + **/ +static guint +gtk_list_base_move_focus_across (GtkListBase *self, + guint pos, + int steps) +{ + return GTK_LIST_BASE_GET_CLASS (self)->move_focus_across (self, pos, steps); +} + +static guint +gtk_list_base_move_focus (GtkListBase *self, + guint pos, + GtkOrientation orientation, + int steps) +{ + GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self); + + if (orientation == GTK_ORIENTATION_HORIZONTAL && + gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL) + steps = -steps; + + if (orientation == priv->orientation) + return gtk_list_base_move_focus_along (self, pos, steps); + else + return gtk_list_base_move_focus_across (self, pos, steps); +} + /* * gtk_list_base_select_item: * @self: a %GtkListBase @@ -185,6 +247,108 @@ gtk_list_base_select_item (GtkListBase *self, 0, 0); } +static guint +gtk_list_base_get_n_items (GtkListBase *self) +{ + GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self); + GtkSelectionModel *model; + + model = gtk_list_item_manager_get_model (priv->item_manager); + if (model == NULL) + return 0; + + return g_list_model_get_n_items (G_LIST_MODEL (model)); +} + +guint +gtk_list_base_get_focus_position (GtkListBase *self) +{ +#if 0 + GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self); + + return gtk_list_item_tracker_get_position (priv->item_manager, priv->focus); +#else + GtkWidget *focus_child = gtk_widget_get_focus_child (GTK_WIDGET (self)); + if (focus_child) + return gtk_list_item_get_position (GTK_LIST_ITEM (focus_child)); + else + return GTK_INVALID_LIST_POSITION; +#endif +} + +static gboolean +gtk_list_base_focus (GtkWidget *widget, + GtkDirectionType direction) +{ + GtkListBase *self = GTK_LIST_BASE (widget); + guint old, pos, n_items; + + pos = gtk_list_base_get_focus_position (self); + n_items = gtk_list_base_get_n_items (self); + old = pos; + + if (pos >= n_items) + { + if (n_items == 0) + return FALSE; + + pos = 0; + } + else if (gtk_widget_get_focus_child (widget) == NULL) + { + /* Focus was outside the list, just grab the old focus item + * while keeping the selection intact. + */ + return gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), pos, FALSE, FALSE, FALSE); + } + else + { + switch (direction) + { + case GTK_DIR_TAB_FORWARD: + pos++; + if (pos >= n_items) + return FALSE; + break; + + case GTK_DIR_TAB_BACKWARD: + if (pos == 0) + return FALSE; + pos--; + break; + + case GTK_DIR_UP: + pos = gtk_list_base_move_focus (self, pos, GTK_ORIENTATION_VERTICAL, -1); + break; + + case GTK_DIR_DOWN: + pos = gtk_list_base_move_focus (self, pos, GTK_ORIENTATION_VERTICAL, 1); + break; + + case GTK_DIR_LEFT: + pos = gtk_list_base_move_focus (self, pos, GTK_ORIENTATION_HORIZONTAL, -1); + break; + + case GTK_DIR_RIGHT: + pos = gtk_list_base_move_focus (self, pos, GTK_ORIENTATION_HORIZONTAL, 1); + break; + + default: + g_assert_not_reached (); + return TRUE; + } + } + + if (old != pos) + { + return gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), pos, TRUE, FALSE, FALSE); + } + else + { + return TRUE; + } +} + static void gtk_list_base_dispose (GObject *object) { @@ -378,6 +542,115 @@ gtk_list_base_unselect_all (GtkWidget *widget, gtk_selection_model_unselect_all (selection_model); } +static gboolean +gtk_list_base_move_cursor_to_start (GtkWidget *widget, + GVariant *args, + gpointer unused) +{ + GtkListBase *self = GTK_LIST_BASE (widget); + gboolean select, modify, extend; + + if (gtk_list_base_get_n_items (self) == 0) + return TRUE; + + g_variant_get (args, "(bbb)", &select, &modify, &extend); + + gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), 0, select, modify, extend); + + return TRUE; +} + +static gboolean +gtk_list_base_move_cursor_to_end (GtkWidget *widget, + GVariant *args, + gpointer unused) +{ + GtkListBase *self = GTK_LIST_BASE (widget); + gboolean select, modify, extend; + guint n_items; + + n_items = gtk_list_base_get_n_items (self); + if (n_items == 0) + return TRUE; + + g_variant_get (args, "(bbb)", &select, &modify, &extend); + + gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), n_items - 1, select, modify, extend); + + return TRUE; +} + +static gboolean +gtk_list_base_move_cursor (GtkWidget *widget, + GVariant *args, + gpointer unused) +{ + GtkListBase *self = GTK_LIST_BASE (widget); + int amount; + guint orientation; + guint pos; + gboolean select, modify, extend; + + g_variant_get (args, "(ubbbi)", &orientation, &select, &modify, &extend, &amount); + + pos = gtk_list_base_get_focus_position (self); + pos = gtk_list_base_move_focus (self, pos, orientation, amount); + + gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), pos, select, modify, extend); + + return TRUE; +} + +static void +gtk_list_base_add_move_binding (GtkWidgetClass *widget_class, + guint keyval, + GtkOrientation orientation, + int amount) +{ + gtk_widget_class_add_binding (widget_class, + keyval, + GDK_CONTROL_MASK, + gtk_list_base_move_cursor, + "(ubbbi)", orientation, FALSE, FALSE, FALSE, amount); + gtk_widget_class_add_binding (widget_class, + keyval, + GDK_SHIFT_MASK, + gtk_list_base_move_cursor, + "(ubbbi)", orientation, TRUE, FALSE, TRUE, amount); + gtk_widget_class_add_binding (widget_class, + keyval, + GDK_CONTROL_MASK | GDK_SHIFT_MASK, + gtk_list_base_move_cursor, + "(ubbbi)", orientation, TRUE, TRUE, TRUE, amount); +} + +static void +gtk_list_base_add_custom_move_binding (GtkWidgetClass *widget_class, + guint keyval, + GtkShortcutFunc callback) +{ + gtk_widget_class_add_binding (widget_class, + keyval, + 0, + callback, + "(bbb)", TRUE, FALSE, FALSE); + gtk_widget_class_add_binding (widget_class, + keyval, + GDK_CONTROL_MASK, + callback, + "(bbb)", FALSE, FALSE, FALSE); + gtk_widget_class_add_binding (widget_class, + keyval, + GDK_SHIFT_MASK, + callback, + "(bbb)", TRUE, FALSE, TRUE); + gtk_widget_class_add_binding (widget_class, + keyval, + GDK_CONTROL_MASK | GDK_SHIFT_MASK, + callback, + "(bbb)", TRUE, TRUE, TRUE); +} + static void gtk_list_base_class_init (GtkListBaseClass *klass) { @@ -385,6 +658,8 @@ gtk_list_base_class_init (GtkListBaseClass *klass) GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gpointer iface; + widget_class->focus = gtk_list_base_focus; + gobject_class->dispose = gtk_list_base_dispose; gobject_class->get_property = gtk_list_base_get_property; gobject_class->set_property = gtk_list_base_set_property; @@ -465,6 +740,24 @@ gtk_list_base_class_init (GtkListBaseClass *klass) NULL, gtk_list_base_unselect_all); + gtk_list_base_add_move_binding (widget_class, GDK_KEY_Up, GTK_ORIENTATION_VERTICAL, -1); + gtk_list_base_add_move_binding (widget_class, GDK_KEY_KP_Up, GTK_ORIENTATION_VERTICAL, -1); + gtk_list_base_add_move_binding (widget_class, GDK_KEY_Down, GTK_ORIENTATION_VERTICAL, 1); + gtk_list_base_add_move_binding (widget_class, GDK_KEY_KP_Down, GTK_ORIENTATION_VERTICAL, 1); + gtk_list_base_add_move_binding (widget_class, GDK_KEY_Left, GTK_ORIENTATION_HORIZONTAL, -1); + gtk_list_base_add_move_binding (widget_class, GDK_KEY_KP_Left, GTK_ORIENTATION_HORIZONTAL, -1); + gtk_list_base_add_move_binding (widget_class, GDK_KEY_Right, GTK_ORIENTATION_HORIZONTAL, 1); + gtk_list_base_add_move_binding (widget_class, GDK_KEY_KP_Right, GTK_ORIENTATION_HORIZONTAL, 1); + + gtk_list_base_add_custom_move_binding (widget_class, GDK_KEY_Home, gtk_list_base_move_cursor_to_start); + gtk_list_base_add_custom_move_binding (widget_class, GDK_KEY_KP_Home, gtk_list_base_move_cursor_to_start); + gtk_list_base_add_custom_move_binding (widget_class, GDK_KEY_End, gtk_list_base_move_cursor_to_end); + gtk_list_base_add_custom_move_binding (widget_class, GDK_KEY_KP_End, gtk_list_base_move_cursor_to_end); + + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_a, GDK_CONTROL_MASK, "list.select-all", NULL); + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_slash, GDK_CONTROL_MASK, "list.select-all", NULL); + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "list.unselect-all", NULL); + gtk_widget_class_add_binding_action (widget_class, GDK_KEY_backslash, GDK_CONTROL_MASK, "list.unselect-all", NULL); } static void diff --git a/gtk/gtklistbaseprivate.h b/gtk/gtklistbaseprivate.h index e6b070c508..189538a700 100644 --- a/gtk/gtklistbaseprivate.h +++ b/gtk/gtklistbaseprivate.h @@ -41,10 +41,17 @@ struct _GtkListBaseClass void (* adjustment_value_changed) (GtkListBase *self, GtkOrientation orientation); + guint (* move_focus_along) (GtkListBase *self, + guint pos, + int steps); + guint (* move_focus_across) (GtkListBase *self, + guint pos, + int steps); }; GtkOrientation gtk_list_base_get_orientation (GtkListBase *self); #define gtk_list_base_get_opposite_orientation(self) OPPOSITE_ORIENTATION(gtk_list_base_get_orientation(self)) +guint gtk_list_base_get_focus_position (GtkListBase *self); GtkListItemManager * gtk_list_base_get_manager (GtkListBase *self); GtkScrollablePolicy gtk_list_base_get_scroll_policy (GtkListBase *self, GtkOrientation orientation); diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c index 08d1fb9beb..dbb24f25a8 100644 --- a/gtk/gtklistview.c +++ b/gtk/gtklistview.c @@ -392,6 +392,32 @@ gtk_list_view_adjustment_value_changed (GtkListBase *base, gtk_widget_queue_allocate (GTK_WIDGET (self)); } +static guint +gtk_list_view_move_focus_along (GtkListBase *base, + guint pos, + int steps) +{ + GtkListView *self = GTK_LIST_VIEW (base); + + if (steps < 0) + return pos - MIN (pos, -steps); + else + { + guint n_items = self->model ? g_list_model_get_n_items (self->model) : 0; + pos += MIN (n_items - pos - 1, steps); + } + + return pos; +} + +static guint +gtk_list_view_move_focus_across (GtkListBase *base, + guint pos, + int steps) +{ + return pos; +} + static int gtk_list_view_update_adjustments (GtkListView *self, GtkOrientation orientation) @@ -688,66 +714,6 @@ gtk_list_view_size_allocate (GtkWidget *widget, } } -static gboolean -gtk_list_view_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - GtkListView *self = GTK_LIST_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)) - { - 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 && - 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_list_base_select_item (GTK_LIST_BASE (self), - gtk_list_item_get_position (GTK_LIST_ITEM (new_focus_child)), - modify, - extend); - } - - return TRUE; -} - static void gtk_list_view_dispose (GObject *object) { @@ -965,82 +931,6 @@ gtk_list_view_activate_item (GtkWidget *widget, g_signal_emit (widget, signals[ACTIVATE], 0, pos); } -static gboolean -gtk_list_view_move_cursor (GtkWidget *widget, - GVariant *args, - gpointer unused) -{ - GtkListView *self = GTK_LIST_VIEW (widget); - int amount; - guint orientation; - guint pos, new_pos, n_items; - gboolean select, modify, extend; - - g_variant_get (args, "(ubbbi)", &orientation, &select, &modify, &extend, &amount); - - if (gtk_list_base_get_orientation (GTK_LIST_BASE (self)) != orientation) - return TRUE; - - if (orientation == GTK_ORIENTATION_HORIZONTAL && - gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) - amount = -amount; - - pos = gtk_list_item_tracker_get_position (self->item_manager, self->focus); - n_items = self->model ? g_list_model_get_n_items (self->model) : 0; - if (pos >= n_items) - return TRUE; - - new_pos = pos + amount; - /* This overflow check only works reliably for amount == 1 */ - if (new_pos >= n_items) - return TRUE; - - gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), new_pos, select, modify, extend); - - return TRUE; -} - -static gboolean -gtk_list_view_move_cursor_to_start (GtkWidget *widget, - GVariant *args, - gpointer unused) -{ - GtkListView *self = GTK_LIST_VIEW (widget); - gboolean select, modify, extend; - - if (self->model == NULL || g_list_model_get_n_items (self->model) == 0) - return TRUE; - - g_variant_get (args, "(bbb)", &select, &modify, &extend); - - gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), 0, select, modify, extend); - - return TRUE; -} - -static gboolean -gtk_list_view_move_cursor_to_end (GtkWidget *widget, - GVariant *args, - gpointer unused) -{ - GtkListView *self = GTK_LIST_VIEW (widget); - gboolean select, modify, extend; - guint n_items; - - if (self->model == NULL) - return TRUE; - - n_items = g_list_model_get_n_items (self->model); - if (n_items == 0) - return TRUE; - - g_variant_get (args, "(bbb)", &select, &modify, &extend); - - gtk_list_base_grab_focus_on_item (GTK_LIST_BASE (self), n_items - 1, select, modify, extend); - - return TRUE; -} - static gboolean gtk_list_view_move_cursor_page_up (GtkWidget *widget, GVariant *args, @@ -1153,29 +1043,6 @@ gtk_list_view_add_custom_move_binding (GtkWidgetClass *widget_class, "(bbb)", TRUE, TRUE, TRUE); } -static void -gtk_list_view_add_move_binding (GtkWidgetClass *widget_class, - guint keyval, - GtkOrientation orientation, - int amount) -{ - gtk_widget_class_add_binding (widget_class, - keyval, - GDK_CONTROL_MASK, - gtk_list_view_move_cursor, - "(ubbbi)", orientation, FALSE, FALSE, FALSE, amount); - gtk_widget_class_add_binding (widget_class, - keyval, - GDK_SHIFT_MASK, - gtk_list_view_move_cursor, - "(ubbbi)", orientation, TRUE, FALSE, TRUE, amount); - gtk_widget_class_add_binding (widget_class, - keyval, - GDK_CONTROL_MASK | GDK_SHIFT_MASK, - gtk_list_view_move_cursor, - "(ubbbi)", orientation, TRUE, TRUE, TRUE, amount); -} - static void gtk_list_view_class_init (GtkListViewClass *klass) { @@ -1188,10 +1055,11 @@ gtk_list_view_class_init (GtkListViewClass *klass) list_base_class->list_item_augment_size = sizeof (ListRowAugment); list_base_class->list_item_augment_func = list_row_augment; list_base_class->adjustment_value_changed = gtk_list_view_adjustment_value_changed; + list_base_class->move_focus_along = gtk_list_view_move_focus_along; + list_base_class->move_focus_across = gtk_list_view_move_focus_across; widget_class->measure = gtk_list_view_measure; widget_class->size_allocate = gtk_list_view_size_allocate; - widget_class->focus = gtk_list_view_focus; gobject_class->dispose = gtk_list_view_dispose; gobject_class->get_property = gtk_list_view_get_property; @@ -1283,30 +1151,11 @@ gtk_list_view_class_init (GtkListViewClass *klass) "u", gtk_list_view_scroll_to_item); - gtk_list_view_add_move_binding (widget_class, GDK_KEY_Up, GTK_ORIENTATION_VERTICAL, -1); - gtk_list_view_add_move_binding (widget_class, GDK_KEY_KP_Up, GTK_ORIENTATION_VERTICAL, -1); - gtk_list_view_add_move_binding (widget_class, GDK_KEY_Down, GTK_ORIENTATION_VERTICAL, 1); - gtk_list_view_add_move_binding (widget_class, GDK_KEY_KP_Down, GTK_ORIENTATION_VERTICAL, 1); - gtk_list_view_add_move_binding (widget_class, GDK_KEY_Left, GTK_ORIENTATION_HORIZONTAL, -1); - gtk_list_view_add_move_binding (widget_class, GDK_KEY_KP_Left, GTK_ORIENTATION_HORIZONTAL, -1); - gtk_list_view_add_move_binding (widget_class, GDK_KEY_Right, GTK_ORIENTATION_HORIZONTAL, 1); - gtk_list_view_add_move_binding (widget_class, GDK_KEY_KP_Right, GTK_ORIENTATION_HORIZONTAL, 1); - - gtk_list_view_add_custom_move_binding (widget_class, GDK_KEY_Home, gtk_list_view_move_cursor_to_start); - gtk_list_view_add_custom_move_binding (widget_class, GDK_KEY_KP_Home, gtk_list_view_move_cursor_to_start); - gtk_list_view_add_custom_move_binding (widget_class, GDK_KEY_End, gtk_list_view_move_cursor_to_end); - gtk_list_view_add_custom_move_binding (widget_class, GDK_KEY_KP_End, gtk_list_view_move_cursor_to_end); gtk_list_view_add_custom_move_binding (widget_class, GDK_KEY_Page_Up, gtk_list_view_move_cursor_page_up); gtk_list_view_add_custom_move_binding (widget_class, GDK_KEY_KP_Page_Up, gtk_list_view_move_cursor_page_up); gtk_list_view_add_custom_move_binding (widget_class, GDK_KEY_Page_Down, gtk_list_view_move_cursor_page_down); gtk_list_view_add_custom_move_binding (widget_class, GDK_KEY_KP_Page_Down, gtk_list_view_move_cursor_page_down); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_a, GDK_CONTROL_MASK, "list.select-all", NULL); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_slash, GDK_CONTROL_MASK, "list.select-all", NULL); - - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "list.unselect-all", NULL); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_backslash, GDK_CONTROL_MASK, "list.unselect-all", NULL); - gtk_widget_class_set_css_name (widget_class, I_("list")); }