paned: Use GtkGesture to handle handle dragging

Dragging is all handled by a GtkGesturePan now, matching the
paned orientation.

On touch events, a wider area is listened for, so touch events
don't need to be as accurate to initiate dragging, if no dragging
is truly initiated in this case, events are just forwarded for
child widgets to handle.
This commit is contained in:
Carlos Garnacho 2014-05-19 19:58:54 +02:00
parent 8f20780fb9
commit 681164564e

View File

@ -110,10 +110,11 @@ struct _GtkPanedPrivate
GtkOrientation orientation;
GdkCursorType cursor_type;
GdkDevice *grab_device;
GdkRectangle handle_pos;
GdkWindow *handle;
GtkGesture *pan_gesture;
gint child1_size;
gint drag_pos;
gint last_allocation;
@ -121,16 +122,14 @@ struct _GtkPanedPrivate
gint min_position;
gint original_position;
guint32 grab_time;
guint handle_prelit : 1;
guint in_drag : 1;
guint in_recursion : 1;
guint child1_resize : 1;
guint child1_shrink : 1;
guint child2_resize : 1;
guint child2_shrink : 1;
guint position_set : 1;
guint panning : 1;
};
enum {
@ -209,16 +208,8 @@ static gboolean gtk_paned_enter (GtkWidget *widget,
GdkEventCrossing *event);
static gboolean gtk_paned_leave (GtkWidget *widget,
GdkEventCrossing *event);
static gboolean gtk_paned_button_press (GtkWidget *widget,
GdkEventButton *event);
static gboolean gtk_paned_button_release (GtkWidget *widget,
GdkEventButton *event);
static gboolean gtk_paned_motion (GtkWidget *widget,
GdkEventMotion *event);
static gboolean gtk_paned_focus (GtkWidget *widget,
GtkDirectionType direction);
static gboolean gtk_paned_grab_broken (GtkWidget *widget,
GdkEventGrabBroken *event);
static void gtk_paned_add (GtkContainer *container,
GtkWidget *widget);
static void gtk_paned_remove (GtkContainer *container,
@ -252,9 +243,10 @@ static gboolean gtk_paned_cancel_position (GtkPaned *paned);
static gboolean gtk_paned_toggle_handle_focus (GtkPaned *paned);
static GType gtk_paned_child_type (GtkContainer *container);
static void gtk_paned_grab_notify (GtkWidget *widget,
gboolean was_grabbed);
static void update_drag (GtkPaned *paned,
int xpos,
int ypos);
G_DEFINE_TYPE_WITH_CODE (GtkPaned, gtk_paned, GTK_TYPE_CONTAINER,
G_ADD_PRIVATE (GtkPaned)
@ -316,11 +308,6 @@ gtk_paned_class_init (GtkPanedClass *class)
widget_class->focus = gtk_paned_focus;
widget_class->enter_notify_event = gtk_paned_enter;
widget_class->leave_notify_event = gtk_paned_leave;
widget_class->button_press_event = gtk_paned_button_press;
widget_class->button_release_event = gtk_paned_button_release;
widget_class->motion_notify_event = gtk_paned_motion;
widget_class->grab_broken_event = gtk_paned_grab_broken;
widget_class->grab_notify = gtk_paned_grab_notify;
widget_class->state_flags_changed = gtk_paned_state_flags_changed;
container_class->add = gtk_paned_add;
@ -667,10 +654,106 @@ gtk_paned_child_type (GtkContainer *container)
return G_TYPE_NONE;
}
static gboolean
initiates_touch_drag (GtkPaned *paned,
gdouble start_x,
gdouble start_y)
{
gint handle_size, handle_pos, drag_pos;
GtkPanedPrivate *priv = paned->priv;
GtkAllocation allocation;
#define TOUCH_EXTRA_AREA_WIDTH 50
gtk_widget_get_allocation (GTK_WIDGET (paned), &allocation);
gtk_widget_style_get (GTK_WIDGET (paned), "handle-size", &handle_size, NULL);
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
{
handle_pos = priv->handle_pos.x - allocation.x;
drag_pos = start_x;
}
else
{
handle_pos = priv->handle_pos.y - allocation.y;
drag_pos = start_y;
}
if (drag_pos < handle_pos - TOUCH_EXTRA_AREA_WIDTH ||
drag_pos > handle_pos + handle_size + TOUCH_EXTRA_AREA_WIDTH)
return FALSE;
#undef TOUCH_EXTRA_AREA_WIDTH
return TRUE;
}
static void
pan_gesture_drag_begin_cb (GtkGestureDrag *gesture,
gdouble start_x,
gdouble start_y,
GtkPaned *paned)
{
GtkPanedPrivate *priv = paned->priv;
GdkEventSequence *sequence;
GtkAllocation allocation;
const GdkEvent *event;
sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
gtk_widget_get_allocation (GTK_WIDGET (paned), &allocation);
paned->priv->panning = FALSE;
if (event->any.window == priv->handle ||
(event->type == GDK_TOUCH_BEGIN &&
initiates_touch_drag (paned, start_x, start_y)))
{
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
priv->drag_pos = start_x - (priv->handle_pos.x - allocation.x);
else
priv->drag_pos = start_y - (priv->handle_pos.y - allocation.y);
gtk_gesture_set_state (GTK_GESTURE (gesture),
GTK_EVENT_SEQUENCE_CLAIMED);
}
else
{
gtk_gesture_set_state (GTK_GESTURE (gesture),
GTK_EVENT_SEQUENCE_DENIED);
}
}
static void
pan_gesture_pan_cb (GtkGesturePan *gesture,
GtkPanDirection direction,
gdouble offset,
GtkPaned *paned)
{
gdouble start_x, start_y, offset_x, offset_y;
paned->priv->panning = TRUE;
gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (gesture),
&start_x, &start_y);
gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture),
&offset_x, &offset_y);
update_drag (paned, start_x + offset_x, start_y + offset_y);
}
static void
pan_gesture_drag_end_cb (GtkGestureDrag *gesture,
gdouble offset_x,
gdouble offset_y,
GtkPaned *paned)
{
if (!paned->priv->panning)
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
}
static void
gtk_paned_init (GtkPaned *paned)
{
GtkPanedPrivate *priv;
GtkGesture *gesture;
gtk_widget_set_has_window (GTK_WIDGET (paned), FALSE);
gtk_widget_set_can_focus (GTK_WIDGET (paned), TRUE);
@ -694,7 +777,6 @@ gtk_paned_init (GtkPaned *paned)
priv->handle_pos.height = 5;
priv->position_set = FALSE;
priv->last_allocation = -1;
priv->in_drag = FALSE;
priv->last_child1_focus = NULL;
priv->last_child2_focus = NULL;
@ -705,7 +787,17 @@ gtk_paned_init (GtkPaned *paned)
priv->handle_pos.x = -1;
priv->handle_pos.y = -1;
priv->drag_pos = -1;
gesture = gtk_gesture_pan_new (GTK_WIDGET (paned),
GTK_PAN_ORIENTATION_HORIZONTAL);
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE);
g_signal_connect (gesture, "drag-begin",
G_CALLBACK (pan_gesture_drag_begin_cb), paned);
g_signal_connect (gesture, "pan",
G_CALLBACK (pan_gesture_pan_cb), paned);
g_signal_connect (gesture, "drag-end",
G_CALLBACK (pan_gesture_drag_end_cb), paned);
gtk_gesture_attach (gesture, GTK_PHASE_CAPTURE);
priv->pan_gesture = gesture;
}
static void
@ -724,9 +816,17 @@ gtk_paned_set_property (GObject *object,
_gtk_orientable_set_style_classes (GTK_ORIENTABLE (paned));
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
priv->cursor_type = GDK_SB_H_DOUBLE_ARROW;
{
priv->cursor_type = GDK_SB_H_DOUBLE_ARROW;
gtk_gesture_pan_set_orientation (GTK_GESTURE_PAN (priv->pan_gesture),
GTK_PAN_ORIENTATION_HORIZONTAL);
}
else
priv->cursor_type = GDK_SB_V_DOUBLE_ARROW;
{
priv->cursor_type = GDK_SB_V_DOUBLE_ARROW;
gtk_gesture_pan_set_orientation (GTK_GESTURE_PAN (priv->pan_gesture),
GTK_PAN_ORIENTATION_VERTICAL);
}
/* state_flags_changed updates the cursor */
gtk_paned_state_flags_changed (GTK_WIDGET (paned), 0);
@ -866,6 +966,9 @@ gtk_paned_finalize (GObject *object)
gtk_paned_set_saved_focus (paned, NULL);
gtk_paned_set_first_paned (paned, NULL);
gtk_gesture_detach (paned->priv->pan_gesture);
g_clear_object (&paned->priv->pan_gesture);
G_OBJECT_CLASS (gtk_paned_parent_class)->finalize (object);
}
@ -1634,10 +1737,9 @@ is_rtl (GtkPaned *paned)
}
static void
update_drag (GtkPaned *paned,
/* relative to priv->handle */
int xpos,
int ypos)
update_drag (GtkPaned *paned,
int xpos,
int ypos)
{
GtkPanedPrivate *priv = paned->priv;
GtkAllocation allocation;
@ -1650,13 +1752,9 @@ update_drag (GtkPaned *paned,
gdk_window_get_position (priv->handle, &x, &y);
gtk_widget_get_allocation (widget, &allocation);
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
{
pos = xpos + x - allocation.x;
}
pos = xpos;
else
{
pos = ypos + y - allocation.y;
}
pos = ypos;
pos -= priv->drag_pos;
@ -1686,9 +1784,7 @@ gtk_paned_enter (GtkWidget *widget,
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
if (priv->in_drag)
update_drag (paned, event->x, event->y);
else
if (!gtk_gesture_is_active (priv->pan_gesture))
{
priv->handle_prelit = TRUE;
gtk_widget_queue_draw_area (widget,
@ -1708,9 +1804,7 @@ gtk_paned_leave (GtkWidget *widget,
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
if (priv->in_drag)
update_drag (paned, event->x, event->y);
else
if (!gtk_gesture_is_active (priv->pan_gesture))
{
priv->handle_prelit = FALSE;
gtk_widget_queue_draw_area (widget,
@ -1741,86 +1835,6 @@ gtk_paned_focus (GtkWidget *widget,
return retval;
}
static gboolean
gtk_paned_button_press (GtkWidget *widget,
GdkEventButton *event)
{
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
if (!priv->in_drag &&
(event->window == priv->handle) && (event->button == GDK_BUTTON_PRIMARY))
{
/* We need a server grab here, not gtk_grab_add(), since
* we don't want to pass events on to the widget's children */
if (gdk_device_grab (event->device,
priv->handle,
GDK_OWNERSHIP_WINDOW, FALSE,
GDK_BUTTON1_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
| GDK_ENTER_NOTIFY_MASK
| GDK_LEAVE_NOTIFY_MASK,
NULL, event->time) != GDK_GRAB_SUCCESS)
return FALSE;
priv->in_drag = TRUE;
priv->grab_time = event->time;
priv->grab_device = event->device;
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
priv->drag_pos = event->x;
else
priv->drag_pos = event->y;
return TRUE;
}
return FALSE;
}
static gboolean
gtk_paned_grab_broken (GtkWidget *widget,
GdkEventGrabBroken *event)
{
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
priv->in_drag = FALSE;
priv->drag_pos = -1;
priv->position_set = TRUE;
return TRUE;
}
static void
stop_drag (GtkPaned *paned)
{
GtkPanedPrivate *priv = paned->priv;
priv->in_drag = FALSE;
priv->drag_pos = -1;
priv->position_set = TRUE;
gdk_device_ungrab (priv->grab_device,
priv->grab_time);
priv->grab_device = NULL;
}
static void
gtk_paned_grab_notify (GtkWidget *widget,
gboolean was_grabbed)
{
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
GdkDevice *grab_device;
grab_device = priv->grab_device;
if (priv->in_drag && grab_device &&
gtk_widget_device_is_shadowed (widget, grab_device))
stop_drag (paned);
}
static void
gtk_paned_state_flags_changed (GtkWidget *widget,
GtkStateFlags previous_state)
@ -1844,39 +1858,6 @@ gtk_paned_state_flags_changed (GtkWidget *widget,
}
}
static gboolean
gtk_paned_button_release (GtkWidget *widget,
GdkEventButton *event)
{
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
if (priv->in_drag && (event->button == GDK_BUTTON_PRIMARY))
{
stop_drag (paned);
return TRUE;
}
return FALSE;
}
static gboolean
gtk_paned_motion (GtkWidget *widget,
GdkEventMotion *event)
{
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
if (priv->in_drag)
{
update_drag (paned, event->x, event->y);
return TRUE;
}
return FALSE;
}
/**
* gtk_paned_new:
* @orientation: the paneds orientation.
@ -2848,4 +2829,4 @@ gtk_paned_get_handle_window (GtkPaned *paned)
g_return_val_if_fail (GTK_IS_PANED (paned), NULL);
return paned->priv->handle;
}
}