From 3ffb4fa6993c461a591a250ab96d09ddae6de8d0 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Thu, 5 Jun 2014 15:45:21 +0200 Subject: [PATCH] flowbox: Use gestures for event management A multipress gesture takes care of item selection/activation, and a drag gesture is used to manage events when rubberbanding selection is performed. --- gtk/gtkflowbox.c | 291 ++++++++++++++++++++++++++++------------------- 1 file changed, 172 insertions(+), 119 deletions(-) diff --git a/gtk/gtkflowbox.c b/gtk/gtkflowbox.c index dc8090a1b1..7e7be81b15 100644 --- a/gtk/gtkflowbox.c +++ b/gtk/gtkflowbox.c @@ -778,15 +778,14 @@ struct _GtkFlowBoxPrivate { gpointer sort_data; GDestroyNotify sort_destroy; - gboolean track_motion; + GtkGesture *multipress_gesture; + GtkGesture *drag_gesture; + gboolean rubberband_select; GtkFlowBoxChild *rubberband_first; GtkFlowBoxChild *rubberband_last; - gint button_down_x; - gint button_down_y; gboolean rubberband_modify; gboolean rubberband_extend; - GdkDevice *rubberband_device; GtkScrollType autoscroll_mode; guint autoscroll_id; @@ -2686,12 +2685,12 @@ autoscroll_cb (GtkWidget *widget, if (priv->rubberband_select) { - gint x, y; + GdkEventSequence *sequence; + gdouble x, y; GtkFlowBoxChild *child; - gdk_window_get_device_position (gtk_widget_get_window (widget), - priv->rubberband_device, - &x, &y, NULL); + sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (priv->drag_gesture)); + gtk_gesture_get_point (priv->drag_gesture, sequence, &x, &y); child = gtk_flow_box_find_child_at_pos (box, x, y); @@ -2732,6 +2731,8 @@ get_view_rect (GtkFlowBox *box, if (GTK_IS_VIEWPORT (parent)) { view = gtk_viewport_get_view_window (GTK_VIEWPORT (parent)); + rect->x = rect->y = 0; + rect->x = gtk_adjustment_get_value (priv->hadjustment); rect->y = gtk_adjustment_get_value (priv->vadjustment); rect->width = gdk_window_get_width (view); @@ -2823,12 +2824,49 @@ gtk_flow_box_leave_notify_event (GtkWidget *widget, return FALSE; } +static void +gtk_flow_box_drag_gesture_update (GtkGestureDrag *gesture, + gdouble offset_x, + gdouble offset_y, + GtkFlowBox *box) +{ + GtkFlowBoxPrivate *priv = BOX_PRIV (box); + gdouble start_x, start_y; + GtkFlowBoxChild *child; + + gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y); + + if (!priv->rubberband_select && + (offset_x * offset_x) + (offset_y * offset_y) > RUBBERBAND_START_DISTANCE * RUBBERBAND_START_DISTANCE) + { + priv->rubberband_select = TRUE; + priv->rubberband_first = gtk_flow_box_find_child_at_pos (box, start_x, start_y); + + /* Grab focus here, so Escape-to-stop-rubberband works */ + gtk_flow_box_update_cursor (box, priv->rubberband_first); + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + } + + if (priv->rubberband_select) + { + child = gtk_flow_box_find_child_at_pos (box, start_x + offset_x, + start_y + offset_y); + + if (priv->rubberband_first == NULL) + priv->rubberband_first = child; + if (child != NULL) + priv->rubberband_last = child; + + update_autoscroll_mode (box, start_x + offset_x, start_y + offset_y); + gtk_widget_queue_draw (GTK_WIDGET (box)); + } +} + static gboolean gtk_flow_box_motion_notify_event (GtkWidget *widget, GdkEventMotion *event) { GtkFlowBox *box = GTK_FLOW_BOX (widget); - GtkFlowBoxPrivate *priv = BOX_PRIV (box); GtkFlowBoxChild *child; GdkWindow *window; GdkWindow *event_window; @@ -2856,71 +2894,99 @@ gtk_flow_box_motion_notify_event (GtkWidget *widget, gtk_flow_box_update_prelight (box, child); gtk_flow_box_update_active (box, child); - if (priv->track_motion) - { - if (!priv->rubberband_select && - (event->x - priv->button_down_x) * (event->x - priv->button_down_x) + - (event->y - priv->button_down_y) * (event->y - priv->button_down_y) > RUBBERBAND_START_DISTANCE * RUBBERBAND_START_DISTANCE) - { - priv->rubberband_select = TRUE; - priv->rubberband_first = gtk_flow_box_find_child_at_pos (box, priv->button_down_x, priv->button_down_y); - - /* Grab focus here, so Escape-to-stop-rubberband works */ - gtk_flow_box_update_cursor (box, priv->rubberband_first); - } - - if (priv->rubberband_select) - { - if (priv->rubberband_first == NULL) - priv->rubberband_first = child; - if (child != NULL) - priv->rubberband_last = child; - - update_autoscroll_mode (box, event->x, event->y); - } - } - - return FALSE; + return GTK_WIDGET_CLASS (gtk_flow_box_parent_class)->motion_notify_event (widget, event); } -static gboolean -gtk_flow_box_button_press_event (GtkWidget *widget, - GdkEventButton *event) +static void +gtk_flow_box_multipress_gesture_pressed (GtkGestureMultiPress *gesture, + guint n_press, + gdouble x, + gdouble y, + GtkFlowBox *box) { - GtkFlowBox *box = GTK_FLOW_BOX (widget); GtkFlowBoxPrivate *priv = BOX_PRIV (box); GtkFlowBoxChild *child; - if (event->button == GDK_BUTTON_PRIMARY) - { - child = gtk_flow_box_find_child_at_pos (box, event->x, event->y); - if (child != NULL) - { - priv->active_child = child; - priv->active_child_active = TRUE; - gtk_widget_queue_draw (GTK_WIDGET (box)); - if (event->type == GDK_2BUTTON_PRESS && - !priv->activate_on_single_click) - { - g_signal_emit (box, signals[CHILD_ACTIVATED], 0, child); - return TRUE; - } - } + child = gtk_flow_box_find_child_at_pos (box, x, y); - if (priv->selection_mode == GTK_SELECTION_MULTIPLE) + if (child == NULL) + return; + + /* The drag gesture is only triggered by first press */ + if (n_press != 1) + gtk_gesture_set_state (priv->drag_gesture, GTK_EVENT_SEQUENCE_DENIED); + + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + priv->active_child = child; + priv->active_child_active = TRUE; + gtk_widget_queue_draw (GTK_WIDGET (box)); + + if (n_press == 2 && !priv->activate_on_single_click) + g_signal_emit (box, signals[CHILD_ACTIVATED], 0, child); +} + +static void +gtk_flow_box_multipress_gesture_released (GtkGestureMultiPress *gesture, + guint n_press, + gdouble x, + gdouble y, + GtkFlowBox *box) +{ + GtkFlowBoxPrivate *priv = BOX_PRIV (box); + + if (priv->active_child != NULL && priv->active_child_active) + { + if (priv->activate_on_single_click) + gtk_flow_box_select_and_activate (box, priv->active_child); + else { - priv->track_motion = TRUE; - priv->rubberband_select = FALSE; - priv->rubberband_first = NULL; - priv->rubberband_last = NULL; - priv->button_down_x = event->x; - priv->button_down_y = event->y; - priv->rubberband_device = gdk_event_get_device ((GdkEvent*)event); - get_current_selection_modifiers (widget, &priv->rubberband_modify, &priv->rubberband_extend); + GdkEventSequence *sequence; + GdkInputSource source; + const GdkEvent *event; + gboolean modify; + gboolean extend; + + get_current_selection_modifiers (GTK_WIDGET (box), &modify, &extend); + + /* With touch, we default to modifying the selection. + * You can still clear the selection and start over + * by holding Ctrl. + */ + + sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); + event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); + source = gdk_device_get_source (gdk_event_get_source_device (event)); + + if (source == GDK_SOURCE_TOUCHSCREEN) + modify = !modify; + + gtk_flow_box_update_selection (box, priv->active_child, modify, extend); } } - return FALSE; + priv->active_child = NULL; + priv->active_child_active = FALSE; + gtk_widget_queue_draw (GTK_WIDGET (box)); +} + +static void +gtk_flow_box_drag_gesture_begin (GtkGestureDrag *gesture, + gdouble start_x, + gdouble start_y, + GtkWidget *widget) +{ + GtkFlowBoxPrivate *priv = BOX_PRIV (widget); + + if (priv->selection_mode != GTK_SELECTION_MULTIPLE) + { + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); + return; + } + + priv->rubberband_select = FALSE; + priv->rubberband_first = NULL; + priv->rubberband_last = NULL; + get_current_selection_modifiers (widget, &priv->rubberband_modify, &priv->rubberband_extend); } static void @@ -2931,65 +2997,40 @@ gtk_flow_box_stop_rubberband (GtkFlowBox *box) priv->rubberband_select = FALSE; priv->rubberband_first = NULL; priv->rubberband_last = NULL; - priv->rubberband_device = NULL; remove_autoscroll (box); gtk_widget_queue_draw (GTK_WIDGET (box)); } -static gboolean -gtk_flow_box_button_release_event (GtkWidget *widget, - GdkEventButton *event) +static void +gtk_flow_box_drag_gesture_end (GtkGestureDrag *gesture, + gdouble offset_x, + gdouble offset_y, + GtkFlowBox *box) { - GtkFlowBox *box = GTK_FLOW_BOX (widget); GtkFlowBoxPrivate *priv = BOX_PRIV (box); + GdkEventSequence *sequence; - if (event->button == GDK_BUTTON_PRIMARY) - { - if (priv->active_child != NULL && priv->active_child_active) - { - if (priv->activate_on_single_click) - gtk_flow_box_select_and_activate (box, priv->active_child); - else - { - gboolean modify; - gboolean extend; - GdkDevice *device; + if (!priv->rubberband_select) + return; - get_current_selection_modifiers (widget, &modify, &extend); + sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); - /* With touch, we default to modifying the selection. - * You can still clear the selection and start over - * by holding Ctrl. - */ - device = gdk_event_get_source_device ((GdkEvent *)event); - if (gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN) - modify = !modify; - - gtk_flow_box_update_selection (box, priv->active_child, modify, extend); - } - } - - priv->active_child = NULL; - priv->active_child_active = FALSE; - gtk_widget_queue_draw (GTK_WIDGET (box)); - } - - priv->track_motion = FALSE; - if (priv->rubberband_select) + if (gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence)) { if (!priv->rubberband_extend && !priv->rubberband_modify) gtk_flow_box_unselect_all_internal (box); - gtk_flow_box_select_all_between (box, priv->rubberband_first, priv->rubberband_last, priv->rubberband_modify); + gtk_flow_box_select_all_between (box, priv->rubberband_first, priv->rubberband_last, priv->rubberband_modify); gtk_flow_box_stop_rubberband (box); g_signal_emit (box, signals[SELECTED_CHILDREN_CHANGED], 0); - } + else + gtk_flow_box_stop_rubberband (box); - return FALSE; + gtk_widget_queue_draw (GTK_WIDGET (box)); } static gboolean @@ -3011,20 +3052,6 @@ gtk_flow_box_key_press_event (GtkWidget *widget, return GTK_WIDGET_CLASS (gtk_flow_box_parent_class)->key_press_event (widget, event); } -static void -gtk_flow_box_grab_notify (GtkWidget *widget, - gboolean was_grabbed) -{ - GtkFlowBox *box = GTK_FLOW_BOX (widget); - GtkFlowBoxPrivate *priv = BOX_PRIV (box); - - if (!was_grabbed) - { - if (priv->rubberband_select) - gtk_flow_box_stop_rubberband (box); - } -} - /* Realize and map {{{3 */ static void @@ -3570,6 +3597,9 @@ gtk_flow_box_finalize (GObject *obj) g_clear_object (&priv->hadjustment); g_clear_object (&priv->vadjustment); + g_object_unref (priv->drag_gesture); + g_object_unref (priv->multipress_gesture); + G_OBJECT_CLASS (gtk_flow_box_parent_class)->finalize (obj); } @@ -3593,10 +3623,7 @@ gtk_flow_box_class_init (GtkFlowBoxClass *class) widget_class->unmap = gtk_flow_box_unmap; widget_class->focus = gtk_flow_box_focus; widget_class->draw = gtk_flow_box_draw; - widget_class->button_press_event = gtk_flow_box_button_press_event; - widget_class->button_release_event = gtk_flow_box_button_release_event; widget_class->key_press_event = gtk_flow_box_key_press_event; - widget_class->grab_notify = gtk_flow_box_grab_notify; widget_class->get_request_mode = gtk_flow_box_get_request_mode; widget_class->get_preferred_width = gtk_flow_box_get_preferred_width; widget_class->get_preferred_height = gtk_flow_box_get_preferred_height; @@ -3934,6 +3961,32 @@ gtk_flow_box_init (GtkFlowBox *box) priv->activate_on_single_click = TRUE; priv->children = g_sequence_new (NULL); + + priv->multipress_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (box)); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->multipress_gesture), + FALSE); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->multipress_gesture), + GDK_BUTTON_PRIMARY); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->multipress_gesture), + GTK_PHASE_BUBBLE); + g_signal_connect (priv->multipress_gesture, "pressed", + G_CALLBACK (gtk_flow_box_multipress_gesture_pressed), box); + g_signal_connect (priv->multipress_gesture, "released", + G_CALLBACK (gtk_flow_box_multipress_gesture_released), box); + + priv->drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (box)); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->drag_gesture), + FALSE); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->drag_gesture), + GDK_BUTTON_PRIMARY); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->drag_gesture), + GTK_PHASE_CAPTURE); + g_signal_connect (priv->drag_gesture, "drag-begin", + G_CALLBACK (gtk_flow_box_drag_gesture_begin), box); + g_signal_connect (priv->drag_gesture, "drag-update", + G_CALLBACK (gtk_flow_box_drag_gesture_update), box); + g_signal_connect (priv->drag_gesture, "drag-end", + G_CALLBACK (gtk_flow_box_drag_gesture_end), box); } /* Public API {{{2 */