ClutterDragAction

## ClutterDragAction ClutterDragAction — Action enabling dragging on actors |

## Synopsis

    struct              ClutterDragAction;
    struct              ClutterDragActionClass;
    ClutterAction *     clutter_drag_action_new             (void);
    void                clutter_drag_action_set_drag_threshold
                                                            (ClutterDragAction *action,
                                                             gint x_threshold,
                                                             gint y_threshold);
    void                clutter_drag_action_get_drag_threshold
                                                            (ClutterDragAction *action,
                                                             guint *x_threshold,
                                                             guint *y_threshold);
    void                clutter_drag_action_set_drag_handle (ClutterDragAction *action,
                                                             ClutterActor *handle);
    ClutterActor *      clutter_drag_action_get_drag_handle (ClutterDragAction *action);
    enum                ClutterDragAxis;
    void                clutter_drag_action_set_drag_axis   (ClutterDragAction *action,
                                                             ClutterDragAxis axis);
    ClutterDragAxis     clutter_drag_action_get_drag_axis   (ClutterDragAction *action);
    void                clutter_drag_action_set_drag_area   (ClutterDragAction *action,
                                                             const ClutterRect *drag_area);
    gboolean            clutter_drag_action_get_drag_area   (ClutterDragAction *action,
                                                             ClutterRect *drag_area);

    void                clutter_drag_action_get_press_coords
                                                            (ClutterDragAction *action,
                                                             gfloat *press_x,
                                                             gfloat *press_y);
    void                clutter_drag_action_get_motion_coords
                                                            (ClutterDragAction *action,
                                                             gfloat *motion_x,
                                                             gfloat *motion_y);

## Object Hierarchy

      GObject
       +----GInitiallyUnowned
             +----ClutterActorMeta
                   +----ClutterAction
                         +----ClutterDragAction

## Properties

      "drag-area"                ClutterRect*          : Read / Write
      "drag-area-set"            gboolean              : Read
      "drag-axis"                ClutterDragAxis       : Read / Write
      "drag-handle"              ClutterActor*         : Read / Write
      "x-drag-threshold"         gint                  : Read / Write
      "y-drag-threshold"         gint                  : Read / Write

## Signals

      "drag-begin"                                     : Run Last
      "drag-end"                                       : Run Last
      "drag-motion"                                    : Run Last
      "drag-progress"                                  : Run Last

## Description

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:

    1
    2
    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 "drag-begin" and "drag-end" signals, respectively. Each pointer motion during a drag will also result in the "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 "drag-begin" signal. The drag handle must be parented and exist between the emission of "drag-begin" and "drag-end".

