timeline: Add support for step() progress
The CSS3 Transitions specification from the W3C defines the possibility of using a parametrized step() timing function, with the following prototype: steps(n_steps, [ start | end ]) where @n_steps represents the number of steps used to divide an interval between 0 and 1; the 'start' and 'end' tokens describe whether the value change should happen at the start of the transition, or at the end. For instance, the "steps(3, start)" timing function has the following profile: 1 | x | | | x---| | ' | | x---' | | ' | 0 |---' | Whereas the "steps(3, end)" timing function has the following profile: 1 | x---| | ' | | x---' | | ' | x---' | | | 0 | | Since ClutterTimeline uses an enumeration for controlling the progress mode, we need additional API to define the parameters of the steps() progress; for this reason, we need a CLUTTER_STEPS enumeration value, and a method for setting the number of steps and the value transition policy. The CSS3 Transitions spec helpfully also defines a step-start and a step-end shorthands, which expand to step(1, start) and step(1, end) respectively; we can provide a CLUTTER_STEP_START and CLUTTER_STEP_END enumeration values for those.
This commit is contained in:
parent
d7eb57a711
commit
4546f84408
@ -379,6 +379,29 @@ clutter_ease_in_out_bounce (double t,
|
||||
return ease_out_bounce_internal (t * 2 - d, d) * 0.5 + 1.0 * 0.5;
|
||||
}
|
||||
|
||||
static inline double
|
||||
ease_steps_end (double p,
|
||||
int n_steps)
|
||||
{
|
||||
return floor (p * (double) n_steps) / (double) n_steps;
|
||||
}
|
||||
|
||||
double
|
||||
clutter_ease_steps_start (double t,
|
||||
double d,
|
||||
int n_steps)
|
||||
{
|
||||
return 1.0 - ease_steps_end (1.0 - (t / d), n_steps);
|
||||
}
|
||||
|
||||
double
|
||||
clutter_ease_steps_end (double t,
|
||||
double d,
|
||||
int n_steps)
|
||||
{
|
||||
return ease_steps_end ((t / d), n_steps);
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* _clutter_animation_modes:
|
||||
*
|
||||
@ -423,6 +446,10 @@ static const struct {
|
||||
{ CLUTTER_EASE_OUT_BOUNCE, clutter_ease_out_bounce, "easeOutBounce" },
|
||||
{ CLUTTER_EASE_IN_OUT_BOUNCE, clutter_ease_in_out_bounce, "easeInOutBounce" },
|
||||
|
||||
{ CLUTTER_STEPS, (ClutterEasingFunc) clutter_ease_steps_end, "steps" },
|
||||
{ CLUTTER_STEP_START, (ClutterEasingFunc) clutter_ease_steps_start, "stepStart" },
|
||||
{ CLUTTER_STEP_END, (ClutterEasingFunc) clutter_ease_steps_end, "stepEnd" },
|
||||
|
||||
{ CLUTTER_ANIMATION_LAST, NULL, "sentinel" },
|
||||
};
|
||||
|
||||
|
@ -121,6 +121,15 @@ G_GNUC_INTERNAL
|
||||
double clutter_ease_in_out_bounce (double t,
|
||||
double d);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
double clutter_ease_steps_start (double t,
|
||||
double d,
|
||||
int steps);
|
||||
G_GNUC_INTERNAL
|
||||
double clutter_ease_steps_end (double t,
|
||||
double d,
|
||||
int steps);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __CLUTTER_EASING_H__ */
|
||||
|
@ -160,6 +160,12 @@ typedef enum { /*< prefix=CLUTTER_REQUEST >*/
|
||||
* tweening, with bounce on end
|
||||
* @CLUTTER_EASE_IN_OUT_BOUNCE: exponentially decaying parabolic (bounce)
|
||||
* tweening, with bounce on both ends
|
||||
* @CLUTTER_STEPS: parametrized step function; see clutter_timeline_set_step_progress()
|
||||
* for further details. (Since 1.12)
|
||||
* @CLUTTER_STEP_START: equivalent to %CLUTTER_STEPS with a number of steps
|
||||
* equal to 1, and a step mode of %CLUTTER_STEP_MODE_START. (Since 1.12)
|
||||
* @CLUTTER_STEP_END: equivalent to %CLUTTER_STEPS with a number of steps
|
||||
* equal to 1, and a step mode of %CLUTTER_STEP_MODE_END. (Since: 1.12)
|
||||
* @CLUTTER_ANIMATION_LAST: last animation mode, used as a guard for
|
||||
* registered global alpha functions
|
||||
*
|
||||
@ -233,6 +239,11 @@ typedef enum {
|
||||
CLUTTER_EASE_OUT_BOUNCE,
|
||||
CLUTTER_EASE_IN_OUT_BOUNCE,
|
||||
|
||||
/* step functions (see css3-transitions) */
|
||||
CLUTTER_STEPS,
|
||||
CLUTTER_STEP_START, /* steps(1, start) */
|
||||
CLUTTER_STEP_END, /* steps(1, end) */
|
||||
|
||||
/* guard, before registered alpha functions */
|
||||
CLUTTER_ANIMATION_LAST
|
||||
} ClutterAnimationMode;
|
||||
@ -1273,6 +1284,26 @@ typedef enum {
|
||||
CLUTTER_REPEAT_BOTH = CLUTTER_REPEAT_X_AXIS | CLUTTER_REPEAT_Y_AXIS
|
||||
} ClutterContentRepeat;
|
||||
|
||||
/**
|
||||
* ClutterStepMode:
|
||||
* @CLUTTER_STEP_MODE_START: The change in the value of a
|
||||
* %CLUTTER_STEP progress mode should occur at the start of
|
||||
* the transition
|
||||
* @CLUTTER_STEP_MODE_END: The change in the value of a
|
||||
* %CLUTTER_STEP progress mode should occur at the end of
|
||||
* the transition
|
||||
*
|
||||
* Change the value transition of a step function.
|
||||
*
|
||||
* See clutter_timeline_set_step_progress().
|
||||
*
|
||||
* Since: 1.12
|
||||
*/
|
||||
typedef enum {
|
||||
CLUTTER_STEP_MODE_START,
|
||||
CLUTTER_STEP_MODE_END
|
||||
} ClutterStepMode;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __CLUTTER_ENUMS_H__ */
|
||||
|
@ -150,6 +150,9 @@ struct _ClutterTimelinePrivate
|
||||
GDestroyNotify progress_notify;
|
||||
ClutterAnimationMode progress_mode;
|
||||
|
||||
gint n_steps;
|
||||
ClutterStepMode step_mode;
|
||||
|
||||
guint is_playing : 1;
|
||||
|
||||
/* If we've just started playing and haven't yet gotten
|
||||
@ -812,6 +815,8 @@ clutter_timeline_init (ClutterTimeline *self)
|
||||
ClutterTimelinePrivate);
|
||||
|
||||
priv->progress_mode = CLUTTER_LINEAR;
|
||||
priv->n_steps = 1;
|
||||
priv->step_mode = CLUTTER_STEP_MODE_END;
|
||||
}
|
||||
|
||||
struct CheckIfMarkerHitClosure
|
||||
@ -2125,6 +2130,28 @@ clutter_timeline_progress_func (ClutterTimeline *timeline,
|
||||
{
|
||||
ClutterTimelinePrivate *priv = timeline->priv;
|
||||
|
||||
/* parametrized easing functions need to be handled separately */
|
||||
switch (priv->progress_mode)
|
||||
{
|
||||
case CLUTTER_STEPS:
|
||||
if (priv->step_mode == CLUTTER_STEP_MODE_START)
|
||||
return clutter_ease_steps_start (elapsed, duration, priv->n_steps);
|
||||
else if (priv->step_mode == CLUTTER_STEP_MODE_END)
|
||||
return clutter_ease_steps_end (elapsed, duration, priv->n_steps);
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
|
||||
case CLUTTER_STEP_START:
|
||||
return clutter_ease_steps_start (elapsed, duration, 1);
|
||||
|
||||
case CLUTTER_STEP_END:
|
||||
return clutter_ease_steps_end (elapsed, duration, 1);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return clutter_easing_for_mode (priv->progress_mode, elapsed, duration);
|
||||
}
|
||||
|
||||
@ -2243,3 +2270,72 @@ clutter_timeline_get_current_repeat (ClutterTimeline *timeline)
|
||||
|
||||
return timeline->priv->current_repeat;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_timeline_set_step_progress:
|
||||
* @timeline: a #ClutterTimeline
|
||||
* @n_steps: the number of steps
|
||||
* @step_mode: whether the change should happen at the start
|
||||
* or at the end of the step
|
||||
*
|
||||
* Sets the #ClutterTimeline:progress-mode of the @timeline to %CLUTTER_STEPS
|
||||
* and provides the parameters of the step function.
|
||||
*
|
||||
* Since: 1.12
|
||||
*/
|
||||
void
|
||||
clutter_timeline_set_step_progress (ClutterTimeline *timeline,
|
||||
gint n_steps,
|
||||
ClutterStepMode step_mode)
|
||||
{
|
||||
ClutterTimelinePrivate *priv;
|
||||
|
||||
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
|
||||
g_return_if_fail (n_steps > 0);
|
||||
|
||||
priv = timeline->priv;
|
||||
|
||||
if (priv->progress_mode == CLUTTER_STEPS &&
|
||||
priv->n_steps == n_steps &&
|
||||
priv->step_mode == step_mode)
|
||||
return;
|
||||
|
||||
priv->n_steps = n_steps;
|
||||
priv->step_mode = step_mode;
|
||||
clutter_timeline_set_progress_mode (timeline, CLUTTER_STEPS);
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_timeline_get_step_progress:
|
||||
* @timeline: a #ClutterTimeline
|
||||
* @n_steps: (out): return location for the number of steps, or %NULL
|
||||
* @step_mode: (out): return location for the value change policy,
|
||||
* or %NULL
|
||||
*
|
||||
* Retrieves the parameters of the step progress mode used by @timeline.
|
||||
*
|
||||
* Return value: %TRUE if the @timeline is using a step progress
|
||||
* mode, and %FALSE otherwise
|
||||
*
|
||||
* Since: 1.12
|
||||
*/
|
||||
gboolean
|
||||
clutter_timeline_get_step_progress (ClutterTimeline *timeline,
|
||||
gint *n_steps,
|
||||
ClutterStepMode *step_mode)
|
||||
{
|
||||
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
|
||||
|
||||
if (timeline->priv->progress_mode != CLUTTER_STEPS ||
|
||||
timeline->priv->progress_mode != CLUTTER_STEP_START ||
|
||||
timeline->priv->progress_mode != CLUTTER_STEP_END)
|
||||
return FALSE;
|
||||
|
||||
if (n_steps != NULL)
|
||||
*n_steps = timeline->priv->n_steps;
|
||||
|
||||
if (step_mode != NULL)
|
||||
*step_mode = timeline->priv->step_mode;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -170,6 +170,14 @@ void clutter_timeline_set_progress_mode (Clutter
|
||||
ClutterAnimationMode mode);
|
||||
CLUTTER_AVAILABLE_IN_1_10
|
||||
ClutterAnimationMode clutter_timeline_get_progress_mode (ClutterTimeline *timeline);
|
||||
CLUTTER_AVAILABLE_IN_1_12
|
||||
void clutter_timeline_set_step_progress (ClutterTimeline *timeline,
|
||||
gint n_steps,
|
||||
ClutterStepMode step_mode);
|
||||
CLUTTER_AVAILABLE_IN_1_12
|
||||
gboolean clutter_timeline_get_step_progress (ClutterTimeline *timeline,
|
||||
gint *n_steps,
|
||||
ClutterStepMode *step_mode);
|
||||
|
||||
CLUTTER_AVAILABLE_IN_1_10
|
||||
gint64 clutter_timeline_get_duration_hint (ClutterTimeline *timeline);
|
||||
|
@ -1283,6 +1283,7 @@ clutter_state_set_key
|
||||
clutter_state_set_state
|
||||
clutter_state_warp_to_state
|
||||
clutter_static_color_get_type
|
||||
clutter_step_mode_get_type
|
||||
clutter_swipe_action_get_type
|
||||
clutter_swipe_action_new
|
||||
clutter_swipe_direction_get_type
|
||||
@ -1463,6 +1464,7 @@ clutter_timeline_get_loop
|
||||
clutter_timeline_get_progress_mode
|
||||
clutter_timeline_get_progress
|
||||
clutter_timeline_get_repeat_count
|
||||
clutter_timeline_get_step_progress
|
||||
clutter_timeline_get_type
|
||||
clutter_timeline_has_marker
|
||||
clutter_timeline_is_playing
|
||||
@ -1478,6 +1480,7 @@ clutter_timeline_set_loop
|
||||
clutter_timeline_set_progress_func
|
||||
clutter_timeline_set_progress_mode
|
||||
clutter_timeline_set_repeat_count
|
||||
clutter_timeline_set_step_progress
|
||||
clutter_timeline_skip
|
||||
clutter_timeline_start
|
||||
clutter_timeline_stop
|
||||
|
@ -22,6 +22,7 @@ units_sources += \
|
||||
state.c \
|
||||
timeline.c \
|
||||
timeline-interpolate.c \
|
||||
timeline-progress.c \
|
||||
timeline-rewind.c \
|
||||
$(NULL)
|
||||
|
||||
|
@ -231,6 +231,8 @@ main (int argc, char **argv)
|
||||
TEST_CONFORM_SIMPLE ("/timeline", timeline_markers_from_script);
|
||||
TEST_CONFORM_SKIP (g_test_slow (), "/timeline", timeline_interpolation);
|
||||
TEST_CONFORM_SKIP (g_test_slow (), "/timeline", timeline_rewind);
|
||||
TEST_CONFORM_SIMPLE ("/timeline", timeline_progress_mode);
|
||||
TEST_CONFORM_SIMPLE ("/timeline", timeline_progress_step);
|
||||
|
||||
TEST_CONFORM_SIMPLE ("/score", score_base);
|
||||
|
||||
|
110
tests/conform/timeline-progress.c
Normal file
110
tests/conform/timeline-progress.c
Normal file
@ -0,0 +1,110 @@
|
||||
#include <glib.h>
|
||||
#include <clutter/clutter.h>
|
||||
#include "test-conform-common.h"
|
||||
|
||||
void
|
||||
timeline_progress_step (TestConformSimpleFixture *fixture G_GNUC_UNUSED,
|
||||
gconstpointer dummy G_GNUC_UNUSED)
|
||||
{
|
||||
ClutterTimeline *timeline;
|
||||
|
||||
timeline = clutter_timeline_new (1000);
|
||||
|
||||
if (g_test_verbose ())
|
||||
g_print ("mode: step(3, end)\n");
|
||||
|
||||
clutter_timeline_rewind (timeline);
|
||||
clutter_timeline_set_step_progress (timeline, 3, CLUTTER_STEP_MODE_END);
|
||||
g_assert_cmpint (clutter_timeline_get_progress (timeline), ==, 0);
|
||||
|
||||
clutter_timeline_advance (timeline, 1000 / 3 - 1);
|
||||
g_assert_cmpint (clutter_timeline_get_progress (timeline) * 1000, ==, 0);
|
||||
|
||||
clutter_timeline_advance (timeline, 1000 / 3 + 1);
|
||||
g_assert_cmpint (clutter_timeline_get_progress (timeline) * 1000, ==, 333);
|
||||
|
||||
clutter_timeline_advance (timeline, 1000 / 3 * 2 - 1);
|
||||
g_assert_cmpint (clutter_timeline_get_progress (timeline) * 1000, ==, 333);
|
||||
|
||||
clutter_timeline_advance (timeline, 1000 / 3 * 2 + 1);
|
||||
g_assert_cmpint (clutter_timeline_get_progress (timeline) * 1000, ==, 666);
|
||||
|
||||
clutter_timeline_rewind (timeline);
|
||||
clutter_timeline_set_progress_mode (timeline, CLUTTER_STEP_START);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
|
||||
|
||||
clutter_timeline_advance (timeline, 1);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
|
||||
|
||||
clutter_timeline_advance (timeline, 500);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
|
||||
|
||||
clutter_timeline_advance (timeline, 999);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
|
||||
|
||||
clutter_timeline_advance (timeline, 1000);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
|
||||
|
||||
if (g_test_verbose ())
|
||||
g_print ("mode: step-start\n");
|
||||
|
||||
clutter_timeline_rewind (timeline);
|
||||
clutter_timeline_set_progress_mode (timeline, CLUTTER_STEP_START);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
|
||||
|
||||
clutter_timeline_advance (timeline, 1);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
|
||||
|
||||
clutter_timeline_advance (timeline, 500);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
|
||||
|
||||
clutter_timeline_advance (timeline, 999);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
|
||||
|
||||
clutter_timeline_advance (timeline, 1000);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
|
||||
|
||||
if (g_test_verbose ())
|
||||
g_print ("mode: step-end\n");
|
||||
|
||||
clutter_timeline_rewind (timeline);
|
||||
clutter_timeline_set_progress_mode (timeline, CLUTTER_STEP_END);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
|
||||
|
||||
clutter_timeline_advance (timeline, 1);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
|
||||
|
||||
clutter_timeline_advance (timeline, 500);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
|
||||
|
||||
clutter_timeline_advance (timeline, 999);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
|
||||
|
||||
clutter_timeline_advance (timeline, 1000);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
|
||||
|
||||
g_object_unref (timeline);
|
||||
}
|
||||
|
||||
void
|
||||
timeline_progress_mode (TestConformSimpleFixture *fixture G_GNUC_UNUSED,
|
||||
gconstpointer dummy G_GNUC_UNUSED)
|
||||
{
|
||||
ClutterTimeline *timeline;
|
||||
|
||||
timeline = clutter_timeline_new (1000);
|
||||
|
||||
g_assert (clutter_timeline_get_progress_mode (timeline) == CLUTTER_LINEAR);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
|
||||
|
||||
clutter_timeline_advance (timeline, 500);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.5);
|
||||
|
||||
clutter_timeline_advance (timeline, 1000);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
|
||||
|
||||
clutter_timeline_rewind (timeline);
|
||||
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0);
|
||||
|
||||
g_object_unref (timeline);
|
||||
}
|
Loading…
Reference in New Issue
Block a user