diff --git a/gtk/gtklistitem.c b/gtk/gtklistitem.c index b7a58d944d..e4a373ab82 100644 --- a/gtk/gtklistitem.c +++ b/gtk/gtklistitem.c @@ -22,6 +22,7 @@ #include "gtklistitemprivate.h" #include "gtkcssnodeprivate.h" +#include "gtkeventcontrollerkey.h" #include "gtkgestureclick.h" #include "gtkintl.h" #include "gtkmain.h" @@ -242,6 +243,22 @@ gtk_list_item_click_gesture_pressed (GtkGestureClick *gesture, gtk_widget_grab_focus (widget); } +static void +gtk_list_item_focus_changed_cb (GtkEventControllerKey *controller, + GParamSpec *psepc, + GtkListItem *self) +{ + GtkWidget *widget = GTK_WIDGET (self); + + if (gtk_event_controller_key_contains_focus (controller)) + { + gtk_widget_activate_action (widget, + "list.scroll-to-item", + "u", + self->position); + } +} + static void gtk_list_item_click_gesture_released (GtkGestureClick *gesture, int n_press, @@ -263,6 +280,7 @@ gtk_list_item_click_gesture_canceled (GtkGestureClick *gesture, static void gtk_list_item_init (GtkListItem *self) { + GtkEventController *controller; GtkGesture *gesture; self->selectable = TRUE; @@ -282,6 +300,10 @@ gtk_list_item_init (GtkListItem *self) g_signal_connect (gesture, "cancel", G_CALLBACK (gtk_list_item_click_gesture_canceled), self); gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture)); + + controller = gtk_event_controller_key_new (); + g_signal_connect (controller, "notify::contains-focus", G_CALLBACK (gtk_list_item_focus_changed_cb), self); + gtk_widget_add_controller (GTK_WIDGET (self), controller); } GtkListItem * diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c index 63f34f455a..6d0283139d 100644 --- a/gtk/gtklistview.c +++ b/gtk/gtklistview.c @@ -731,6 +731,48 @@ gtk_list_view_select_item (GtkWidget *widget, } } +static void +gtk_list_view_scroll_to_item (GtkWidget *widget, + const char *action_name, + GVariant *parameter) +{ + GtkListView *self = GTK_LIST_VIEW (widget); + ListRow *row; + guint pos; + + if (!g_variant_check_format_string (parameter, "u", FALSE)) + return; + + g_variant_get (parameter, "u", &pos); + row = gtk_list_item_manager_get_nth (self->item_manager, pos, NULL); + if (row == NULL) + return; + + if (row->parent.widget) + { + int y = list_row_get_y (self, row); + int start = gtk_adjustment_get_value (self->adjustment[GTK_ORIENTATION_VERTICAL]); + int height = gtk_widget_get_height (GTK_WIDGET (self)); + double align; + + if (y < start) + align = 0.0; + else if (y + row->height > start + height) + align = 1.0; + else + align = (double) (y - start) / (height - row->height); + + gtk_list_view_set_anchor (self, pos, align); + } + else + { + if (pos < gtk_list_item_tracker_get_position (self->item_manager, self->anchor)) + gtk_list_view_set_anchor (self, pos, 0.0); + else + gtk_list_view_set_anchor (self, pos, 1.0); + } +} + static void gtk_list_view_class_init (GtkListViewClass *klass) { @@ -810,6 +852,18 @@ gtk_list_view_class_init (GtkListViewClass *klass) "(ubb)", gtk_list_view_select_item); + /** + * GtkListView|list.scroll-to-item: + * @position: position of item to scroll to + * + * Scrolls to the item given in @position with the minimum amount + * of scrolling required. If the item is already visible, nothing happens. + */ + gtk_widget_class_install_action (widget_class, + "list.scroll-to-item", + "u", + gtk_list_view_scroll_to_item); + gtk_widget_class_set_css_name (widget_class, I_("list")); }