Example 5. A simple draggable actor

    #include <stdlib.h>
    #include <clutter/clutter.h>

    static gboolean
    on_enter (ClutterActor *actor,
              ClutterEvent *event)
    {
      ClutterTransition *t;

      t = clutter_actor_get_transition (actor, "curl");
      if (t == NULL)
        {
          t = clutter_property_transition_new ("@effects.curl.period");
          clutter_timeline_set_duration (CLUTTER_TIMELINE (t), 250);
          clutter_actor_add_transition (actor, "curl", t);
          g_object_unref (t);
        }

      clutter_transition_set_from (t, G_TYPE_DOUBLE, 0.0);
      clutter_transition_set_to (t, G_TYPE_DOUBLE, 0.25);
      clutter_timeline_rewind (CLUTTER_TIMELINE (t));
      clutter_timeline_start (CLUTTER_TIMELINE (t));

      return CLUTTER_EVENT_STOP;
    }

    static gboolean
    on_leave (ClutterActor *actor,
              ClutterEvent *event)
    {
      ClutterTransition *t;

      t = clutter_actor_get_transition (actor, "curl");
      if (t == NULL)
        {
          t = clutter_property_transition_new ("@effects.curl.period");
          clutter_timeline_set_duration (CLUTTER_TIMELINE (t), 250);
          clutter_actor_add_transition (actor, "curl", t);
          g_object_unref (t);
        }

      clutter_transition_set_from (t, G_TYPE_DOUBLE, 0.25);
      clutter_transition_set_to (t, G_TYPE_DOUBLE, 0.0);
      clutter_timeline_rewind (CLUTTER_TIMELINE (t));
      clutter_timeline_start (CLUTTER_TIMELINE (t));

      return CLUTTER_EVENT_STOP;
    }

    static void
    on_drag_begin (ClutterDragAction   *action,
                   ClutterActor        *actor,
                   gfloat               event_x,
                   gfloat               event_y,
                   ClutterModifierType  modifiers)
    {
      gboolean is_copy = (modifiers & CLUTTER_SHIFT_MASK) ? TRUE : FALSE;
      ClutterActor *drag_handle = NULL;
      ClutterTransition *t;

      if (is_copy)
        {
          ClutterActor *stage = clutter_actor_get_stage (actor);

          drag_handle = clutter_actor_new ();
          clutter_actor_set_size (drag_handle, 48, 48);

          clutter_actor_set_background_color (drag_handle, CLUTTER_COLOR_DarkSkyBlue);

          clutter_actor_add_child (stage, drag_handle);
          clutter_actor_set_position (drag_handle, event_x, event_y);
        }
      else
        drag_handle = actor;

      clutter_drag_action_set_drag_handle (action, drag_handle);

      /* fully desaturate the actor */
      t = clutter_actor_get_transition (actor, "disable");
      if (t == NULL)
        {
          t = clutter_property_transition_new ("@effects.disable.factor");
          clutter_timeline_set_duration (CLUTTER_TIMELINE (t), 250);
          clutter_actor_add_transition (actor, "disable", t);
          g_object_unref (t);
        }

      clutter_transition_set_from (t, G_TYPE_DOUBLE, 0.0);
      clutter_transition_set_to (t, G_TYPE_DOUBLE, 1.0);
      clutter_timeline_rewind (CLUTTER_TIMELINE (t));
      clutter_timeline_start (CLUTTER_TIMELINE (t));
    }

    static void
    on_drag_end (ClutterDragAction   *action,
                 ClutterActor        *actor,
                 gfloat               event_x,
                 gfloat               event_y,
                 ClutterModifierType  modifiers)
    {
      ClutterActor *drag_handle;
      ClutterTransition *t;

      drag_handle = clutter_drag_action_get_drag_handle (action);
      if (actor != drag_handle)
        {
          gfloat real_x, real_y;
          ClutterActor *parent;

          /* if we are dragging a copy we can destroy the copy now
           * and animate the real actor to the drop coordinates,
           * transformed in the parent's coordinate space
           */
          clutter_actor_save_easing_state (drag_handle);
          clutter_actor_set_easing_mode (drag_handle, CLUTTER_LINEAR);
          clutter_actor_set_opacity (drag_handle, 0);
          clutter_actor_restore_easing_state (drag_handle);
          g_signal_connect (drag_handle, "transitions-completed",
                            G_CALLBACK (clutter_actor_destroy),
                            NULL);

          parent = clutter_actor_get_parent (actor);
          clutter_actor_transform_stage_point (parent, event_x, event_y,
                                               &real_x,
                                               &real_y);

          clutter_actor_save_easing_state (actor);
          clutter_actor_set_easing_mode (actor, CLUTTER_EASE_OUT_CUBIC);
          clutter_actor_set_position (actor, real_x, real_y);
          clutter_actor_restore_easing_state (actor);
        }

      t = clutter_actor_get_transition (actor, "disable");
      if (t == NULL)
        {
          t = clutter_property_transition_new ("@effects.disable.factor");
          clutter_timeline_set_duration (CLUTTER_TIMELINE (t), 250);
          clutter_actor_add_transition (actor, "disable", t);
          g_object_unref (t);
        }

      clutter_transition_set_from (t, G_TYPE_DOUBLE, 1.0);
      clutter_transition_set_to (t, G_TYPE_DOUBLE, 0.0);
      clutter_timeline_rewind (CLUTTER_TIMELINE (t));
      clutter_timeline_start (CLUTTER_TIMELINE (t));
    }

    static ClutterDragAxis
    get_drag_axis (const gchar *str)
    {
      if (str == NULL || *str == '\0')
        return CLUTTER_DRAG_AXIS_NONE;

      if (*str == 'x' || *str == 'X')
        return CLUTTER_DRAG_X_AXIS;

      if (*str == 'y' || *str == 'Y')
        return CLUTTER_DRAG_Y_AXIS;

      g_warn_if_reached ();

      return CLUTTER_DRAG_AXIS_NONE;
    }

    static gchar *drag_axis = NULL;
    static gint x_drag_threshold = 0;
    static gint y_drag_threshold = 0;

    static GOptionEntry entries[] = {
      {
        "x-threshold", 'x',
        0,
        G_OPTION_ARG_INT,
        &x_drag_threshold,
        "Set the horizontal drag threshold", "PIXELS"
      },
      {
        "y-threshold", 'y',
        0,
        G_OPTION_ARG_INT,
        &y_drag_threshold,
        "Set the vertical drag threshold", "PIXELS"
      },
      {
        "axis", 'a',
        0,
        G_OPTION_ARG_STRING,
        &drag_axis,
        "Set the drag axis", "AXIS"
      },

      { NULL }
    };

    int
    main (int argc, char *argv[])
    {
      ClutterActor *stage, *handle;
      ClutterAction *action;
      GError *error;

      error = NULL;
      if (clutter_init_with_args (&argc, &argv,
                                  "test-drag",
                                  entries,
                                  NULL,
                                  &error) != CLUTTER_INIT_SUCCESS)
        {
          g_print ("Unable to run drag-action: %s\n", error->message);
          g_error_free (error);

          return EXIT_FAILURE;
        }

      stage = clutter_stage_new ();
      clutter_stage_set_title (CLUTTER_STAGE (stage), "Drag Test");
      clutter_actor_set_size (stage, 800, 600);
      g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);

      handle = clutter_actor_new ();
      clutter_actor_set_background_color (handle, CLUTTER_COLOR_SkyBlue);
      clutter_actor_set_size (handle, 128, 128);
      clutter_actor_set_position (handle, (800 - 128) / 2, (600 - 128) / 2);
      clutter_actor_set_reactive (handle, TRUE);
      clutter_actor_add_child (stage, handle);
      g_signal_connect (handle, "enter-event", G_CALLBACK (on_enter), NULL);
      g_signal_connect (handle, "leave-event", G_CALLBACK (on_leave), NULL);

      action = clutter_drag_action_new ();
      clutter_drag_action_set_drag_threshold (CLUTTER_DRAG_ACTION (action),
                                              x_drag_threshold,
                                              y_drag_threshold);
      clutter_drag_action_set_drag_axis (CLUTTER_DRAG_ACTION (action),
                                         get_drag_axis (drag_axis));

      g_signal_connect (action, "drag-begin", G_CALLBACK (on_drag_begin), NULL);
      g_signal_connect (action, "drag-end", G_CALLBACK (on_drag_end), NULL);

      clutter_actor_add_action (handle, action);

      clutter_actor_add_effect_with_name (handle, "disable", clutter_desaturate_effect_new (0.0));
      clutter_actor_add_effect_with_name (handle, "curl", clutter_page_turn_effect_new (0.0, 45.0, 12.0));

      clutter_actor_show (stage);

      clutter_main ();

      return EXIT_SUCCESS;
    }

