gtkentry: Use gestures for entry icons
authorCarlos Garnacho <carlosg@gnome.org>
Wed, 6 Jun 2018 11:52:08 +0000 (13:52 +0200)
committerCarlos Garnacho <carlosg@gnome.org>
Thu, 21 Jun 2018 10:50:57 +0000 (12:50 +0200)
Instead of doing all handling manually in the ::event vfunc,
set up drag/multipress gestures on icon images, and implement
emission of ::icon-press/release and DnD there.

As a side effect, the GdkEvent field in ::icon-press/release
signals has been dropped. Callers that might be interested on it
may still use gtk_get_current_event*().

demos/gtk-demo/popover.c
demos/gtk-demo/search_entry.c
demos/widget-factory/widget-factory.c
gtk/a11y/gtkentryaccessible.c
gtk/gtkentry.c
gtk/gtkplacesview.c
tests/testentryicons.c

index f49949adf0d6020b25e61c18bfca4a914ceb8396..2feb48ac700a128ed6412b23d7f34a4934e88139 100644 (file)
@@ -77,7 +77,6 @@ entry_size_allocate_cb (GtkEntry            *entry,
 static void
 entry_icon_press_cb (GtkEntry             *entry,
                      GtkEntryIconPosition  icon_pos,
-                     GdkEvent             *event,
                      gpointer              user_data)
 {
   GtkWidget *popover = user_data;
index 6d6f1ec143ffe534955c1122d04ac39afa4f7b7d..d45afa290ba1d396e99efee19e9fb03e28fb09df 100644 (file)
@@ -151,11 +151,10 @@ create_search_menu (GtkWidget *entry)
 static void
 icon_press_cb (GtkEntry       *entry,
                gint            position,
-               GdkEventButton *event,
                gpointer        data)
 {
   if (position == GTK_ENTRY_ICON_PRIMARY)
-    gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *) event);
+    gtk_menu_popup_at_pointer (GTK_MENU (menu), NULL);
 }
 
 static void
index 8b8e852dd5bac8258866783b48c6fb0f57b07e15..836eae77afb506b2bcf21f973631f055007ef3f5 100644 (file)
@@ -353,7 +353,6 @@ update_pulse_time (GtkAdjustment *adjustment, GtkWidget *widget)
 static void
 on_entry_icon_release (GtkEntry            *entry,
                        GtkEntryIconPosition icon_pos,
-                       GdkEvent            *event,
                        gpointer             user_data)
 {
   if (icon_pos != GTK_ENTRY_ICON_SECONDARY)
index de106c3cf049ee3312bbdd32fe921bde09456b21..f8aaabad87d28de7dccc989d1673c7e0557a51e8 100644 (file)
@@ -221,8 +221,6 @@ gtk_entry_icon_accessible_do_action (AtkAction *action,
   GtkEntryIconAccessible *icon = (GtkEntryIconAccessible *)action;
   GtkWidget *widget;
   GtkEntry *gtk_entry;
-  GdkEvent *event;
-  GdkRectangle icon_area;
 
   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (icon->entry));
   if (widget == NULL)
@@ -240,18 +238,7 @@ gtk_entry_icon_accessible_do_action (AtkAction *action,
       !gtk_entry_get_icon_activatable (gtk_entry, icon->pos))
     return FALSE;
 
-  gtk_entry_get_icon_area (gtk_entry, icon->pos, &icon_area);
-
-  event = gdk_event_new (GDK_BUTTON_PRESS);
-  event->any.surface = g_object_ref (gtk_widget_get_surface (widget));
-  event->button.button = 1;
-  event->any.send_event = TRUE;
-  event->button.time = GDK_CURRENT_TIME;
-  event->button.x = icon_area.x;
-  event->button.y = icon_area.y;
-
-  g_signal_emit_by_name (widget, "icon-press", 0, icon->pos, event);
-  g_object_unref (event);
+  g_signal_emit_by_name (widget, "icon-press", 0, icon->pos);
   return TRUE;
 }
 
