1eae9fcd7e
*** This is an API change *** Replaced the original drag-threshold property with two separate horizontal (x-drag-threshold) and vertical (y-drag-threshold) thresholds. It is some times necessary to have different drag thresholds for the horizontal and vertical axes. For example, when a draggable actor is inside a horizontal scrolling area, only vertical movement must begin dragging. That can be achieved by setting the x-drag-threshold to G_MAXUINT while y-drag-threshold is something usual, say, 20 pixels. This is different than drag axis, because after the threshold has been cleared by the pointer, the draggable actor can be dragged along both axes (if allowed by the drag-axis property). http://bugzilla.clutter-project.org/show_bug.cgi?id=2291 Signed-off-by: Emmanuele Bassi <ebassi@linux.intel.com>
898 lines
26 KiB
C
898 lines
26 KiB
C
/*
|
|
* Clutter.
|
|
*
|
|
* An OpenGL based 'interactive canvas' library.
|
|
*
|
|
* Copyright (C) 2010 Intel Corporation.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Author:
|
|
* Emmanuele Bassi <ebassi@linux.intel.com>
|
|
*/
|
|
|
|
/**
|
|
* SECTION:clutter-drag-action
|
|
* @Title: ClutterDragAction
|
|
* @Short_Description: Action enabling dragging on actors
|
|
*
|
|
* #ClutterDragAction is a sub-class of #ClutterAction that implements
|
|
* all the necessary logic for dragging actors.
|
|
*
|
|
* The simplest usage of #ClutterDragAction consists in adding it to
|
|
* a #ClutterActor and setting it as reactive; for instance, the following
|
|
* code:
|
|
*
|
|
* |[
|
|
* clutter_actor_add_action (actor, clutter_drag_action_new ());
|
|
* clutter_actor_set_reactive (actor, TRUE);
|
|
* ]|
|
|
*
|
|
* will automatically result in the actor moving to follow the pointer
|
|
* whenever the pointer's button is pressed over the actor and moved
|
|
* across the stage.
|
|
*
|
|
* The #ClutterDragAction will signal the begin and the end of a dragging
|
|
* through the #ClutterDragAction::drag-begin and #ClutterDragAction::drag-end
|
|
* signals, respectively. Each pointer motion during a drag will also result
|
|
* in the #ClutterDragAction::drag-motion signal to be emitted.
|
|
*
|
|
* It is also possible to set another #ClutterActor as the dragged actor
|
|
* by calling clutter_drag_action_set_drag_handle() from within a handle
|
|
* of the #ClutterDragAction::drag-begin signal. The drag handle must be
|
|
* parented and exist between the emission of #ClutterDragAction::drag-begin
|
|
* and #ClutterDragAction::drag-end.
|
|
*
|
|
* #ClutterDragAction is available since Clutter 1.4
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "clutter-drag-action.h"
|
|
|
|
#include "clutter-debug.h"
|
|
#include "clutter-enum-types.h"
|
|
#include "clutter-marshal.h"
|
|
#include "clutter-private.h"
|
|
|
|
struct _ClutterDragActionPrivate
|
|
{
|
|
ClutterActor *stage;
|
|
|
|
gfloat x_drag_threshold;
|
|
gfloat y_drag_threshold;
|
|
ClutterActor *drag_handle;
|
|
ClutterDragAxis drag_axis;
|
|
|
|
gulong button_press_id;
|
|
gulong capture_id;
|
|
|
|
gfloat press_x;
|
|
gfloat press_y;
|
|
ClutterModifierType press_state;
|
|
|
|
gfloat last_motion_x;
|
|
gfloat last_motion_y;
|
|
|
|
gfloat transformed_press_x;
|
|
gfloat transformed_press_y;
|
|
|
|
guint emit_delayed_press : 1;
|
|
guint in_drag : 1;
|
|
guint motion_events_enabled : 1;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_X_DRAG_THRESHOLD,
|
|
PROP_Y_DRAG_THRESHOLD,
|
|
PROP_DRAG_HANDLE,
|
|
PROP_DRAG_AXIS,
|
|
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *obj_props[PROP_LAST];
|
|
|
|
enum
|
|
{
|
|
DRAG_BEGIN,
|
|
DRAG_MOTION,
|
|
DRAG_END,
|
|
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint drag_signals[LAST_SIGNAL] = { 0, };
|
|
|
|
/* forward declaration */
|
|
static gboolean on_captured_event (ClutterActor *stage,
|
|
ClutterEvent *event,
|
|
ClutterDragAction *action);
|
|
|
|
G_DEFINE_TYPE (ClutterDragAction, clutter_drag_action, CLUTTER_TYPE_ACTION);
|
|
|
|
static void
|
|
emit_drag_begin (ClutterDragAction *action,
|
|
ClutterActor *actor,
|
|
ClutterEvent *event)
|
|
{
|
|
ClutterDragActionPrivate *priv = action->priv;
|
|
|
|
priv->motion_events_enabled = clutter_get_motion_events_enabled ();
|
|
clutter_set_motion_events_enabled (FALSE);
|
|
|
|
g_signal_emit (action, drag_signals[DRAG_BEGIN], 0,
|
|
actor,
|
|
priv->press_x, priv->press_y,
|
|
priv->press_state);
|
|
}
|
|
|
|
static void
|
|
emit_drag_motion (ClutterDragAction *action,
|
|
ClutterActor *actor,
|
|
ClutterEvent *event)
|
|
{
|
|
ClutterDragActionPrivate *priv = action->priv;
|
|
ClutterActor *drag_handle = NULL;
|
|
gfloat delta_x, delta_y;
|
|
gfloat motion_x, motion_y;
|
|
|
|
clutter_event_get_coords (event, &priv->last_motion_x, &priv->last_motion_y);
|
|
|
|
if (priv->drag_handle != NULL && !priv->emit_delayed_press)
|
|
drag_handle = priv->drag_handle;
|
|
else
|
|
drag_handle = actor;
|
|
|
|
motion_x = motion_y = 0.0f;
|
|
clutter_actor_transform_stage_point (drag_handle,
|
|
priv->last_motion_x,
|
|
priv->last_motion_y,
|
|
&motion_x, &motion_y);
|
|
|
|
delta_x = delta_y = 0.0f;
|
|
|
|
switch (priv->drag_axis)
|
|
{
|
|
case CLUTTER_DRAG_AXIS_NONE:
|
|
delta_x = motion_x - priv->transformed_press_x;
|
|
delta_y = motion_y - priv->transformed_press_y;
|
|
break;
|
|
|
|
case CLUTTER_DRAG_X_AXIS:
|
|
delta_x = motion_x - priv->transformed_press_x;
|
|
break;
|
|
|
|
case CLUTTER_DRAG_Y_AXIS:
|
|
delta_y = motion_y - priv->transformed_press_y;
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
return;
|
|
}
|
|
|
|
if (priv->emit_delayed_press)
|
|
{
|
|
if (ABS (delta_x) >= priv->x_drag_threshold ||
|
|
ABS (delta_y) >= priv->y_drag_threshold)
|
|
{
|
|
priv->emit_delayed_press = FALSE;
|
|
|
|
emit_drag_begin (action, actor, NULL);
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
g_signal_emit (action, drag_signals[DRAG_MOTION], 0,
|
|
actor,
|
|
delta_x, delta_y);
|
|
}
|
|
|
|
static void
|
|
emit_drag_end (ClutterDragAction *action,
|
|
ClutterActor *actor,
|
|
ClutterEvent *event)
|
|
{
|
|
ClutterDragActionPrivate *priv = action->priv;
|
|
|
|
clutter_event_get_coords (event, &priv->last_motion_x, &priv->last_motion_y);
|
|
|
|
/* we might not have emitted ::drag-begin yet */
|
|
if (!priv->emit_delayed_press)
|
|
g_signal_emit (action, drag_signals[DRAG_END], 0,
|
|
actor,
|
|
priv->last_motion_x, priv->last_motion_y,
|
|
clutter_event_get_state (event));
|
|
|
|
/* disconnect the capture */
|
|
if (priv->capture_id != 0)
|
|
{
|
|
g_signal_handler_disconnect (priv->stage, priv->capture_id);
|
|
priv->capture_id = 0;
|
|
}
|
|
|
|
clutter_set_motion_events_enabled (priv->motion_events_enabled);
|
|
|
|
priv->in_drag = FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
on_captured_event (ClutterActor *stage,
|
|
ClutterEvent *event,
|
|
ClutterDragAction *action)
|
|
{
|
|
ClutterDragActionPrivate *priv = action->priv;
|
|
ClutterActor *actor;
|
|
|
|
actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action));
|
|
|
|
if (!priv->in_drag)
|
|
return FALSE;
|
|
|
|
switch (clutter_event_type (event))
|
|
{
|
|
case CLUTTER_MOTION:
|
|
{
|
|
ClutterModifierType mods = clutter_event_get_state (event);
|
|
|
|
/* we might miss a button-release event in case of grabs,
|
|
* so we need to check whether the button is still down
|
|
* during a motion event
|
|
*/
|
|
if (mods & CLUTTER_BUTTON1_MASK)
|
|
emit_drag_motion (action, actor, event);
|
|
else
|
|
emit_drag_end (action, actor, event);
|
|
}
|
|
break;
|
|
|
|
case CLUTTER_BUTTON_RELEASE:
|
|
if (priv->in_drag)
|
|
emit_drag_end (action, actor, event);
|
|
break;
|
|
|
|
case CLUTTER_ENTER:
|
|
case CLUTTER_LEAVE:
|
|
if (priv->in_drag)
|
|
return TRUE;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
on_button_press (ClutterActor *actor,
|
|
ClutterEvent *event,
|
|
ClutterDragAction *action)
|
|
{
|
|
ClutterDragActionPrivate *priv = action->priv;
|
|
|
|
if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (action)))
|
|
return FALSE;
|
|
|
|
if (clutter_event_get_button (event) != 1)
|
|
return FALSE;
|
|
|
|
if (priv->stage == NULL)
|
|
priv->stage = clutter_actor_get_stage (actor);
|
|
|
|
clutter_event_get_coords (event, &priv->press_x, &priv->press_y);
|
|
priv->press_state = clutter_event_get_state (event);
|
|
|
|
priv->last_motion_x = priv->press_x;
|
|
priv->last_motion_y = priv->press_y;
|
|
|
|
priv->transformed_press_x = priv->press_x;
|
|
priv->transformed_press_y = priv->press_y;
|
|
clutter_actor_transform_stage_point (actor, priv->press_x, priv->press_y,
|
|
&priv->transformed_press_x,
|
|
&priv->transformed_press_y);
|
|
|
|
if (priv->x_drag_threshold == 0 || priv->y_drag_threshold == 0)
|
|
emit_drag_begin (action, actor, event);
|
|
else
|
|
priv->emit_delayed_press = TRUE;
|
|
|
|
priv->in_drag = TRUE;
|
|
priv->capture_id = g_signal_connect_after (priv->stage, "captured-event",
|
|
G_CALLBACK (on_captured_event),
|
|
action);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
clutter_drag_action_set_actor (ClutterActorMeta *meta,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterDragActionPrivate *priv = CLUTTER_DRAG_ACTION (meta)->priv;
|
|
|
|
if (priv->button_press_id != 0)
|
|
{
|
|
ClutterActor *old_actor;
|
|
|
|
old_actor = clutter_actor_meta_get_actor (meta);
|
|
|
|
g_signal_handler_disconnect (old_actor, priv->button_press_id);
|
|
|
|
if (priv->capture_id != 0)
|
|
g_signal_handler_disconnect (old_actor, priv->capture_id);
|
|
|
|
priv->button_press_id = 0;
|
|
priv->capture_id = 0;
|
|
|
|
priv->stage = NULL;
|
|
}
|
|
|
|
if (actor != NULL)
|
|
priv->button_press_id = g_signal_connect (actor, "button-press-event",
|
|
G_CALLBACK (on_button_press),
|
|
meta);
|
|
|
|
CLUTTER_ACTOR_META_CLASS (clutter_drag_action_parent_class)->set_actor (meta, actor);
|
|
}
|
|
|
|
static void
|
|
clutter_drag_action_real_drag_motion (ClutterDragAction *action,
|
|
ClutterActor *actor,
|
|
gfloat delta_x,
|
|
gfloat delta_y)
|
|
{
|
|
ClutterActor *drag_handle;
|
|
|
|
if (action->priv->drag_handle != NULL)
|
|
drag_handle = action->priv->drag_handle;
|
|
else
|
|
drag_handle = actor;
|
|
|
|
clutter_actor_move_by (drag_handle, delta_x, delta_y);
|
|
}
|
|
|
|
static void
|
|
clutter_drag_action_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterDragAction *action = CLUTTER_DRAG_ACTION (gobject);
|
|
ClutterDragActionPrivate *priv = action->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_X_DRAG_THRESHOLD:
|
|
clutter_drag_action_set_drag_threshold (action, g_value_get_uint (value),
|
|
priv->y_drag_threshold);
|
|
break;
|
|
|
|
case PROP_Y_DRAG_THRESHOLD:
|
|
clutter_drag_action_set_drag_threshold (action, priv->x_drag_threshold,
|
|
g_value_get_uint (value));
|
|
break;
|
|
|
|
case PROP_DRAG_HANDLE:
|
|
clutter_drag_action_set_drag_handle (action, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_DRAG_AXIS:
|
|
clutter_drag_action_set_drag_axis (action, g_value_get_enum (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_drag_action_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterDragActionPrivate *priv = CLUTTER_DRAG_ACTION (gobject)->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_X_DRAG_THRESHOLD:
|
|
g_value_set_uint (value, priv->x_drag_threshold);
|
|
break;
|
|
|
|
case PROP_Y_DRAG_THRESHOLD:
|
|
g_value_set_uint (value, priv->y_drag_threshold);
|
|
break;
|
|
|
|
case PROP_DRAG_HANDLE:
|
|
g_value_set_object (value, priv->drag_handle);
|
|
break;
|
|
|
|
case PROP_DRAG_AXIS:
|
|
g_value_set_enum (value, priv->drag_axis);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_drag_action_dispose (GObject *gobject)
|
|
{
|
|
ClutterDragActionPrivate *priv = CLUTTER_DRAG_ACTION (gobject)->priv;
|
|
|
|
if (priv->capture_id != 0)
|
|
{
|
|
if (priv->stage != NULL)
|
|
g_signal_handler_disconnect (priv->stage, priv->capture_id);
|
|
|
|
priv->capture_id = 0;
|
|
priv->stage = NULL;
|
|
}
|
|
|
|
if (priv->button_press_id != 0)
|
|
{
|
|
ClutterActor *actor;
|
|
|
|
actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (gobject));
|
|
g_signal_handler_disconnect (actor, priv->button_press_id);
|
|
priv->button_press_id = 0;
|
|
}
|
|
|
|
G_OBJECT_CLASS (clutter_drag_action_parent_class)->dispose (gobject);
|
|
}
|
|
|
|
static void
|
|
clutter_drag_action_class_init (ClutterDragActionClass *klass)
|
|
{
|
|
ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GParamSpec *pspec;
|
|
|
|
g_type_class_add_private (klass, sizeof (ClutterDragActionPrivate));
|
|
|
|
gobject_class->set_property = clutter_drag_action_set_property;
|
|
gobject_class->get_property = clutter_drag_action_get_property;
|
|
gobject_class->dispose = clutter_drag_action_dispose;
|
|
|
|
meta_class->set_actor = clutter_drag_action_set_actor;
|
|
|
|
klass->drag_motion = clutter_drag_action_real_drag_motion;
|
|
|
|
/**
|
|
* ClutterDragAction:x-drag-threshold:
|
|
*
|
|
* The horizontal threshold, in pixels, that begins a drag action
|
|
*
|
|
* When set to a non-zero value, #ClutterDragAction will only emit
|
|
* #ClutterDragAction::drag-begin if the pointer has moved
|
|
* horizontally at least of the given amount of pixels since
|
|
* the button press event
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
pspec = g_param_spec_uint ("x-drag-threshold",
|
|
P_("Horizontal Drag Threshold"),
|
|
P_("The horizontal amount of pixels required to start dragging"),
|
|
0, G_MAXUINT,
|
|
0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_X_DRAG_THRESHOLD] = pspec;
|
|
g_object_class_install_property (gobject_class, PROP_X_DRAG_THRESHOLD, pspec);
|
|
|
|
/**
|
|
* ClutterDragAction:y-drag-threshold:
|
|
*
|
|
* The vertical threshold, in pixels, that begins a drag action
|
|
*
|
|
* When set to a non-zero value, #ClutterDragAction will only emit
|
|
* #ClutterDragAction::drag-begin if the pointer has moved
|
|
* vertically at least of the given amount of pixels since
|
|
* the button press event
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
pspec = g_param_spec_uint ("y-drag-threshold",
|
|
P_("Vertical Drag Threshold"),
|
|
P_("The vertical amount of pixels required to start dragging"),
|
|
0, G_MAXUINT,
|
|
0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_Y_DRAG_THRESHOLD] = pspec;
|
|
g_object_class_install_property (gobject_class, PROP_Y_DRAG_THRESHOLD, pspec);
|
|
|
|
/**
|
|
* ClutterDragAction:drag-handle:
|
|
*
|
|
* The #ClutterActor that is effectively being dragged
|
|
*
|
|
* A #ClutterDragAction will, be default, use the #ClutterActor that
|
|
* has been attached to the action; it is possible to create a
|
|
* separate #ClutterActor and use it instead.
|
|
*
|
|
* Setting this property has no effect on the #ClutterActor argument
|
|
* passed to the #ClutterDragAction signals
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
pspec = g_param_spec_object ("drag-handle",
|
|
P_("Drag Handle"),
|
|
P_("The actor that is being dragged"),
|
|
CLUTTER_TYPE_ACTOR,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_DRAG_HANDLE] = pspec;
|
|
g_object_class_install_property (gobject_class, PROP_DRAG_HANDLE, pspec);
|
|
|
|
/**
|
|
* ClutterDragAction:drag-axis:
|
|
*
|
|
* Constraints the dragging action to the specified axis
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
pspec = g_param_spec_enum ("drag-axis",
|
|
P_("Drag Axis"),
|
|
P_("Constraints the dragging to an axis"),
|
|
CLUTTER_TYPE_DRAG_AXIS,
|
|
CLUTTER_DRAG_AXIS_NONE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_DRAG_AXIS] = pspec;
|
|
g_object_class_install_property (gobject_class, PROP_DRAG_AXIS, pspec);
|
|
|
|
/**
|
|
* ClutterDragAction::drag-begin:
|
|
* @action: the #ClutterDragAction that emitted the signal
|
|
* @actor: the #ClutterActor attached to the action
|
|
* @event_x: the X coordinate (in stage space) of the press event
|
|
* @event_y: the Y coordinate (in stage space) of the press event
|
|
* @modifiers: the modifiers of the press event
|
|
*
|
|
* The ::drag-begin signal is emitted when the #ClutterDragAction
|
|
* starts the dragging
|
|
*
|
|
* The emission of this signal can be delayed by using the
|
|
* #ClutterDragAction:x-drag-threshold and
|
|
* #ClutterDragAction:y-drag-threshold properties
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
drag_signals[DRAG_BEGIN] =
|
|
g_signal_new (I_("drag-begin"),
|
|
CLUTTER_TYPE_DRAG_ACTION,
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterDragActionClass, drag_begin),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__OBJECT_FLOAT_FLOAT_FLAGS,
|
|
G_TYPE_NONE, 4,
|
|
CLUTTER_TYPE_ACTOR,
|
|
G_TYPE_FLOAT,
|
|
G_TYPE_FLOAT,
|
|
CLUTTER_TYPE_MODIFIER_TYPE);
|
|
|
|
/**
|
|
* ClutterDragAction::drag-motion
|
|
* @action: the #ClutterDragAction that emitted the signal
|
|
* @actor: the #ClutterActor attached to the action
|
|
* @delta_x: the X component of the distance between the press event
|
|
* that began the dragging and the current position of the pointer,
|
|
* as of the latest motion event
|
|
* @delta_y: the Y component of the distance between the press event
|
|
* that began the dragging and the current position of the pointer,
|
|
* as of the latest motion event
|
|
*
|
|
* The ::drag-motion signal is emitted for each motion event after
|
|
* the #ClutterDragAction::drag-begin signal has been emitted.
|
|
*
|
|
* The components of the distance between the press event and the
|
|
* latest motion event are computed in the actor's coordinate space,
|
|
* to take into account eventual transformations. If you want the
|
|
* stage coordinates of the latest motion event you can use
|
|
* clutter_drag_action_get_motion_coords().
|
|
*
|
|
* The default handler of the signal will call clutter_actor_move_by()
|
|
* either on @actor or, if set, of #ClutterDragAction:drag-handle using
|
|
* the @delta_x and @delta_y components of the dragging motion. If you
|
|
* want to override the default behaviour, you can connect to this
|
|
* signal and call g_signal_stop_emission_by_name() from within your
|
|
* callback.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
drag_signals[DRAG_MOTION] =
|
|
g_signal_new (I_("drag-motion"),
|
|
CLUTTER_TYPE_DRAG_ACTION,
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterDragActionClass, drag_motion),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__OBJECT_FLOAT_FLOAT,
|
|
G_TYPE_NONE, 3,
|
|
CLUTTER_TYPE_ACTOR,
|
|
G_TYPE_FLOAT,
|
|
G_TYPE_FLOAT);
|
|
|
|
/**
|
|
* ClutterDragAction::drag-end:
|
|
* @action: the #ClutterDragAction that emitted the signal
|
|
* @actor: the #ClutterActor attached to the action
|
|
* @event_x: the X coordinate (in stage space) of the release event
|
|
* @event_y: the Y coordinate (in stage space) of the release event
|
|
* @modifiers: the modifiers of the release event
|
|
*
|
|
* The ::drag-end signal is emitted at the end of the dragging,
|
|
* when the pointer button's is released
|
|
*
|
|
* This signal is emitted if and only if the #ClutterDragAction::drag-begin
|
|
* signal has been emitted first
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
drag_signals[DRAG_END] =
|
|
g_signal_new (I_("drag-end"),
|
|
CLUTTER_TYPE_DRAG_ACTION,
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterDragActionClass, drag_end),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__OBJECT_FLOAT_FLOAT_FLAGS,
|
|
G_TYPE_NONE, 4,
|
|
CLUTTER_TYPE_ACTOR,
|
|
G_TYPE_FLOAT,
|
|
G_TYPE_FLOAT,
|
|
CLUTTER_TYPE_MODIFIER_TYPE);
|
|
}
|
|
|
|
static void
|
|
clutter_drag_action_init (ClutterDragAction *self)
|
|
{
|
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_DRAG_ACTION,
|
|
ClutterDragActionPrivate);
|
|
}
|
|
|
|
/**
|
|
* clutter_drag_action_new:
|
|
*
|
|
* Creates a new #ClutterDragAction instance
|
|
*
|
|
* Return value: the newly created #ClutterDragAction
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
ClutterAction *
|
|
clutter_drag_action_new (void)
|
|
{
|
|
return g_object_new (CLUTTER_TYPE_DRAG_ACTION, NULL);
|
|
}
|
|
|
|
/**
|
|
* clutter_drag_action_set_drag_threshold:
|
|
* @action: a #ClutterDragAction
|
|
* @threshold: a distance, in pixels
|
|
*
|
|
* Sets the horizontal and vertical drag thresholds that must be
|
|
* cleared by the pointer before @action can begin the dragging
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_drag_action_set_drag_threshold (ClutterDragAction *action,
|
|
guint x_threshold,
|
|
guint y_threshold)
|
|
{
|
|
ClutterDragActionPrivate *priv;
|
|
GObject *self;
|
|
|
|
g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action));
|
|
|
|
self = G_OBJECT (action);
|
|
priv = action->priv;
|
|
|
|
g_object_freeze_notify (self);
|
|
|
|
if (priv->x_drag_threshold != x_threshold)
|
|
{
|
|
priv->x_drag_threshold = x_threshold;
|
|
|
|
_clutter_notify_by_pspec (self, obj_props[PROP_X_DRAG_THRESHOLD]);
|
|
}
|
|
|
|
if (priv->y_drag_threshold != y_threshold)
|
|
{
|
|
priv->y_drag_threshold = y_threshold;
|
|
|
|
_clutter_notify_by_pspec (self, obj_props[PROP_Y_DRAG_THRESHOLD]);
|
|
}
|
|
|
|
g_object_thaw_notify (self);
|
|
}
|
|
|
|
/**
|
|
* clutter_drag_action_get_drag_threshold:
|
|
* @action: a #ClutterDragAction
|
|
* @x_threshold: (out): return location for the horizontal drag
|
|
* threshold value, in pixels
|
|
* @y_threshold: (out): return location for the vertical drag
|
|
* threshold value, in pixels
|
|
*
|
|
* Retrieves the values set by clutter_drag_action_set_drag_threshold()
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_drag_action_get_drag_threshold (ClutterDragAction *action,
|
|
guint *x_threshold,
|
|
guint *y_threshold)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action));
|
|
|
|
if (x_threshold)
|
|
*x_threshold = action->priv->x_drag_threshold;
|
|
|
|
if (y_threshold)
|
|
*y_threshold = action->priv->y_drag_threshold;
|
|
}
|
|
|
|
/**
|
|
* clutter_drag_action_set_drag_handle:
|
|
* @action: a #ClutterDragHandle
|
|
* @handle: a #ClutterActor
|
|
*
|
|
* Sets the actor to be used as the drag handle
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_drag_action_set_drag_handle (ClutterDragAction *action,
|
|
ClutterActor *handle)
|
|
{
|
|
ClutterDragActionPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (handle));
|
|
|
|
priv = action->priv;
|
|
|
|
if (priv->drag_handle == handle)
|
|
return;
|
|
|
|
priv->drag_handle = handle;
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (action), obj_props[PROP_DRAG_HANDLE]);
|
|
}
|
|
|
|
/**
|
|
* clutter_drag_action_get_drag_handle:
|
|
* @action: a #ClutterDragAction
|
|
*
|
|
* Retrieves the drag handle set by clutter_drag_action_set_drag_handle()
|
|
*
|
|
* Return value: (transfer none): a #ClutterActor, used as the drag
|
|
* handle, or %NULL if none was set
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
ClutterActor *
|
|
clutter_drag_action_get_drag_handle (ClutterDragAction *action)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_DRAG_ACTION (action), NULL);
|
|
|
|
return action->priv->drag_handle;
|
|
}
|
|
|
|
/**
|
|
* clutter_drag_action_set_drag_axis:
|
|
* @action: a #ClutterDragAction
|
|
* @axis: the axis to constraint the dragging to
|
|
*
|
|
* Restricts the dragging action to a specific axis
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_drag_action_set_drag_axis (ClutterDragAction *action,
|
|
ClutterDragAxis axis)
|
|
{
|
|
ClutterDragActionPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action));
|
|
g_return_if_fail (axis >= CLUTTER_DRAG_AXIS_NONE &&
|
|
axis <= CLUTTER_DRAG_Y_AXIS);
|
|
|
|
priv = action->priv;
|
|
|
|
if (priv->drag_axis == axis)
|
|
return;
|
|
|
|
priv->drag_axis = axis;
|
|
|
|
_clutter_notify_by_pspec (G_OBJECT (action), obj_props[PROP_DRAG_AXIS]);
|
|
}
|
|
|
|
/**
|
|
* clutter_drag_action_get_drag_axis:
|
|
* @action: a #ClutterDragAction
|
|
*
|
|
* Retrieves the axis constraint set by clutter_drag_action_set_drag_axis()
|
|
*
|
|
* Return value: the axis constraint
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
ClutterDragAxis
|
|
clutter_drag_action_get_drag_axis (ClutterDragAction *action)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_DRAG_ACTION (action),
|
|
CLUTTER_DRAG_AXIS_NONE);
|
|
|
|
return action->priv->drag_axis;
|
|
}
|
|
|
|
/**
|
|
* clutter_drag_action_get_press_coords:
|
|
* @action: a #ClutterDragAction
|
|
* @press_x: (out): return location for the press event's X coordinate
|
|
* @press_y: (out): return location for the press event's Y coordinate
|
|
*
|
|
* Retrieves the coordinates, in stage space, of the press event
|
|
* that started the dragging
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_drag_action_get_press_coords (ClutterDragAction *action,
|
|
gfloat *press_x,
|
|
gfloat *press_y)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action));
|
|
|
|
if (press_x)
|
|
*press_x = action->priv->press_x;
|
|
|
|
if (press_y)
|
|
*press_y = action->priv->press_y;
|
|
}
|
|
|
|
/**
|
|
* clutter_drag_action_get_motion_coords:
|
|
* @action: a #ClutterDragAction
|
|
* @motion_x: (out): return location for the latest motion
|
|
* event's X coordinate
|
|
* @motion_y: (out): return location for the latest motion
|
|
* event's Y coordinate
|
|
*
|
|
* Retrieves the coordinates, in stage space, of the latest motion
|
|
* event during the dragging
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_drag_action_get_motion_coords (ClutterDragAction *action,
|
|
gfloat *motion_x,
|
|
gfloat *motion_y)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action));
|
|
|
|
if (motion_x)
|
|
*motion_x = action->priv->last_motion_x;
|
|
|
|
if (motion_y)
|
|
*motion_y = action->priv->last_motion_y;
|
|
}
|