The example program above allows dragging the rectangle around the stage using a ClutterDragAction. When pressing the Shift key the actor that is going to be dragged is a separate rectangle, and when the drag ends, the original rectangle will be animated to the final coordinates.

ClutterDragAction is available since Clutter 1.4

## Details

### struct ClutterDragAction

    struct ClutterDragAction;

The ClutterDragAction structure contains only private data and should be accessed using the provided API

Since 1.4

### struct ClutterDragActionClass

    struct ClutterDragActionClass {
      void          (* drag_begin)          (ClutterDragAction   *action,
                                             ClutterActor        *actor,
                                             gfloat               event_x,
                                             gfloat               event_y,
                                             ClutterModifierType  modifiers);
      void          (* drag_motion)         (ClutterDragAction   *action,
                                             ClutterActor        *actor,
                                             gfloat               delta_x,
                                             gfloat               delta_y);
      void          (* drag_end)            (ClutterDragAction   *action,
                                             ClutterActor        *actor,
                                             gfloat               event_x,
                                             gfloat               event_y,
                                             ClutterModifierType  modifiers);
      gboolean      (* drag_progress)       (ClutterDragAction   *action,
                                             ClutterActor        *actor,
                                             gfloat               delta_x,
                                             gfloat               delta_y);
    };

The ClutterDragActionClass structure contains only private data

`drag_begin` () | class handler of the "drag-begin" signal
`drag_motion` () | class handler of the "drag-motion" signal
`drag_end` () | class handler of the "drag-end" signal
`drag_progress` () | class handler of the "drag-progress" signal

Since 1.4

### clutter_drag_action_new ()

    ClutterAction *     clutter_drag_action_new             (void);

Creates a new ClutterDragAction instance

Returns : | the newly created ClutterDragAction

Since 1.4

### clutter_drag_action_set_drag_threshold ()

    void                clutter_drag_action_set_drag_threshold
                                                            (ClutterDragAction *action,
                                                             gint x_threshold,
                                                             gint y_threshold);

