[gtk+] label: Use gestures for event management
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+] label: Use gestures for event management
- Date: Thu, 5 Jun 2014 14:16:39 +0000 (UTC)
commit 8dcdec58ebf149b295d03226dca5103d668d30dd
Author: Carlos Garnacho <carlosg gnome org>
Date: Thu Jun 5 15:36:15 2014 +0200
label: Use gestures for event management
A multipress gesture takes care of link handling, and char/word/all
selection mode on selectable labels. A drag gesture is used for both
text selection and DnD checks on selectable labels.
gtk/gtklabel.c | 403 +++++++++++++++++++++++++++++++++-----------------------
1 files changed, 239 insertions(+), 164 deletions(-)
---
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index b3ed083..1617578 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -229,6 +229,9 @@ struct _GtkLabelPrivate
PangoAttrList *markup_attrs;
PangoLayout *layout;
+ GtkGesture *drag_gesture;
+ GtkGesture *multipress_gesture;
+
gchar *label;
gchar *text;
@@ -375,10 +378,6 @@ static void gtk_label_unrealize (GtkWidget *widget);
static void gtk_label_map (GtkWidget *widget);
static void gtk_label_unmap (GtkWidget *widget);
-static gboolean gtk_label_button_press (GtkWidget *widget,
- GdkEventButton *event);
-static gboolean gtk_label_button_release (GtkWidget *widget,
- GdkEventButton *event);
static gboolean gtk_label_motion (GtkWidget *widget,
GdkEventMotion *event);
static gboolean gtk_label_leave_notify (GtkWidget *widget,
@@ -482,6 +481,26 @@ static GtkLabelLink *gtk_label_get_current_link (GtkLabel *label);
static void emit_activate_link (GtkLabel *label,
GtkLabelLink *link);
+/* Event controller callbacks */
+static void gtk_label_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ GtkLabel *label);
+static void gtk_label_multipress_gesture_released (GtkGestureMultiPress *gesture,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ GtkLabel *label);
+static void gtk_label_drag_gesture_begin (GtkGestureDrag *gesture,
+ gdouble start_x,
+ gdouble start_y,
+ GtkLabel *label);
+static void gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
+ gdouble offset_x,
+ gdouble offset_y,
+ GtkLabel *label);
+
static GtkSizeRequestMode gtk_label_get_request_mode (GtkWidget *widget);
static void gtk_label_get_preferred_width (GtkWidget *widget,
gint *minimum_size,
@@ -557,8 +576,6 @@ gtk_label_class_init (GtkLabelClass *class)
widget_class->unrealize = gtk_label_unrealize;
widget_class->map = gtk_label_map;
widget_class->unmap = gtk_label_unmap;
- widget_class->button_press_event = gtk_label_button_press;
- widget_class->button_release_event = gtk_label_button_release;
widget_class->motion_notify_event = gtk_label_motion;
widget_class->leave_notify_event = gtk_label_leave_notify;
widget_class->hierarchy_changed = gtk_label_hierarchy_changed;
@@ -1258,6 +1275,27 @@ gtk_label_init (GtkLabel *label)
priv->mnemonics_visible = TRUE;
gtk_label_set_text (label, "");
+
+ priv->drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (label));
+ g_signal_connect (priv->drag_gesture, "drag-begin",
+ G_CALLBACK (gtk_label_drag_gesture_begin), label);
+ g_signal_connect (priv->drag_gesture, "drag-update",
+ G_CALLBACK (gtk_label_drag_gesture_update), label);
+ gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->drag_gesture), FALSE);
+ gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (priv->drag_gesture), TRUE);
+ 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_BUBBLE);
+
+ priv->multipress_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (label));
+ g_signal_connect (priv->multipress_gesture, "pressed",
+ G_CALLBACK (gtk_label_multipress_gesture_pressed), label);
+ g_signal_connect (priv->multipress_gesture, "released",
+ G_CALLBACK (gtk_label_multipress_gesture_released), label);
+ gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->multipress_gesture), FALSE);
+ gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (priv->multipress_gesture), TRUE);
+ gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->multipress_gesture),
+ GTK_PHASE_BUBBLE);
}
@@ -3132,6 +3170,9 @@ gtk_label_finalize (GObject *object)
gtk_label_clear_links (label);
g_free (priv->select_info);
+ g_object_unref (priv->drag_gesture);
+ g_object_unref (priv->multipress_gesture);
+
G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
}
@@ -4665,49 +4706,61 @@ out:
return FALSE;
}
-static gboolean
-gtk_label_button_press (GtkWidget *widget,
- GdkEventButton *event)
+static void
+gtk_label_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
+ gint n_press,
+ gdouble widget_x,
+ gdouble widget_y,
+ GtkLabel *label)
{
- GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv = label->priv;
GtkLabelSelectionInfo *info = priv->select_info;
- gint index = 0;
- gint min, max;
+ GtkWidget *widget = GTK_WIDGET (label);
+ GdkEventSequence *sequence;
+ const GdkEvent *event;
+ guint button;
if (info == NULL)
- return FALSE;
+ {
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+ return;
+ }
+
+ button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+ sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+ event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
+
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
if (info->active_link)
{
- if (gdk_event_triggers_context_menu ((GdkEvent *) event))
+ if (gdk_event_triggers_context_menu (event))
{
info->link_clicked = 1;
- gtk_label_do_popup (label, event);
- return TRUE;
+ gtk_label_do_popup (label, (GdkEventButton*) event);
+ return;
}
- else if (event->button == GDK_BUTTON_PRIMARY)
+ else if (button == GDK_BUTTON_PRIMARY)
{
info->link_clicked = 1;
gtk_widget_queue_draw (widget);
if (!info->selectable)
- return TRUE;
+ return;
}
}
if (!info->selectable)
- return FALSE;
+ {
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+ return;
+ }
info->in_drag = FALSE;
info->select_words = FALSE;
if (gdk_event_triggers_context_menu ((GdkEvent *) event))
- {
- gtk_label_do_popup (label, event);
-
- return TRUE;
- }
- else if (event->button == GDK_BUTTON_PRIMARY)
+ gtk_label_do_popup (label, (GdkEventButton*) event);
+ else if (button == GDK_BUTTON_PRIMARY)
{
if (!gtk_widget_has_focus (widget))
{
@@ -4716,118 +4769,55 @@ gtk_label_button_press (GtkWidget *widget,
priv->in_click = FALSE;
}
- if (event->type == GDK_3BUTTON_PRESS)
- {
- gtk_label_select_region_index (label, 0, strlen (priv->text));
- return TRUE;
- }
-
- if (event->type == GDK_2BUTTON_PRESS)
+ if (n_press == 3)
+ gtk_label_select_region_index (label, 0, strlen (priv->text));
+ else if (n_press == 2)
{
info->select_words = TRUE;
gtk_label_select_word (label);
- return TRUE;
- }
-
- get_layout_index (label, event->x, event->y, &index);
-
- min = MIN (info->selection_anchor, info->selection_end);
- max = MAX (info->selection_anchor, info->selection_end);
-
- if ((info->selection_anchor != info->selection_end) &&
- (event->state & GDK_SHIFT_MASK))
- {
- if (index > min && index < max)
- {
- /* truncate selection, but keep it as big as possible */
- if (index - min > max - index)
- max = index;
- else
- min = index;
- }
- else
- {
- /* extend (same as motion) */
- min = MIN (min, index);
- max = MAX (max, index);
- }
-
- /* ensure the anchor is opposite index */
- if (index == min)
- {
- gint tmp = min;
- min = max;
- max = tmp;
- }
-
- gtk_label_select_region_index (label, min, max);
- }
- else
- {
- if (event->type == GDK_3BUTTON_PRESS)
- gtk_label_select_region_index (label, 0, strlen (priv->text));
- else if (event->type == GDK_2BUTTON_PRESS)
- gtk_label_select_word (label);
- else if (min < max && min <= index && index <= max)
- {
- info->in_drag = TRUE;
- info->drag_start_x = event->x;
- info->drag_start_y = event->y;
- }
- else
- /* start a replacement */
- gtk_label_select_region_index (label, index, index);
}
-
- return TRUE;
}
- return FALSE;
+ if (n_press >= 3)
+ gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
}
-static gboolean
-gtk_label_button_release (GtkWidget *widget,
- GdkEventButton *event)
-
+static void
+gtk_label_multipress_gesture_released (GtkGestureMultiPress *gesture,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ GtkLabel *label)
{
- GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv = label->priv;
GtkLabelSelectionInfo *info = priv->select_info;
+ GdkEventSequence *sequence;
gint index;
if (info == NULL)
- return FALSE;
+ return;
+
+ sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+
+ if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
+ return;
+
+ if (n_press != 1)
+ return;
if (info->in_drag)
{
info->in_drag = 0;
-
- get_layout_index (label, event->x, event->y, &index);
+ get_layout_index (label, x, y, &index);
gtk_label_select_region_index (label, index, index);
-
- return FALSE;
}
-
- if (event->button != GDK_BUTTON_PRIMARY)
- return FALSE;
-
- if (info->active_link &&
- info->selection_anchor == info->selection_end &&
- info->link_clicked)
+ else if (info->active_link &&
+ info->selection_anchor == info->selection_end &&
+ info->link_clicked)
{
emit_activate_link (label, info->active_link);
info->link_clicked = 0;
-
- return TRUE;
}
-
- /* The goal here is to return TRUE iff we ate the
- * button press to start selecting.
- */
- if (info->selectable)
- return TRUE;
-
- return FALSE;
}
static void
@@ -4909,84 +4899,112 @@ drag_begin_cb (GtkWidget *widget,
}
}
-static gboolean
-gtk_label_motion (GtkWidget *widget,
- GdkEventMotion *event)
+static void
+gtk_label_drag_gesture_begin (GtkGestureDrag *gesture,
+ gdouble start_x,
+ gdouble start_y,
+ GtkLabel *label)
{
- GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv = label->priv;
GtkLabelSelectionInfo *info = priv->select_info;
- gint index;
-
- if (info == NULL)
- return FALSE;
+ GdkModifierType state_mask;
+ GdkEventSequence *sequence;
+ const GdkEvent *event;
+ gint min, max, index;
- if (info->links && !info->in_drag)
+ if (!info)
{
- GList *l;
- GtkLabelLink *link;
- gboolean found = FALSE;
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+ return;
+ }
- if (info->selection_anchor == info->selection_end)
+ get_layout_index (label, start_x, start_y, &index);
+ min = MIN (info->selection_anchor, info->selection_end);
+ max = MAX (info->selection_anchor, info->selection_end);
+
+ sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+ event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
+ gdk_event_get_state (event, &state_mask);
+
+ if ((info->selection_anchor != info->selection_end) &&
+ (state_mask & GDK_SHIFT_MASK))
+ {
+ if (index > min && index < max)
{
- if (get_layout_index (label, event->x, event->y, &index))
- {
- for (l = info->links; l != NULL; l = l->next)
- {
- link = l->data;
- if (index >= link->start && index <= link->end)
- {
- found = TRUE;
- break;
- }
- }
- }
+ /* truncate selection, but keep it as big as possible */
+ if (index - min > max - index)
+ max = index;
+ else
+ min = index;
+ }
+ else
+ {
+ /* extend (same as motion) */
+ min = MIN (min, index);
+ max = MAX (max, index);
}
- if (found)
+ /* ensure the anchor is opposite index */
+ if (index == min)
{
- if (info->active_link != link)
- {
- info->link_clicked = 0;
- info->active_link = link;
- gtk_label_update_cursor (label);
- gtk_widget_queue_draw (widget);
- }
+ gint tmp = min;
+ min = max;
+ max = tmp;
}
- else
+
+ gtk_label_select_region_index (label, min, max);
+ }
+ else
+ {
+ if (min < max && min <= index && index <= max)
{
- if (info->active_link != NULL)
- {
- info->link_clicked = 0;
- info->active_link = NULL;
- gtk_label_update_cursor (label);
- gtk_widget_queue_draw (widget);
- }
+ info->in_drag = TRUE;
+ info->drag_start_x = start_x;
+ info->drag_start_y = start_y;
}
+ else
+ /* start a replacement */
+ gtk_label_select_region_index (label, index, index);
}
+}
- if (!info->selectable)
- return FALSE;
+static void
+gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
+ gdouble offset_x,
+ gdouble offset_y,
+ GtkLabel *label)
+{
+ GtkLabelPrivate *priv = label->priv;
+ GtkLabelSelectionInfo *info = priv->select_info;
+ GtkWidget *widget = GTK_WIDGET (label);
+ GdkEventSequence *sequence;
+ gdouble x, y;
+ gint index;
- if ((event->state & GDK_BUTTON1_MASK) == 0)
- return FALSE;
+ if (info == NULL || !info->selectable)
+ return;
+
+ sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+ gtk_gesture_get_point (GTK_GESTURE (gesture), sequence, &x, &y);
if (info->in_drag)
{
if (gtk_drag_check_threshold (widget,
info->drag_start_x,
info->drag_start_y,
- event->x, event->y))
+ x, y))
{
GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
+ const GdkEvent *event;
+ event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
gtk_target_list_add_text_targets (target_list, 0);
g_signal_connect (widget, "drag-begin",
G_CALLBACK (drag_begin_cb), NULL);
gtk_drag_begin_with_coordinates (widget, target_list,
GDK_ACTION_COPY,
- 1, (GdkEvent *)event,
+ 1, (GdkEvent*) event,
info->drag_start_x,
info->drag_start_y);
@@ -4997,11 +5015,11 @@ gtk_label_motion (GtkWidget *widget,
}
else
{
- gint x, y;
-
- gdk_window_get_device_position (info->window, event->device, &x, &y, NULL);
get_layout_index (label, x, y, &index);
+ if (index != info->selection_anchor)
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+
if (info->select_words)
{
gint min, max;
@@ -5043,8 +5061,65 @@ gtk_label_motion (GtkWidget *widget,
else
gtk_label_select_region_index (label, info->selection_anchor, index);
}
+}
- return TRUE;
+static gboolean
+gtk_label_motion (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkLabel *label = GTK_LABEL (widget);
+ GtkLabelPrivate *priv = label->priv;
+ GtkLabelSelectionInfo *info = priv->select_info;
+ gint index;
+
+ if (info == NULL)
+ return FALSE;
+
+ if (info->links && !info->in_drag)
+ {
+ GList *l;
+ GtkLabelLink *link;
+ gboolean found = FALSE;
+
+ if (info->selection_anchor == info->selection_end)
+ {
+ if (get_layout_index (label, event->x, event->y, &index))
+ {
+ for (l = info->links; l != NULL; l = l->next)
+ {
+ link = l->data;
+ if (index >= link->start && index <= link->end)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+ }
+
+ if (found)
+ {
+ if (info->active_link != link)
+ {
+ info->link_clicked = 0;
+ info->active_link = link;
+ gtk_label_update_cursor (label);
+ gtk_widget_queue_draw (widget);
+ }
+ }
+ else
+ {
+ if (info->active_link != NULL)
+ {
+ info->link_clicked = 0;
+ info->active_link = NULL;
+ gtk_label_update_cursor (label);
+ gtk_widget_queue_draw (widget);
+ }
+ }
+ }
+
+ return GTK_WIDGET_CLASS (gtk_label_parent_class)->motion_notify_event (widget, event);
}
static gboolean
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]