index 75de722cbb012fa60c79af4b94df47fef13cd721..d9863310ffc1cc9f655748b3aa1b9e76a2a836d9 100644 (file)
@@ -284,12 +284,9 @@ struct _EntryIconInfo
   gchar *tooltip;
   guint nonactivatable : 1;
   guint in_drag        : 1;
-  guint pressed        : 1;
 
   GdkDragAction actions;
   GdkContentFormats *target_list;
-  GdkEventSequence *current_sequence;
-  GdkDevice *device;
 };
 
 struct _GtkEntryPasswordHint
@@ -418,8 +415,6 @@ static void   gtk_entry_size_allocate        (GtkWidget           *widget,
                                               int                  baseline);
 static void   gtk_entry_snapshot             (GtkWidget        *widget,
                                               GtkSnapshot      *snapshot);
-static gboolean gtk_entry_event              (GtkWidget        *widget,
-                                              GdkEvent         *event);
 static void   gtk_entry_focus_in             (GtkWidget        *widget);
 static void   gtk_entry_focus_out            (GtkWidget        *widget);
 static void   gtk_entry_grab_focus           (GtkWidget        *widget);
@@ -594,8 +589,6 @@ static void         gtk_entry_do_popup                 (GtkEntry       *entry,
                                                        const GdkEvent *event);
 static gboolean     gtk_entry_mnemonic_activate        (GtkWidget      *widget,
                                                        gboolean        group_cycling);
-static void         gtk_entry_grab_notify              (GtkWidget      *widget,
-                                                        gboolean        was_grabbed);
 static void         gtk_entry_check_cursor_blink       (GtkEntry       *entry);
 static void         gtk_entry_pend_cursor_blink        (GtkEntry       *entry);
 static void         gtk_entry_reset_blink_time         (GtkEntry       *entry);
@@ -790,7 +783,6 @@ gtk_entry_class_init (GtkEntryClass *class)
   widget_class->measure = gtk_entry_measure;
   widget_class->size_allocate = gtk_entry_size_allocate;
   widget_class->snapshot = gtk_entry_snapshot;
-  widget_class->event = gtk_entry_event;
   widget_class->grab_focus = gtk_entry_grab_focus;
   widget_class->style_updated = gtk_entry_style_updated;
   widget_class->query_tooltip = gtk_entry_query_tooltip;
@@ -800,7 +792,6 @@ gtk_entry_class_init (GtkEntryClass *class)
   widget_class->state_flags_changed = gtk_entry_state_flags_changed;
   widget_class->display_changed = gtk_entry_display_changed;
   widget_class->mnemonic_activate = gtk_entry_mnemonic_activate;
-  widget_class->grab_notify = gtk_entry_grab_notify;
 
   widget_class->drag_drop = gtk_entry_drag_drop;
   widget_class->drag_motion = gtk_entry_drag_motion;
@@ -1651,7 +1642,6 @@ gtk_entry_class_init (GtkEntryClass *class)
    * GtkEntry::icon-press:
    * @entry: The entry on which the signal is emitted
    * @icon_pos: The position of the clicked icon
-   * @event: (type Gdk.EventButton): the button press event
    *
    * The ::icon-press signal is emitted when an activatable icon
    * is clicked.
@@ -1662,16 +1652,14 @@ gtk_entry_class_init (GtkEntryClass *class)
                   G_SIGNAL_RUN_LAST,
                   0,
                   NULL, NULL,