Sets the horizontal and vertical drag thresholds that must be cleared by the pointer before `action` can begin the dragging.

If `x_threshold` or `y_threshold` are set to -1 then the default drag threshold stored in the "dnd-drag-threshold" property of ClutterSettings will be used.

`action` : | a ClutterDragAction
`x_threshold` : | a distance on the horizontal axis, in pixels, or -1 to use the default drag threshold from ClutterSettings
`y_threshold` : | a distance on the vertical axis, in pixels, or -1 to use the default drag threshold from ClutterSettings

Since 1.4

### clutter_drag_action_get_drag_threshold ()

    void                clutter_drag_action_get_drag_threshold
                                                            (ClutterDragAction *action,
                                                             guint *x_threshold,
                                                             guint *y_threshold);

Retrieves the values set by `clutter_drag_action_set_drag_threshold()`.

If the "x-drag-threshold" property or the "y-drag-threshold" property have been set to -1 then this function will return the default drag threshold value as stored by the "dnd-drag-threshold" property of ClutterSettings.

`action` : | a ClutterDragAction
`x_threshold` : | return location for the horizontal drag threshold value, in pixels. [out]
`y_threshold` : | return location for the vertical drag threshold value, in pixels. [out]

Since 1.4

### clutter_drag_action_set_drag_handle ()

    void                clutter_drag_action_set_drag_handle (ClutterDragAction *action,
                                                             ClutterActor *handle);

Sets the actor to be used as the drag handle.

`action` : | a ClutterDragAction
`handle` : | a ClutterActor, or `NULL` to unset. [allow-none]

Since 1.4

### clutter_drag_action_get_drag_handle ()

    ClutterActor *      clutter_drag_action_get_drag_handle (ClutterDragAction *action);

Retrieves the drag handle set by `clutter_drag_action_set_drag_handle()`

`action` : | a ClutterDragAction
Returns : | a ClutterActor, used as the drag handle, or `NULL` if none was set. [transfer none]

Since 1.4

### enum ClutterDragAxis

    typedef enum {
     /*< prefix=CLUTTER_DRAG >*/
      CLUTTER_DRAG_AXIS_NONE = 0,

      CLUTTER_DRAG_X_AXIS,
      CLUTTER_DRAG_Y_AXIS
    } ClutterDragAxis;

The axis of the constraint that should be applied on the dragging action

`CLUTTER_DRAG_AXIS_NONE` | No constraint
`CLUTTER_DRAG_X_AXIS` | Set a constraint on the X axis
`CLUTTER_DRAG_Y_AXIS` | Set a constraint on the Y axis

Since 1.4

### clutter_drag_action_set_drag_axis ()

    void                clutter_drag_action_set_drag_axis   (ClutterDragAction *action,
                                                             ClutterDragAxis axis);

Restricts the dragging action to a specific axis

`action` : | a ClutterDragAction
`axis` : | the axis to constraint the dragging to

Since 1.4

### clutter_drag_action_get_drag_axis ()

    ClutterDragAxis     clutter_drag_action_get_drag_axis   (ClutterDragAction *action);

Retrieves the axis constraint set by `clutter_drag_action_set_drag_axis()`

`action` : | a ClutterDragAction
Returns : | the axis constraint

Since 1.4

### clutter_drag_action_set_drag_area ()

    void                clutter_drag_action_set_drag_area   (ClutterDragAction *action,
                                                             const ClutterRect *drag_area);

Sets `drag_area` to constrain the dragging of the actor associated with `action`, so that it position is always within `drag_area`, expressed in parent's coordinates. If `drag_area` is `NULL`, the actor is not constrained.

`action` : | a ClutterDragAction
`drag_area` : | a ClutterRect. [allow-none]

### clutter_drag_action_get_drag_area ()

    gboolean            clutter_drag_action_get_drag_area   (ClutterDragAction *action,
                                                             ClutterRect *drag_area);

Retrieves the "drag area" associated with `action`, that is a ClutterRect that constrains the actor movements, in parents coordinates.

`action` : | a ClutterDragAction
`drag_area` : | a ClutterRect to be filled. [out caller-allocates]
Returns : | `TRUE` if the actor is actually constrained (and thus `drag_area` is valid), `FALSE` otherwise

### clutter_drag_action_get_press_coords ()

    void                clutter_drag_action_get_press_coords
                                                            (ClutterDragAction *action,
                                                             gfloat *press_x,
                                                             gfloat *press_y);

Retrieves the coordinates, in stage space, of the press event that started the dragging

