listview: Add GtkListView::tab-behavior
authorBenjamin Otte <otte@redhat.com>
Fri, 24 Mar 2023 02:00:02 +0000 (03:00 +0100)
committerBenjamin Otte <otte@redhat.com>
Sun, 26 Mar 2023 18:29:47 +0000 (20:29 +0200)
The implementation lives (as always) in GtkListBase.

This is a feature request from the Nautilus developers, who currently do
some hacks to emulate that behavior and it apparently only breaks
sometimes.

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

index af2f3689cae70adc71bc90f54d500c220c7a2196..eb460ea997a95dfa1505627d8b781af8f61066f9 100644 (file)
@@ -265,6 +265,31 @@ typedef enum
   GTK_JUSTIFY_FILL
 } GtkJustification;
 
+/**
+ * GtkListTabBehavior:
+ * @GTK_LIST_TAB_ALL: Cycle through all focusable items of the list
+ * @GTK_LIST_TAB_ITEM: Cycle through a single list element, then move
+ *   focus out of the list. Moving focus between items needs to be
+ *   done with the arrow keys.
+ * @GTK_LIST_TAB_CELL: Cycle only through a single cell, then
+ *   move focus out of the list. Moving focus between cells needs to
+ *   be done with the arrow keys. This is only relevant for
+ *   cell-based widgets like #GtkColumnView, otherwise it behaves
+ *   like `GTK_LIST_TAB_ITEM`.
+ *
+ * Used to configure the focus behavior in the `GTK_DIR_TAB_FORWARD`
+ * and `GTK_DIR_TAB_BACKWARD` direction, like the <kbd>Tab</kbd> key
+ * in a [class@Gtk.ListView].
+ *
+ * Since: 4.12
+ */
+typedef enum
+{
+  GTK_LIST_TAB_ALL,
+  GTK_LIST_TAB_ITEM,
+  GTK_LIST_TAB_CELL
+} GtkListTabBehavior;
+
 /**
  * GtkMessageType:
  * @GTK_MESSAGE_INFO: Informational message
index 4f0d5757775462cdd3f60d00b668d43209395fb5..33161454842658f0ee31d8c8ccc8ee228570d72a 100644 (file)
@@ -68,6 +68,7 @@ struct _GtkListBasePrivate
   GtkOrientation orientation;
   GtkAdjustment *adjustment[2];
   GtkScrollablePolicy scroll_policy[2];
+  GtkListTabBehavior tab_behavior;
 
   GtkListItemTracker *anchor;
   double anchor_align_along;
@@ -554,21 +555,42 @@ gtk_list_base_focus (GtkWidget        *widget,
        * while keeping the selection intact.
        */
       old = GTK_INVALID_LIST_POSITION;
