listview: Implement gtk_list_view_scroll_to()
authorBenjamin Otte <otte@redhat.com>
Tue, 7 Mar 2023 23:31:33 +0000 (00:31 +0100)
committerBenjamin Otte <otte@redhat.com>
Sat, 5 Aug 2023 01:51:53 +0000 (03:51 +0200)
This adds a flags enum so we can also do select/focus at the same time.

It's implemented in GtkListBase, so adding support forgridview should be
easy.

gtk/gtkenums.h
gtk/gtklistbase.c
gtk/gtklistbaseprivate.h
gtk/gtklistview.c
gtk/gtklistview.h

index 38bb3e25d1be0476a8b3688bdb74097292200edb..bcc1373a6fc51cd53dec1abc298fbd6193b2839e 100644 (file)
@@ -295,6 +295,24 @@ typedef enum
   GTK_LIST_TAB_CELL
 } GtkListTabBehavior;
 
+/**
+ * GtkListScrollFlags:
+ * @GTK_LIST_SCROLL_NONE: Don't do anything extra
+ * @GTK_LIST_SCROLL_FOCUS: Focus the target item
+ * @GTK_LIST_SCROLL_SELECT: Select the target item and
+ *   unselect all other items.
+ *
+ * List of actions to perform when scrolling to items in
+ * a list widget.
+ *
+ * Since: 4.12
+ */
+typedef enum {
+  GTK_LIST_SCROLL_NONE      = 0,
+  GTK_LIST_SCROLL_FOCUS     = 1 << 0,
+  GTK_LIST_SCROLL_SELECT    = 1 << 1
+} GtkListScrollFlags;
+
 /**
  * GtkMessageType:
  * @GTK_MESSAGE_INFO: Informational message
index 2589af28fab25c18aae887e8a8037339ea225a12..2cdac1461cf63c40aa512c26382b9b51710f7004 100644 (file)
@@ -34,6 +34,7 @@
 #include "gtkmultiselection.h"
 #include "gtkorientable.h"
 #include "gtkscrollable.h"
+#include "gtkscrollinfoprivate.h"
 #include "gtksingleselection.h"
 #include "gtksnapshot.h"
 #include "gtktypebuiltins.h"
@@ -528,7 +529,7 @@ gtk_list_base_get_n_items (GtkListBase *self)
   return g_list_model_get_n_items (G_LIST_MODEL (priv->model));
 }
 
-guint
+static guint
 gtk_list_base_get_focus_position (GtkListBase *self)
 {
   GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
@@ -818,23 +819,19 @@ gtk_list_base_set_property (GObject      *object,
 }
 
 static void
-gtk_list_base_compute_scroll_align (GtkListBase   *self,
-                                    GtkOrientation orientation,
-                                    int            cell_start,
-                                    int            cell_end,
+gtk_list_base_compute_scroll_align (int            cell_start,
+                                    int            cell_size,
+                                    int            visible_start,
+                                    int            visible_size,
                                     double         current_align,
                                     GtkPackType    current_side,
                                     double        *new_align,
                                     GtkPackType   *new_side)
 {
-  int visible_start, visible_size, visible_end;
-  int cell_size;
+  int cell_end, visible_end;
 
-  gtk_list_base_get_adjustment_values (GTK_LIST_BASE (self),
-                                       orientation,
-                                       &visible_start, NULL, &visible_size);
   visible_end = visible_start + visible_size;
-  cell_size = cell_end - cell_start;
+  cell_end = cell_start + cell_size;
 
   if (cell_size <= visible_size)
     {
@@ -878,26 +875,38 @@ gtk_list_base_compute_scroll_align (GtkListBase   *self,
 }
 
 static void
-gtk_list_base_scroll_to_item (GtkListBase *self,
-                              guint        pos)
+gtk_list_base_scroll_to_item (GtkListBase   *self,
+                              guint          pos,
+                              GtkScrollInfo *scroll)
 {
   GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
   double align_along, align_across;
   GtkPackType side_along, side_across;
-  GdkRectangle area;
+  GdkRectangle area, viewport;
+  int x, y;
 
   if (!gtk_list_base_get_allocation (GTK_LIST_BASE (self), pos, &area))
-    return;
+    {
+      g_clear_pointer (&scroll, gtk_scroll_info_unref);
+      return;
+    }
 
-  gtk_list_base_compute_scroll_align (self,
-                                      gtk_list_base_get_orientation (GTK_LIST_BASE (self)),
-                                      area.y, area.y + area.height,
+  gtk_list_base_get_adjustment_values (GTK_LIST_BASE (self),
+                                       gtk_list_base_get_orientation (GTK_LIST_BASE (self)),
+                                       &viewport.y, NULL, &viewport.height);
+  gtk_list_base_get_adjustment_values (GTK_LIST_BASE (self),
+                                       gtk_list_base_get_opposite_orientation (GTK_LIST_BASE (self)),
+                                       &viewport.x, NULL, &viewport.width);
+
+  gtk_scroll_info_compute_scroll (scroll, &area, &viewport, &x, &y);
+
+  gtk_list_base_compute_scroll_align (area.y, area.height,
+                                      y, viewport.height,
                                       priv->anchor_align_along, priv->anchor_side_along,
                                       &align_along, &side_along);
 
-  gtk_list_base_compute_scroll_align (self,
-                                      gtk_list_base_get_opposite_orientation (GTK_LIST_BASE (self)),
-                                      area.x, area.x + area.width,
+  gtk_list_base_compute_scroll_align (area.x, area.width,
+                                      x, viewport.width,
                                       priv->anchor_align_across, priv->anchor_side_across,
                                       &align_across, &side_across);
 
@@ -905,6 +914,8 @@ gtk_list_base_scroll_to_item (GtkListBase *self,
                             pos,
                             align_across, side_across,
                             align_along, side_along);
+
+  g_clear_pointer (&scroll, gtk_scroll_info_unref);
 }
 
 static void
@@ -920,7 +931,7 @@ gtk_list_base_scroll_to_item_action (GtkWidget  *widget,
 
   g_variant_get (parameter, "u", &pos);
 
-  gtk_list_base_scroll_to_item (self, pos);
+  gtk_list_base_scroll_to_item (self, pos, NULL);
 }
 
 static void
@@ -940,7 +951,7 @@ gtk_list_base_set_focus_child (GtkWidget *widget,
 
   if (pos != gtk_list_item_tracker_get_position (priv->item_manager, priv->focus))
     {
-      gtk_list_base_scroll_to_item (self, pos);
+      gtk_list_base_scroll_to_item (self, pos, NULL);
       gtk_list_item_tracker_set_position (priv->item_manager,
                                           priv->focus,
                                           pos,
@@ -2320,3 +2331,42 @@ gtk_list_base_get_tab_behavior (GtkListBase *self)
   return priv->tab_behavior;
 }
 
+void
+gtk_list_base_scroll_to (GtkListBase        *self,
+                         guint               pos,
+                         GtkListScrollFlags  flags,
+                         GtkScrollInfo      *scroll)
+{
+  GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+
+  if (flags & GTK_LIST_SCROLL_FOCUS)
+    {
+      GtkListItemTracker *old_focus;
+
+      /* We need a tracker here to keep the focus widget around,
+       * because we need to update the focus tracker before grabbing
+       * focus, because otherwise gtk_list_base_set_focus_child() will
+       * scroll to the item, and we want to avoid that.
+       */
+      old_focus = gtk_list_item_tracker_new (priv->item_manager);
+      gtk_list_item_tracker_set_position (priv->item_manager, old_focus, gtk_list_base_get_focus_position (self), 0, 0);
+
+      gtk_list_item_tracker_set_position (priv->item_manager, priv->focus, pos, 0, 0);
+
+      /* XXX: Is this the proper check? */
+      if (gtk_widget_get_state_flags (GTK_WIDGET (self)) & GTK_STATE_FLAG_FOCUS_WITHIN)
+        {
+          GtkListTile *tile = gtk_list_item_manager_get_nth (priv->item_manager, pos, NULL);
+
+          gtk_widget_grab_focus (tile->widget);
+        }
+
+      gtk_list_item_tracker_free (priv->item_manager, old_focus);
+    }
+
+  if (flags & GTK_LIST_SCROLL_SELECT)
+    gtk_list_base_select_item (self, pos, FALSE, FALSE);
+
+  gtk_list_base_scroll_to_item (self, pos, scroll);
+}
+
index fad3872cdc09433de5e255a687a3c98fe58640bf..01afcde913cca78c60fb3b09ba3b82941c2cad3b 100644 (file)
@@ -62,7 +62,6 @@ struct _GtkListBaseClass
 
 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);
 void                   gtk_list_base_get_border_spacing         (GtkListBase            *self,
                                                                  int                    *xspacing,
                                                                  int                    *yspacing);