`action` : | a ClutterDragAction
`press_x` : | return location for the press event's X coordinate. [out]
`press_y` : | return location for the press event's Y coordinate. [out]

Since 1.4

### clutter_drag_action_get_motion_coords ()

    void                clutter_drag_action_get_motion_coords
                                                            (ClutterDragAction *action,
                                                             gfloat *motion_x,
                                                             gfloat *motion_y);

Retrieves the coordinates, in stage space, of the latest motion event during the dragging

`action` : | a ClutterDragAction
`motion_x` : | return location for the latest motion event's X coordinate. [out]
`motion_y` : | return location for the latest motion event's Y coordinate. [out]

Since 1.4

## Property Details

### The `"drag-area"` property

      "drag-area"                ClutterRect*          : Read / Write

Constains the dragging action (or in particular, the resulting actor position) to the specified ClutterRect, in parent's coordinates.

Since 1.12

### The `"drag-area-set"` property

      "drag-area-set"            gboolean              : Read

Whether the "drag-area" property has been set.

Default value: FALSE

Since 1.12

### The `"drag-axis"` property

      "drag-axis"                ClutterDragAxis       : Read / Write

Constraints the dragging action to the specified axis

Default value: CLUTTER_DRAG_AXIS_NONE

Since 1.4

### The `"drag-handle"` property

      "drag-handle"              ClutterActor*         : Read / Write

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

### The `"x-drag-threshold"` property

      "x-drag-threshold"         gint                  : Read / Write

The horizontal threshold, in pixels, that the cursor must travel in order to begin a drag action.

When set to a positive value, ClutterDragAction will only emit "drag-begin" if the pointer has moved horizontally at least of the given amount of pixels since the button press event.

When set to -1, ClutterDragAction will use the default threshold stored in the "dnd-drag-threshold" property of ClutterSettings.

When read, this property will always return a valid drag threshold, either as set or the default one.

Allowed values: >= G_MAXULONG

Default value: 0

Since 1.4

### The `"y-drag-threshold"` property

      "y-drag-threshold"         gint                  : Read / Write

The vertical threshold, in pixels, that the cursor must travel in order to begin a drag action.

When set to a positive value, ClutterDragAction will only emit "drag-begin" if the pointer has moved vertically at least of the given amount of pixels since the button press event.

When set to -1, ClutterDragAction will use the value stored in the "dnd-drag-threshold" property of ClutterSettings.

When read, this property will always return a valid drag threshold, either as set or the default one.

Allowed values: >= G_MAXULONG

Default value: 0

Since 1.4

## Signal Details

### The `"drag-begin"` signal

    void                user_function                      (ClutterDragAction  *action,
                                                            ClutterActor       *actor,
                                                            gfloat              event_x,
                                                            gfloat              event_y,
                                                            ClutterModifierType modifiers,
                                                            gpointer            user_data)      : Run Last

The ::drag-begin signal is emitted when the ClutterDragAction starts the dragging

The emission of this signal can be delayed by using the "x-drag-threshold" and "y-drag-threshold" properties

`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
`user_data` : | user data set when the signal handler was connected.

Since 1.4

### The `"drag-end"` signal

    void                user_function                      (ClutterDragAction  *action,
                                                            ClutterActor       *actor,
                                                            gfloat              event_x,
                                                            gfloat              event_y,
                                                            ClutterModifierType modifiers,
                                                            gpointer            user_data)      : Run Last

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 "drag-begin" signal has been emitted first

`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
`user_data` : | user data set when the signal handler was connected.

Since 1.4

### The `"drag-motion"` signal

    void                user_function                      (ClutterDragAction *action,
                                                            ClutterActor      *actor,
                                                            gfloat             delta_x,
                                                            gfloat             delta_y,
                                                            gpointer           user_data)      : Run Last

The ::drag-motion signal is emitted for each motion event after the "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 "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 the "drag-progress" signal and return `FALSE` from the handler.

`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
`user_data` : | user data set when the signal handler was connected.

Since 1.4

### The `"drag-progress"` signal

    gboolean            user_function                      (ClutterDragAction *action,
                                                            ClutterActor      *actor,
                                                            gfloat             delta_x,
                                                            gfloat             delta_y,
                                                            gpointer           user_data)      : Run Last

The ::drag-progress signal is emitted for each motion event after the "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 will emit "drag-motion", if "drag-progress" emission returns `TRUE`.

`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
`user_data` : | user data set when the signal handler was connected.
Returns : | `TRUE` if the drag should continue, and `FALSE` if it should be stopped.

Since 1.12