mirror of
https://github.com/brl/mutter.git
synced 2024-11-26 18:11:05 -05:00
90a2401299
The documentation said that you should return TRUE to mark that the action was handled, but the code did the reverse. Change the documentation to reflect what all the other gestures do. https://bugzilla.gnome.org/show_bug.cgi?id=689061
511 lines
15 KiB
C
511 lines
15 KiB
C
/*
|
|
* Clutter.
|
|
*
|
|
* An OpenGL based 'interactive canvas' library.
|
|
*
|
|
* Copyright (C) 2012 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:
|
|
* Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
|
|
*/
|
|
|
|
/**
|
|
* SECTION:clutter-zoom-action
|
|
* @Title: ClutterZoomAction
|
|
* @Short_Description: Action enabling zooming on actors
|
|
*
|
|
* #ClutterZoomAction is a sub-class of #ClutterGestureAction that
|
|
* implements all the necessary logic for zooming actors.
|
|
*
|
|
* The simplest usage of #ClutterZoomAction consists in adding it to
|
|
* a #ClutterActor and setting it as reactive; for instance, the following
|
|
* code:
|
|
*
|
|
* |[
|
|
* clutter_actor_add_action (actor, clutter_zoom_action_new ());
|
|
* clutter_actor_set_reactive (actor, TRUE);
|
|
* ]|
|
|
*
|
|
* will automatically result in the actor to be scale according to the
|
|
* distance between 2 touch points.
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <math.h>
|
|
|
|
#include "clutter-zoom-action.h"
|
|
|
|
#include "clutter-debug.h"
|
|
#include "clutter-enum-types.h"
|
|
#include "clutter-marshal.h"
|
|
#include "clutter-private.h"
|
|
#include "clutter-stage-private.h"
|
|
|
|
typedef struct
|
|
{
|
|
gfloat start_x;
|
|
gfloat start_y;
|
|
gfloat transformed_start_x;
|
|
gfloat transformed_start_y;
|
|
|
|
gfloat update_x;
|
|
gfloat update_y;
|
|
gfloat transformed_update_x;
|
|
gfloat transformed_update_y;
|
|
} ZoomPoint;
|
|
|
|
struct _ClutterZoomActionPrivate
|
|
{
|
|
ClutterStage *stage;
|
|
|
|
ClutterZoomAxis zoom_axis;
|
|
|
|
ZoomPoint points[2];
|
|
|
|
ClutterPoint focal_point;
|
|
ClutterPoint transformed_focal_point;
|
|
|
|
gfloat initial_x;
|
|
gfloat initial_y;
|
|
gfloat initial_z;
|
|
|
|
gdouble initial_scale_x;
|
|
gdouble initial_scale_y;
|
|
|
|
gdouble zoom_initial_distance;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_ZOOM_AXIS,
|
|
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *zoom_props[PROP_LAST] = { NULL, };
|
|
|
|
enum
|
|
{
|
|
ZOOM,
|
|
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint zoom_signals[LAST_SIGNAL] = { 0, };
|
|
|
|
G_DEFINE_TYPE (ClutterZoomAction, clutter_zoom_action, CLUTTER_TYPE_GESTURE_ACTION);
|
|
|
|
static void
|
|
capture_point_initial_position (ClutterGestureAction *action,
|
|
ClutterActor *actor,
|
|
gint index,
|
|
ZoomPoint *point)
|
|
{
|
|
clutter_gesture_action_get_motion_coords (action, index,
|
|
&point->start_x,
|
|
&point->start_y);
|
|
|
|
point->transformed_start_x = point->update_x = point->start_x;
|
|
point->transformed_start_y = point->update_x = point->start_y;
|
|
clutter_actor_transform_stage_point (actor,
|
|
point->start_x, point->start_y,
|
|
&point->transformed_start_x,
|
|
&point->transformed_start_y);
|
|
point->transformed_update_x = point->transformed_start_x;
|
|
point->transformed_update_y = point->transformed_start_y;
|
|
}
|
|
|
|
static void
|
|
capture_point_update_position (ClutterGestureAction *action,
|
|
ClutterActor *actor,
|
|
gint index,
|
|
ZoomPoint *point)
|
|
{
|
|
clutter_gesture_action_get_motion_coords (action, index,
|
|
&point->update_x,
|
|
&point->update_y);
|
|
|
|
point->transformed_update_x = point->update_x;
|
|
point->transformed_update_y = point->update_y;
|
|
clutter_actor_transform_stage_point (actor,
|
|
point->update_x, point->update_y,
|
|
&point->transformed_update_x,
|
|
&point->transformed_update_y);
|
|
}
|
|
|
|
static gboolean
|
|
clutter_zoom_action_gesture_begin (ClutterGestureAction *action,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterZoomActionPrivate *priv = ((ClutterZoomAction *) action)->priv;
|
|
gfloat dx, dy;
|
|
|
|
capture_point_initial_position (action, actor, 0, &priv->points[0]);
|
|
capture_point_initial_position (action, actor, 1, &priv->points[1]);
|
|
|
|
dx = priv->points[1].transformed_start_x - priv->points[0].transformed_start_x;
|
|
dy = priv->points[1].transformed_start_y - priv->points[0].transformed_start_y;
|
|
priv->zoom_initial_distance = sqrt (dx * dx + dy * dy);
|
|
|
|
clutter_actor_get_translation (actor,
|
|
&priv->initial_x,
|
|
&priv->initial_y,
|
|
&priv->initial_z);
|
|
clutter_actor_get_scale (actor,
|
|
&priv->initial_scale_x,
|
|
&priv->initial_scale_y);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
clutter_zoom_action_gesture_progress (ClutterGestureAction *action,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterZoomActionPrivate *priv = ((ClutterZoomAction *) action)->priv;
|
|
gdouble distance, new_scale;
|
|
gfloat dx, dy;
|
|
gboolean retval;
|
|
|
|
capture_point_update_position (action, actor, 0, &priv->points[0]);
|
|
capture_point_update_position (action, actor, 1, &priv->points[1]);
|
|
|
|
dx = priv->points[1].update_x - priv->points[0].update_x;
|
|
dy = priv->points[1].update_y - priv->points[0].update_y;
|
|
distance = sqrt (dx * dx + dy * dy);
|
|
|
|
if (distance == 0)
|
|
return TRUE;
|
|
|
|
priv->focal_point.x = (priv->points[0].update_x + priv->points[1].update_x) / 2;
|
|
priv->focal_point.y = (priv->points[0].update_y + priv->points[1].update_y) / 2;
|
|
priv->transformed_focal_point.x = (priv->points[0].transformed_update_x +
|
|
priv->points[1].transformed_update_x) / 2;
|
|
priv->transformed_focal_point.y = (priv->points[0].transformed_update_y +
|
|
priv->points[1].transformed_update_y) / 2;
|
|
|
|
|
|
new_scale = distance / priv->zoom_initial_distance;
|
|
|
|
g_signal_emit (action, zoom_signals[ZOOM], 0,
|
|
actor, &priv->focal_point, new_scale,
|
|
&retval);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
clutter_zoom_action_gesture_cancel (ClutterGestureAction *action,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterZoomActionPrivate *priv = ((ClutterZoomAction *) action)->priv;
|
|
|
|
clutter_actor_set_translation (actor,
|
|
priv->initial_x,
|
|
priv->initial_y,
|
|
priv->initial_z);
|
|
clutter_actor_set_scale (actor, priv->initial_scale_x, priv->initial_scale_y);
|
|
}
|
|
|
|
static gboolean
|
|
clutter_zoom_action_real_zoom (ClutterZoomAction *action,
|
|
ClutterActor *actor,
|
|
ClutterPoint *focal_point,
|
|
gdouble factor)
|
|
{
|
|
ClutterZoomActionPrivate *priv = action->priv;
|
|
ClutterActor *parent = clutter_actor_get_parent (actor);
|
|
gfloat x, y, z;
|
|
gdouble scale_x, scale_y;
|
|
ClutterVertex out, in;
|
|
|
|
clutter_actor_get_scale (actor, &scale_x, &scale_y);
|
|
|
|
switch (priv->zoom_axis)
|
|
{
|
|
case CLUTTER_ZOOM_BOTH:
|
|
clutter_actor_set_scale (actor, factor, factor);
|
|
break;
|
|
|
|
case CLUTTER_ZOOM_X_AXIS:
|
|
clutter_actor_set_scale (actor, factor, scale_y);
|
|
break;
|
|
|
|
case CLUTTER_ZOOM_Y_AXIS:
|
|
clutter_actor_set_scale (actor, scale_x, factor);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
in.x = priv->transformed_focal_point.x;
|
|
in.y = priv->transformed_focal_point.y;
|
|
in.z = 0;
|
|
|
|
clutter_actor_apply_relative_transform_to_point (actor,
|
|
parent,
|
|
&in, &out);
|
|
|
|
|
|
clutter_actor_get_translation (actor, &x, &y, &z);
|
|
clutter_actor_set_translation (actor,
|
|
x + priv->focal_point.x - out.x,
|
|
y + priv->focal_point.y - out.y,
|
|
z);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
clutter_zoom_action_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterZoomAction *action = CLUTTER_ZOOM_ACTION (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_ZOOM_AXIS:
|
|
clutter_zoom_action_set_zoom_axis (action, g_value_get_enum (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_zoom_action_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterZoomActionPrivate *priv = CLUTTER_ZOOM_ACTION (gobject)->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_ZOOM_AXIS:
|
|
g_value_set_enum (value, priv->zoom_axis);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_zoom_action_dispose (GObject *gobject)
|
|
{
|
|
G_OBJECT_CLASS (clutter_zoom_action_parent_class)->dispose (gobject);
|
|
}
|
|
|
|
static void
|
|
clutter_zoom_action_class_init (ClutterZoomActionClass *klass)
|
|
{
|
|
ClutterGestureActionClass *gesture_class =
|
|
CLUTTER_GESTURE_ACTION_CLASS (klass);
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
g_type_class_add_private (klass, sizeof (ClutterZoomActionPrivate));
|
|
|
|
gobject_class->set_property = clutter_zoom_action_set_property;
|
|
gobject_class->get_property = clutter_zoom_action_get_property;
|
|
gobject_class->dispose = clutter_zoom_action_dispose;
|
|
|
|
gesture_class->gesture_begin = clutter_zoom_action_gesture_begin;
|
|
gesture_class->gesture_progress = clutter_zoom_action_gesture_progress;
|
|
gesture_class->gesture_cancel = clutter_zoom_action_gesture_cancel;
|
|
|
|
klass->zoom = clutter_zoom_action_real_zoom;
|
|
|
|
/**
|
|
* ClutterZoomAction:zoom-axis:
|
|
*
|
|
* Constraints the zooming action to the specified axis
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
zoom_props[PROP_ZOOM_AXIS] =
|
|
g_param_spec_enum ("zoom-axis",
|
|
P_("Zoom Axis"),
|
|
P_("Constraints the zoom to an axis"),
|
|
CLUTTER_TYPE_ZOOM_AXIS,
|
|
CLUTTER_ZOOM_BOTH,
|
|
CLUTTER_PARAM_READWRITE);
|
|
|
|
g_object_class_install_properties (gobject_class,
|
|
PROP_LAST,
|
|
zoom_props);
|
|
|
|
/**
|
|
* ClutterZoomAction::zoom:
|
|
* @action: the #ClutterZoomAction that emitted the signal
|
|
* @actor: the #ClutterActor attached to the action
|
|
* @distance: the initial distance between the 2 touch points
|
|
*
|
|
* The ::zoom signal is emitted for each touch event after the
|
|
* #ClutterZoomAction::zoom-begin signal has been emitted.
|
|
*
|
|
* The components of the distance between the touch begin event and
|
|
* the latest touch update 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_zoom_action_get_motion_coords().
|
|
*
|
|
* The default handler of the signal will call
|
|
* clutter_actor_set_scale() on @actor using the ratio of the first
|
|
* distance between the 2 touch points and the current distance. 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.
|
|
*
|
|
* Return value: %TRUE if the zoom should continue, and %FALSE if
|
|
* the zoom should be cancelled.
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
zoom_signals[ZOOM] =
|
|
g_signal_new (I_("zoom"),
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterZoomActionClass, zoom),
|
|
_clutter_boolean_continue_accumulator, NULL,
|
|
_clutter_marshal_BOOLEAN__OBJECT_BOXED_DOUBLE,
|
|
G_TYPE_BOOLEAN, 3,
|
|
CLUTTER_TYPE_ACTOR,
|
|
CLUTTER_TYPE_POINT,
|
|
G_TYPE_DOUBLE);
|
|
}
|
|
|
|
static void
|
|
clutter_zoom_action_init (ClutterZoomAction *self)
|
|
{
|
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_ZOOM_ACTION,
|
|
ClutterZoomActionPrivate);
|
|
|
|
self->priv->zoom_axis = CLUTTER_ZOOM_BOTH;
|
|
|
|
clutter_gesture_action_set_n_touch_points (CLUTTER_GESTURE_ACTION (self), 2);
|
|
}
|
|
|
|
/**
|
|
* clutter_zoom_action_new:
|
|
*
|
|
* Creates a new #ClutterZoomAction instance
|
|
*
|
|
* Return value: the newly created #ClutterZoomAction
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
ClutterAction *
|
|
clutter_zoom_action_new (void)
|
|
{
|
|
return g_object_new (CLUTTER_TYPE_ZOOM_ACTION, NULL);
|
|
}
|
|
|
|
/**
|
|
* clutter_zoom_action_set_zoom_axis:
|
|
* @action: a #ClutterZoomAction
|
|
* @axis: the axis to constraint the zooming to
|
|
*
|
|
* Restricts the zooming action to a specific axis
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
void
|
|
clutter_zoom_action_set_zoom_axis (ClutterZoomAction *action,
|
|
ClutterZoomAxis axis)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ZOOM_ACTION (action));
|
|
g_return_if_fail (axis >= CLUTTER_ZOOM_X_AXIS &&
|
|
axis <= CLUTTER_ZOOM_BOTH);
|
|
|
|
if (action->priv->zoom_axis == axis)
|
|
return;
|
|
|
|
action->priv->zoom_axis = axis;
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (action), zoom_props[PROP_ZOOM_AXIS]);
|
|
}
|
|
|
|
/**
|
|
* clutter_zoom_action_get_zoom_axis:
|
|
* @action: a #ClutterZoomAction
|
|
*
|
|
* Retrieves the axis constraint set by clutter_zoom_action_set_zoom_axis()
|
|
*
|
|
* Return value: the axis constraint
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
ClutterZoomAxis
|
|
clutter_zoom_action_get_zoom_axis (ClutterZoomAction *action)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ZOOM_ACTION (action),
|
|
CLUTTER_ZOOM_BOTH);
|
|
|
|
return action->priv->zoom_axis;
|
|
}
|
|
|
|
/**
|
|
* clutter_zoom_action_get_focal_point:
|
|
* @action: a #ClutterZoomAction
|
|
* @point: (out): a #ClutterPoint
|
|
*
|
|
* Retrieves the focal point of the current zoom
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
void
|
|
clutter_zoom_action_get_focal_point (ClutterZoomAction *action,
|
|
ClutterPoint *point)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ZOOM_ACTION (action));
|
|
g_return_if_fail (point != NULL);
|
|
|
|
*point = action->priv->focal_point;
|
|
}
|
|
|
|
/**
|
|
* clutter_zoom_action_get_transformed_focal_point:
|
|
* @action: a #ClutterZoomAction
|
|
* @point: (out): a #ClutterPoint
|
|
*
|
|
* Retrieves the focal point relative to the actor's coordinates of
|
|
* the current zoom
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
void
|
|
clutter_zoom_action_get_transformed_focal_point (ClutterZoomAction *action,
|
|
ClutterPoint *point)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_ZOOM_ACTION (action));
|
|
g_return_if_fail (point != NULL);
|
|
|
|
*point = action->priv->transformed_focal_point;
|
|
}
|