-                  _gtk_marshal_VOID__ENUM_OBJECT,
-                  G_TYPE_NONE, 2,
-                  GTK_TYPE_ENTRY_ICON_POSITION,
-                  GDK_TYPE_EVENT);
-  
+                  g_cclosure_marshal_VOID__ENUM,
+                  G_TYPE_NONE, 1,
+                  GTK_TYPE_ENTRY_ICON_POSITION);
+
   /**
    * GtkEntry::icon-release:
    * @entry: The entry on which the signal is emitted
    * @icon_pos: The position of the clicked icon
-   * @event: (type Gdk.EventButton): the button release event
    *
    * The ::icon-release signal is emitted on the button release from a
    * mouse click over an activatable icon.
@@ -1682,10 +1670,9 @@ gtk_entry_class_init (GtkEntryClass *class)
                   G_SIGNAL_RUN_LAST,
                   0,
                   NULL, NULL,
-                  _gtk_marshal_VOID__ENUM_OBJECT,
-                  G_TYPE_NONE, 2,
-                  GTK_TYPE_ENTRY_ICON_POSITION,
-                  GDK_TYPE_EVENT);
+                  g_cclosure_marshal_VOID__ENUM,
+                  G_TYPE_NONE, 1,
+                  GTK_TYPE_ENTRY_ICON_POSITION);
 
   /**
    * GtkEntry::preedit-changed:
@@ -2965,6 +2952,91 @@ update_node_ordering (GtkEntry *entry)
     gtk_css_node_insert_before (parent, gtk_widget_get_css_node (icon_info->widget), NULL);
 }
 
+static GtkEntryIconPosition
+get_icon_position_from_controller (GtkEntry           *entry,
+                                   GtkEventController *controller)
+{
+  GtkEntryPrivate *priv = gtk_entry_get_instance_private (entry);
+  GtkWidget *widget = gtk_event_controller_get_widget (controller);
+
+  if (priv->icons[GTK_ENTRY_ICON_PRIMARY] &&
+      priv->icons[GTK_ENTRY_ICON_PRIMARY]->widget == widget)
+    return GTK_ENTRY_ICON_PRIMARY;
+  else if (priv->icons[GTK_ENTRY_ICON_SECONDARY] &&
+           priv->icons[GTK_ENTRY_ICON_SECONDARY]->widget == widget)
+    return GTK_ENTRY_ICON_SECONDARY;
+
+  g_assert_not_reached ();
+  return -1;
+}
+
+static void
+icon_pressed_cb (GtkGestureMultiPress *gesture,
+                 gint                  n_press,
+                 gdouble               x,
+                 gdouble               y,
+                 GtkEntry             *entry)
+{
+  GtkEntryPrivate *priv = gtk_entry_get_instance_private (entry);
+  GtkEntryIconPosition pos;
+  EntryIconInfo *icon_info;
+
+  pos = get_icon_position_from_controller (entry, GTK_EVENT_CONTROLLER (gesture));
+  icon_info = priv->icons[pos];
+
+  if (!icon_info->nonactivatable)
+    g_signal_emit (entry, signals[ICON_PRESS], 0, pos);
+
+  gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+}
+
+static void
+icon_released_cb (GtkGestureMultiPress *gesture,
+                  gint                  n_press,
+                  gdouble               x,
+                  gdouble               y,
+                  GtkEntry             *entry)
+{
+  GtkEntryPrivate *priv = gtk_entry_get_instance_private (entry);
+  GtkEntryIconPosition pos;
+  EntryIconInfo *icon_info;
+
+  pos = get_icon_position_from_controller (entry, GTK_EVENT_CONTROLLER (gesture));
+  icon_info = priv->icons[pos];
+
+  if (!icon_info->nonactivatable)
+    g_signal_emit (entry, signals[ICON_RELEASE], 0, pos);
+}
+
+static void
+icon_drag_update_cb (GtkGestureDrag *gesture,
+                     gdouble         x,
+                     gdouble         y,
+                     GtkEntry       *entry)
+{
+  GtkEntryPrivate *priv = gtk_entry_get_instance_private (entry);
+  gdouble start_x, start_y;
+  GtkEntryIconPosition pos;
+  EntryIconInfo *icon_info;
+
+  gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
+  pos = get_icon_position_from_controller (entry, GTK_EVENT_CONTROLLER (gesture));
+  icon_info = priv->icons[pos];
+
+  if (icon_info->target_list != NULL &&
+      gtk_drag_check_threshold (icon_info->widget,
+                                start_x, start_y,
+                                x, y))
+    {
+      icon_info->in_drag = TRUE;
+      gtk_drag_begin_with_coordinates (GTK_WIDGET (entry),
+                                       gtk_gesture_get_device (GTK_GESTURE (gesture)),
+                                       icon_info->target_list,
+                                       icon_info->actions,
+                                       start_x, start_y);
+    }
+}
+
 static EntryIconInfo*
 construct_icon_info (GtkWidget            *widget,
                      GtkEntryIconPosition  icon_pos)
@@ -2972,6 +3044,7 @@ construct_icon_info (GtkWidget            *widget,
   GtkEntry *entry = GTK_ENTRY (widget);
   GtkEntryPrivate *priv = gtk_entry_get_instance_private (entry);
   EntryIconInfo *icon_info;
+  GtkGesture *drag, *press;
 
   g_return_val_if_fail (priv->icons[icon_pos] == NULL, NULL);
 
@@ -2985,6 +3058,20 @@ construct_icon_info (GtkWidget            *widget,
   update_icon_style (widget, icon_pos);
   update_node_ordering (entry);
 
+  press = gtk_gesture_multi_press_new ();
+  g_signal_connect (press, "pressed",
+                    G_CALLBACK (icon_pressed_cb), entry);
+  g_signal_connect (press, "released",
+                    G_CALLBACK (icon_released_cb), entry);
+  gtk_widget_add_controller (icon_info->widget, GTK_EVENT_CONTROLLER (press));
+
+  drag = gtk_gesture_drag_new ();
+  g_signal_connect (drag, "drag-update",
+                    G_CALLBACK (icon_drag_update_cb), entry);
+  gtk_widget_add_controller (icon_info->widget, GTK_EVENT_CONTROLLER (drag));
+
+  gtk_gesture_group (press, drag);
+
   return icon_info;
 }
 
@@ -3511,108 +3598,6 @@ gtk_entry_update_handles (GtkEntry          *entry,
                            cursor, 0, text_allocation.height);
 }
 
-static gboolean
-gtk_entry_event (GtkWidget *widget,
-                 GdkEvent  *event)
-{
-  GtkEntryPrivate *priv = gtk_entry_get_instance_private (GTK_ENTRY (widget));
-  EntryIconInfo *icon_info = NULL;
-  GdkEventSequence *sequence;
-  GdkDevice *device;
-  gdouble x, y;
-  gint i = 0;
-
-  if (!gdk_event_get_coords (event, &x, &y))
-    return GDK_EVENT_PROPAGATE;
-
-  for (i = 0; i < MAX_ICONS; i++)
-    {
-      if (priv->icons[i])
-        {
-          int icon_x, icon_y;
-          gtk_widget_translate_coordinates (widget, priv->icons[i]->widget,
-                                            x, y, &icon_x, &icon_y);
-          if (gtk_widget_contains (priv->icons[i]->widget, icon_x, icon_y))
-            {
-              icon_info = priv->icons[i];
-              break;
-            }
-        }
-    }
-
-  if (!icon_info)
-    return GDK_EVENT_PROPAGATE;
-
-  if (!gtk_widget_get_sensitive (icon_info->widget))
-    return GDK_EVENT_STOP;
-
-  sequence = gdk_event_get_event_sequence (event);
-  device = gdk_event_get_device (event);
-
-  switch ((guint) gdk_event_get_event_type (event))
-    {
-    case GDK_TOUCH_BEGIN:
-      if (icon_info->current_sequence)
-        break;
-
-      icon_info->current_sequence = sequence;
-      /* Fall through */
-    case GDK_BUTTON_PRESS:
-      priv->start_x = x;
-      priv->start_y = y;
-      icon_info->pressed = TRUE;
-      icon_info->device = device;
-
-      if (!icon_info->nonactivatable) {
-        g_signal_emit (widget, signals[ICON_PRESS], 0, i, event);
-      }
-
-      break;
-    case GDK_TOUCH_UPDATE:
-      if (icon_info->device != device ||
-          icon_info->current_sequence != sequence)
-        break;
-      /* Fall through */
-    case GDK_MOTION_NOTIFY:
-      if (icon_info->pressed &&
-          icon_info->target_list != NULL &&
-              gtk_drag_check_threshold (widget,
-                                        priv->start_x,
-                                        priv->start_y,
-                                        x, y))
-        {
-          icon_info->in_drag = TRUE;
-          gtk_drag_begin_with_coordinates (widget,
-                                           device,
-                                           icon_info->target_list,
-                                           icon_info->actions,
-                                           priv->start_x,
-                                           priv->start_y);
-        }
-
-      break;
-    case GDK_TOUCH_END:
-      if (icon_info->device != device ||
-          icon_info->current_sequence != sequence)
-        break;
-
-      icon_info->current_sequence = NULL;
-      /* Fall through */
-    case GDK_BUTTON_RELEASE:
-      icon_info->pressed = FALSE;
-      icon_info->device = NULL;
-
-      if (!icon_info->nonactivatable)
-        g_signal_emit (widget, signals[ICON_RELEASE], 0, i, event);
-
-      break;
-    default:
-      return GDK_EVENT_PROPAGATE;
-    }
-
-  return GDK_EVENT_STOP;
-}
-
 static void
 gesture_get_current_point_in_layout (GtkGestureSingle *gesture,
                                      GtkEntry         *entry,
@@ -7683,8 +7668,6 @@ gtk_entry_set_icon_sensitive (GtkEntry             *entry,
     {
       gtk_widget_set_sensitive (icon_info->widget, sensitive);
 
-      icon_info->pressed = FALSE;
-
       g_object_notify_by_pspec (G_OBJECT (entry),
                                 entry_props[icon_pos == GTK_ENTRY_ICON_PRIMARY
                                             ? PROP_SENSITIVE_PRIMARY
@@ -8168,35 +8151,6 @@ gtk_entry_mnemonic_activate (GtkWidget *widget,
   return GDK_EVENT_STOP;
 }
 
-static void
-check_undo_icon_grab (GtkEntry      *entry,
-                      EntryIconInfo *info)
-{
-  if (!info->device ||
-      !gtk_widget_device_is_shadowed (GTK_WIDGET (entry), info->device))
-    return;
-
-  info->pressed = FALSE;
-  info->current_sequence = NULL;
-  info->device = NULL;
-}
-
-static void
-gtk_entry_grab_notify (GtkWidget *widget,
-                       gboolean   was_grabbed)
-{
-  GtkEntryPrivate *priv = gtk_entry_get_instance_private (GTK_ENTRY (widget));
-  gint i;
-
-  GTK_WIDGET_CLASS (gtk_entry_parent_class)->grab_notify (widget, was_grabbed);
-
-  for (i = 0; i < MAX_ICONS; i++)
-    {
-      if (priv->icons[i])
-        check_undo_icon_grab (GTK_ENTRY (widget), priv->icons[i]);
-    }
-}
-
 static void
 append_action_signal (GtkEntry     *entry,
                      GtkWidget    *menu,
index fd5968dff0ac371f9d5717535f5ed660e8fc38d1..693a28e3b3a35cf5195434d888f3c8003f779099 100644 (file)
@@ -1816,7 +1816,6 @@ out:
 static void
 on_address_entry_show_help_pressed (GtkPlacesView        *view,
                                     GtkEntryIconPosition  icon_pos,
-                                    GdkEvent             *event,
                                     GtkEntry             *entry)
 {
   GtkPlacesViewPrivate *priv;
index 245ab1da2caf3c73d43d31eb9d647a118b0471b9..3395c879ae50967f7902d97f1d5543efa4f68161 100644 (file)
@@ -2,7 +2,7 @@
 #include <stdio.h>
 
 static void
-clear_pressed (GtkEntry *entry, gint icon, GdkEvent *event, gpointer data)
+clear_pressed (GtkEntry *entry, gint icon, gpointer data)
 {
    if (icon == GTK_ENTRY_ICON_SECONDARY)
      gtk_entry_set_text (entry, "");