@@ -95,3 +94,7 @@ GtkListTabBehavior     gtk_list_base_get_tab_behavior           (GtkListBase
 
 void                   gtk_list_base_allocate                   (GtkListBase            *self);
 
+void                   gtk_list_base_scroll_to                  (GtkListBase            *self,
+                                                                 guint                   pos,
+                                                                 GtkListScrollFlags      flags,
+                                                                 GtkScrollInfo          *scroll);
index f06b9cafeca6ac58587cef10f2371abdcd49d5c6..53e8558dd82bed277da0484f7c1c284978dbc8fd 100644 (file)
@@ -1349,3 +1349,30 @@ gtk_list_view_get_tab_behavior (GtkListView *self)
   return gtk_list_base_get_tab_behavior (GTK_LIST_BASE (self));
 }
 
+/**
+ * gtk_list_view_scroll_to:
+ * @self: The listview to scroll in
+ * @pos: position of the item
+ * @flags: actions to perform
+ * @scroll: (nullable) (transfer full): details of how to perform
+ *   the scroll operation or %NULL to scroll into view 
+ *
+ * Scrolls to the item at the given position and performs the actions
+ * specified in @flags.
+ *
+ * This function works no matter if the listview is shown or focused.
+ * If it isn't, then the changes will take effect once that happens.
+ *
+ * Since: 4.12
+ */
+void
+gtk_list_view_scroll_to (GtkListView        *self,
+                         guint               pos,
+                         GtkListScrollFlags  flags,
+                         GtkScrollInfo      *scroll)
+{
+  g_return_if_fail (GTK_IS_LIST_VIEW (self));
+
+  gtk_list_base_scroll_to (GTK_LIST_BASE (self), pos, flags, scroll);
+}
+
index df4901e319a1e2d84f1b3ebc62c62fa8142d5fb3..1948f4236c198e6c00c90290d590aa2995d08838 100644 (file)
@@ -89,6 +89,11 @@ GDK_AVAILABLE_IN_4_12
 GtkListTabBehavior
                 gtk_list_view_get_tab_behavior                  (GtkListView            *self);
 
+GDK_AVAILABLE_IN_4_12
+void            gtk_list_view_scroll_to                         (GtkListView            *self,
+                                                                 guint                   pos,
+                                                                 GtkListScrollFlags      flags,
+                                                                 GtkScrollInfo          *scroll);
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListView, g_object_unref)