+      if (priv->tab_behavior == GTK_LIST_TAB_ALL)
+        {
+          if (direction == GTK_DIR_TAB_FORWARD)
+            pos = 0;
+          else if (direction == GTK_DIR_TAB_BACKWARD)
+            pos = n_items - 1;
+        }
     }
   else
     {
       switch (direction)
         {
         case GTK_DIR_TAB_FORWARD:
-          pos++;
-          if (pos >= n_items)
-            return FALSE;
+          if (priv->tab_behavior == GTK_LIST_TAB_ALL)
+            {
+              pos++;
+              if (pos >= n_items)
+                return FALSE;
+            }
+          else
+            {
+              return FALSE;
+            }
           break;
 
         case GTK_DIR_TAB_BACKWARD:
-          if (pos == 0)
-            return FALSE;
-          pos--;
+          if (priv->tab_behavior == GTK_LIST_TAB_ALL)
+            {
+              if (pos == 0)
+                return FALSE;
+              pos--;
+            }
+          else
+            {
+              return FALSE;
+            }
           break;
 
         case GTK_DIR_UP:
@@ -1948,6 +1970,7 @@ gtk_list_base_init_real (GtkListBase      *self,
   priv->adjustment[GTK_ORIENTATION_VERTICAL] = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
   g_object_ref_sink (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
 
+  priv->tab_behavior = GTK_LIST_TAB_ALL;
   priv->orientation = GTK_ORIENTATION_VERTICAL;
 
   gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
@@ -2248,3 +2271,21 @@ gtk_list_base_set_model (GtkListBase       *self,
 
   return TRUE;
 }
+
+void
+gtk_list_base_set_tab_behavior (GtkListBase        *self,
+                                GtkListTabBehavior  behavior)
+{
+  GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+
+  priv->tab_behavior = behavior;
+}
+
+GtkListTabBehavior
+gtk_list_base_get_tab_behavior (GtkListBase *self)
+{
+  GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
+
+  return priv->tab_behavior;
+}
+
index 9ee30b79b431f52fde94464aadf66fbee52f164e..cd66e58eccf0d77e0abe649d362d2746e860d6b4 100644 (file)
@@ -85,6 +85,10 @@ void                   gtk_list_base_set_anchor_max_widgets     (GtkListBase
 void                   gtk_list_base_set_enable_rubberband      (GtkListBase            *self,
                                                                  gboolean                enable);
 gboolean               gtk_list_base_get_enable_rubberband      (GtkListBase            *self);
+void                   gtk_list_base_set_tab_behavior           (GtkListBase            *self,
+                                                                 GtkListTabBehavior      behavior);
+GtkListTabBehavior     gtk_list_base_get_tab_behavior           (GtkListBase            *self);
+
 
 void                   gtk_list_base_allocate                   (GtkListBase            *self);
 
index 3e0f277b607ea23612205da97588ba216f9998f2..c7977119a821d497a3bd755bc241629282a4777a 100644 (file)
 #include "gtklistbaseprivate.h"
 #include "gtklistitemmanagerprivate.h"
 #include "gtklistitemwidgetprivate.h"
-#include "gtkmain.h"
-#include "gtkprivate.h"
-#include "gtkrbtreeprivate.h"
-#include "gtkwidgetprivate.h"
 #include "gtkmultiselection.h"
+#include "gtktypebuiltins.h"
+#include "gtkwidgetprivate.h"
 
 /* Maximum number of list items created by the listview.
  * For debugging, you can set this to G_MAXUINT to ensure
 enum
 {
   PROP_0,
+  PROP_ENABLE_RUBBERBAND,
   PROP_FACTORY,
   PROP_MODEL,
   PROP_SHOW_SEPARATORS,
   PROP_SINGLE_CLICK_ACTIVATE,
-  PROP_ENABLE_RUBBERBAND,
+  PROP_TAB_BEHAVIOR,
 
   N_PROPS
 };
@@ -627,6 +626,10 @@ gtk_list_view_get_property (GObject    *object,
 
   switch (property_id)
     {
+    case PROP_ENABLE_RUBBERBAND:
+      g_value_set_boolean (value, gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)));
+      break;
+
     case PROP_FACTORY:
       g_value_set_object (value, self->factory);
       break;
@@ -643,8 +646,8 @@ gtk_list_view_get_property (GObject    *object,
       g_value_set_boolean (value, self->single_click_activate);
       break;
 
-    case PROP_ENABLE_RUBBERBAND:
-      g_value_set_boolean (value, gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)));
+    case PROP_TAB_BEHAVIOR:
+      g_value_set_enum (value, gtk_list_base_get_tab_behavior (GTK_LIST_BASE (self)));
       break;
 
     default:
@@ -663,6 +666,10 @@ gtk_list_view_set_property (GObject      *object,
 
   switch (property_id)
     {
+    case PROP_ENABLE_RUBBERBAND:
+      gtk_list_view_set_enable_rubberband (self, g_value_get_boolean (value));
+      break;
+
     case PROP_FACTORY:
       gtk_list_view_set_factory (self, g_value_get_object (value));
       break;
@@ -679,8 +686,8 @@ gtk_list_view_set_property (GObject      *object,
       gtk_list_view_set_single_click_activate (self, g_value_get_boolean (value));
       break;
 
-    case PROP_ENABLE_RUBBERBAND:
-      gtk_list_view_set_enable_rubberband (self, g_value_get_boolean (value));
+    case PROP_TAB_BEHAVIOR:
+      gtk_list_view_set_tab_behavior (self, g_value_get_enum (value));
       break;
 
     default:
@@ -729,6 +736,16 @@ gtk_list_view_class_init (GtkListViewClass *klass)
   gobject_class->get_property = gtk_list_view_get_property;
   gobject_class->set_property = gtk_list_view_set_property;
 
+  /**
+   * GtkListView:enable-rubberband: (attributes org.gtk.Property.get=gtk_list_view_get_enable_rubberband org.gtk.Property.set=gtk_list_view_set_enable_rubberband)
+   *
+   * Allow rubberband selection.
+   */
+  properties[PROP_ENABLE_RUBBERBAND] =
+    g_param_spec_boolean ("enable-rubberband", NULL, NULL,
+                          FALSE,
+                          G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
   /**
    * GtkListView:factory: (attributes org.gtk.Property.get=gtk_list_view_get_factory org.gtk.Property.set=gtk_list_view_set_factory)
    *
@@ -770,14 +787,17 @@ gtk_list_view_class_init (GtkListViewClass *klass)
                           G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
 
   /**
-   * GtkListView:enable-rubberband: (attributes org.gtk.Property.get=gtk_list_view_get_enable_rubberband org.gtk.Property.set=gtk_list_view_set_enable_rubberband)
+   * GtkListView:tab-behavior: (attributes org.gtk.Property.get=gtk_list_view_get_tab_behavior org.gtk.Property.set=gtk_list_view_set_tab_behavior)
    *
-   * Allow rubberband selection.
+   * Behavior of the <kbd>Tab</kbd> key
+   *
+   * Since: 4.12
    */
-  properties[PROP_ENABLE_RUBBERBAND] =
-    g_param_spec_boolean ("enable-rubberband", NULL, NULL,
-                          FALSE,
-                          G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+  properties[PROP_TAB_BEHAVIOR] =
+    g_param_spec_enum ("tab-behavior", NULL, NULL,
+                       GTK_TYPE_LIST_TAB_BEHAVIOR,
+                       GTK_LIST_TAB_ALL,
+                       G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
 
   g_object_class_install_properties (gobject_class, N_PROPS, properties);
 
@@ -1089,3 +1109,45 @@ gtk_list_view_get_enable_rubberband (GtkListView *self)
 
   return gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self));
 }
+
+/**
+ * gtk_list_view_set_tab_behavior: (attributes org.gtk.Method.set_property=tab-behavior)
+ * @self: a `GtkListView`
+ * @tab_behavior: The desired tab behavior
+ *
+ * Sets the behavior of the <kbd>Tab</kbd> and <kbd>Shift</kbd>+<kbd>Tab</kbd> keys.
+ *
+ * Since: 4.12
+ */
+void
+gtk_list_view_set_tab_behavior (GtkListView        *self,
+                                GtkListTabBehavior  tab_behavior)
+{
+  g_return_if_fail (GTK_IS_LIST_VIEW (self));
+
+  if (tab_behavior == gtk_list_base_get_tab_behavior (GTK_LIST_BASE (self)))
+    return;
+
+  gtk_list_base_set_tab_behavior (GTK_LIST_BASE (self), tab_behavior);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TAB_BEHAVIOR]);
+}
+
+/**
+ * gtk_list_view_get_tab_behavior: (attributes org.gtk.Method.get_property=tab-behavior)
+ * @self: a `GtkListView`
+ *
+ * Gets the behavior set for the <kbd>Tab</kbd> key.
+ *
+ * Returns: The behavior of the <kbd>Tab</kbd> key
+ *
+ * Since: 4.12
+ */
+gboolean
+gtk_list_view_get_tab_behavior (GtkListView *self)
+{
+  g_return_val_if_fail (GTK_IS_LIST_VIEW (self), FALSE);
+
+  return gtk_list_base_get_tab_behavior (GTK_LIST_BASE (self));
+}
+
index 2664b07afb6984d45c3a500f79ea75d361efbde0..e2ee0b8c4dd6798c266da4c123c53744ab8ecf1d 100644 (file)
@@ -76,6 +76,13 @@ void            gtk_list_view_set_enable_rubberband             (GtkListView
 GDK_AVAILABLE_IN_ALL
 gboolean        gtk_list_view_get_enable_rubberband             (GtkListView            *self);
 
+GDK_AVAILABLE_IN_4_12
+void            gtk_list_view_set_tab_behavior                  (GtkListView            *self,
+                                                                 GtkListTabBehavior      tab_behavior);
+GDK_AVAILABLE_IN_4_12
+gboolean        gtk_list_view_get_tab_behavior                  (GtkListView            *self);
+
+
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListView, g_object_unref)
 
 G_END_DECLS