[gtk/wip/otte/listview: 36/135] listitem: Add a press gesture to select the item



commit a3e442b56b9f22ea1409973bdd5a9bfd2ba6ac6c
Author: Benjamin Otte <otte redhat com>
Date:   Fri Oct 5 23:24:18 2018 +0200

    listitem: Add a press gesture to select the item
    
    This is implemented by using actions, which are a neat trick to get to
    allow the ListItem to call functions on the ListView without actually
    needing to be aware of it.

 gtk/gtklistitem.c               | 91 +++++++++++++++++++++++++++++++++++++++--
 gtk/gtklistitemfactory.c        | 15 +++----
 gtk/gtklistitemfactoryprivate.h |  3 +-
 gtk/gtklistitemmanager.c        |  4 +-
 gtk/gtklistitemprivate.h        |  4 +-
 gtk/gtklistview.c               | 51 +++++++++++++++++++++++
 6 files changed, 151 insertions(+), 17 deletions(-)
---
diff --git a/gtk/gtklistitem.c b/gtk/gtklistitem.c
index b1461dd386..cc51953835 100644
--- a/gtk/gtklistitem.c
+++ b/gtk/gtklistitem.c
@@ -23,7 +23,9 @@
 
 #include "gtkbinlayout.h"
 #include "gtkcssnodeprivate.h"
+#include "gtkgestureclick.h"
 #include "gtkintl.h"
+#include "gtkmain.h"
 #include "gtkselectionmodel.h" /* for GTK_INVALID_LIST_POSITION */
 #include "gtkwidget.h"
 #include "gtkwidgetprivate.h"
@@ -233,20 +235,99 @@ gtk_list_item_class_init (GtkListItemClass *klass)
   gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
 }
 
+static void
+gtk_list_item_click_gesture_pressed (GtkGestureClick *gesture,
+                                     int              n_press,
+                                     double           x,
+                                     double           y,
+                                     GtkListItem     *self)
+{
+  GtkWidget *widget = GTK_WIDGET (self);
+  GdkModifierType state;
+  GdkModifierType mask;
+  gboolean extend = FALSE, modify = FALSE;
+
+  if (!self->selectable)
+    {
+      gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+      return;
+    }
+
+  if (gtk_get_current_event_state (&state))
+    {
+      mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_MODIFY_SELECTION);
+      if ((state & mask) == mask)
+        modify = TRUE;
+      mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION);
+      if ((state & mask) == mask)
+        extend = TRUE;
+    }
+
+  gtk_widget_activate_action (GTK_WIDGET (self),
+                              "list.select-item",
+                              "(ubb)",
+                              self->position, modify, extend);
+
+  gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_ACTIVE, FALSE);
+
+  if (gtk_widget_get_focus_on_click (widget))
+    gtk_widget_grab_focus (widget);
+}
+
+static void
+gtk_list_item_click_gesture_released (GtkGestureClick *gesture,
+                                      int              n_press,
+                                      double           x,
+                                      double           y,
+                                      GtkListItem     *self)
+{
+  gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
+}
+
+static void
+gtk_list_item_click_gesture_canceled (GtkGestureClick  *gesture,
+                                      GdkEventSequence *sequence,
+                                      GtkListItem      *self)
+{
+  gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
+}
+
 static void
 gtk_list_item_init (GtkListItem *self)
 {
+  GtkGesture *gesture;
+
   self->selectable = TRUE;
+  gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
+
+  gesture = gtk_gesture_click_new ();
+  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
+                                              GTK_PHASE_BUBBLE);
+  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture),
+                                     FALSE);
+  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture),
+                                 GDK_BUTTON_PRIMARY);
+  g_signal_connect (gesture, "pressed",
+                    G_CALLBACK (gtk_list_item_click_gesture_pressed), self);
+  g_signal_connect (gesture, "released",
+                    G_CALLBACK (gtk_list_item_click_gesture_released), 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));
 }
 
-GtkWidget *
+GtkListItem *
 gtk_list_item_new (const char *css_name)
 {
+  GtkListItem *result;
+
   g_return_val_if_fail (css_name != NULL, NULL);
 
-  return g_object_new (GTK_TYPE_LIST_ITEM,
-                       "css-name", css_name,
-                       NULL);
+  result = g_object_new (GTK_TYPE_LIST_ITEM,
+                         "css-name", css_name,
+                         NULL);
+
+  return result;
 }
 
 /**
@@ -449,5 +530,7 @@ gtk_list_item_set_selectable (GtkListItem *self,
 
   self->selectable = selectable;
 
+  gtk_widget_set_can_focus (GTK_WIDGET (self), self->selectable);
+
   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTABLE]);
 }
diff --git a/gtk/gtklistitemfactory.c b/gtk/gtklistitemfactory.c
index 6b7c2b3550..9115b1c3f8 100644
--- a/gtk/gtklistitemfactory.c
+++ b/gtk/gtklistitemfactory.c
@@ -85,19 +85,14 @@ gtk_list_item_factory_new (GtkListItemSetupFunc setup_func,
   return self;
 }
 
-GtkListItem *
-gtk_list_item_factory_create (GtkListItemFactory *self)
+void
+gtk_list_item_factory_setup (GtkListItemFactory *self,
+                             GtkListItem         *list_item)
 {
-  GtkWidget *result;
-
-  g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (self), NULL);
-
-  result = gtk_list_item_new ("row");
+  g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
 
   if (self->setup_func)
-    self->setup_func (GTK_LIST_ITEM (result), self->user_data);
-
-  return GTK_LIST_ITEM (result);
+    self->setup_func (list_item, self->user_data);
 }
 
 void
diff --git a/gtk/gtklistitemfactoryprivate.h b/gtk/gtklistitemfactoryprivate.h
index 3e815fa131..dc538f09fa 100644
--- a/gtk/gtklistitemfactoryprivate.h
+++ b/gtk/gtklistitemfactoryprivate.h
@@ -43,7 +43,8 @@ GtkListItemFactory *    gtk_list_item_factory_new               (GtkListItemSetu
                                                                  gpointer                user_data,
                                                                  GDestroyNotify          user_destroy);
 
-GtkListItem *           gtk_list_item_factory_create            (GtkListItemFactory     *self);
+void                    gtk_list_item_factory_setup             (GtkListItemFactory     *self,
+                                                                 GtkListItem            *list_item);
 
 void                    gtk_list_item_factory_bind              (GtkListItemFactory     *self,
                                                                  GtkListItem            *list_item,
diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c
index e7c11f483f..00bce1a10b 100644
--- a/gtk/gtklistitemmanager.c
+++ b/gtk/gtklistitemmanager.c
@@ -21,6 +21,7 @@
 
 #include "gtklistitemmanagerprivate.h"
 
+#include "gtklistitemprivate.h"
 #include "gtkwidgetprivate.h"
 
 struct _GtkListItemManager
@@ -260,7 +261,8 @@ gtk_list_item_manager_acquire_list_item (GtkListItemManager *self,
   g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
   g_return_val_if_fail (prev_sibling == NULL || GTK_IS_WIDGET (prev_sibling), NULL);
 
-  result = gtk_list_item_factory_create (self->factory);
+  result = gtk_list_item_new ("row");
+  gtk_list_item_factory_setup (self->factory, result);
 
   item = g_list_model_get_item (G_LIST_MODEL (self->model), position);
   selected = gtk_selection_model_is_selected (self->model, position);
diff --git a/gtk/gtklistitemprivate.h b/gtk/gtklistitemprivate.h
index 08ed82f162..3a2ac463d1 100644
--- a/gtk/gtklistitemprivate.h
+++ b/gtk/gtklistitemprivate.h
@@ -22,9 +22,11 @@
 
 #include "gtklistitem.h"
 
+#include "gtklistitemmanagerprivate.h"
+
 G_BEGIN_DECLS
 
-GtkWidget *     gtk_list_item_new                               (const char             *css_name);
+GtkListItem *   gtk_list_item_new                               (const char             *css_name);
 
 void            gtk_list_item_set_item                          (GtkListItem            *self,
                                                                  gpointer                item);
diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c
index 494a2a591b..36c18d698b 100644
--- a/gtk/gtklistview.c
+++ b/gtk/gtklistview.c
@@ -1201,6 +1201,34 @@ gtk_list_view_set_property (GObject      *object,
     }
 }
 
+static void
+gtk_list_view_select_item (GtkWidget  *widget,
+                           const char *action_name,
+                           GVariant   *parameter)
+{
+  GtkListView *self = GTK_LIST_VIEW (widget);
+  GtkSelectionModel *selection_model;
+  guint pos;
+  gboolean modify, extend;
+
+  selection_model = gtk_list_item_manager_get_model (self->item_manager);
+  g_variant_get (parameter, "(ubb)", &pos, &modify, &extend);
+
+  /* XXX: handle extend by tracking the item to extend from */
+
+  if (modify)
+    {
+      if (gtk_selection_model_is_selected (selection_model, pos))
+        gtk_selection_model_unselect_item (selection_model, pos);
+      else
+        gtk_selection_model_select_item (selection_model, pos, FALSE);
+    }
+  else
+    {
+      gtk_selection_model_select_item (selection_model, pos, TRUE);
+    }
+}
+
 static void
 gtk_list_view_class_init (GtkListViewClass *klass)
 {
@@ -1245,6 +1273,29 @@ gtk_list_view_class_init (GtkListViewClass *klass)
 
   g_object_class_install_properties (gobject_class, N_PROPS, properties);
 
+  /**
+   * GtkListView|list.select-item:
+   * @position: position of item to select
+   * @modify: %TRUE to toggle the existing selection, %FALSE to select
+   * @extend: %TRUE to extend the selection
+   *
+   * Changes selection.
+   *
+   * If @extend is %TRUE and the model supports selecting ranges, the
+   * affected items are all items from the last selected item to the item
+   * in @position.
+   * If @extend is %FALSE or selecting ranges is not supported, only the
+   * item in @position is affected.
+   *
+   * If @modify is %TRUE, the affected items will be set to the same state.
+   * If @modify is %FALSE, the affected items will be selected and
+   * all other items will be deselected.
+   */
+  gtk_widget_class_install_action (widget_class,
+                                   "list.select-item",
+                                   "(ubb)",
+                                   gtk_list_view_select_item);
+
   gtk_widget_class_set_css_name (widget_class, I_("list"));
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]