st: Add transition API to StAdjustment

StAdjustment implements the ClutterAnimatable interface, so we can
already animate its properties with ClutterPropertyTransitions.

But as it is currently not possible to associate a transition with
an adjustment, it must be owned (and kept alive in case of GC) by
the calling code.

Change that by implementing the same (add|remove|get)_transition() API
as ClutterActor, so we can use a familiar API and even duck typing in
case of javascript.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/669
This commit is contained in:
Florian Müllner 2019-08-02 16:58:39 +02:00
parent 79b54f65b4
commit 9097c5e9c0
2 changed files with 172 additions and 0 deletions

View File

@ -45,6 +45,8 @@ struct _StAdjustmentPrivate
* not all properties may be set yet. */
guint is_constructing : 1;
GHashTable *transitions;
gdouble lower;
gdouble upper;
gdouble value;
@ -85,6 +87,14 @@ enum
static guint signals[LAST_SIGNAL] = { 0, };
typedef struct _TransitionClosure
{
StAdjustment *adjustment;
ClutterTransition *transition;
char *name;
gulong completed_id;
} TransitionClosure;
static gboolean st_adjustment_set_lower (StAdjustment *adjustment,
gdouble lower);
static gboolean st_adjustment_set_upper (StAdjustment *adjustment,
@ -201,6 +211,17 @@ st_adjustment_set_property (GObject *gobject,
}
}
static void
st_adjustment_dispose (GObject *object)
{
StAdjustmentPrivate *priv;
priv = st_adjustment_get_instance_private (ST_ADJUSTMENT (object));
g_clear_pointer (&priv->transitions, g_hash_table_unref);
G_OBJECT_CLASS (st_adjustment_parent_class)->dispose (object);
}
static void
st_adjustment_class_init (StAdjustmentClass *klass)
{
@ -209,6 +230,7 @@ st_adjustment_class_init (StAdjustmentClass *klass)
object_class->constructed = st_adjustment_constructed;
object_class->get_property = st_adjustment_get_property;
object_class->set_property = st_adjustment_set_property;
object_class->dispose = st_adjustment_dispose;
props[PROP_LOWER] =
g_param_spec_double ("lower", "Lower", "Lower bound",
@ -588,3 +610,145 @@ st_adjustment_adjust_for_scroll_event (StAdjustment *adjustment,
new_value = priv->value + delta * scroll_unit;
st_adjustment_set_value (adjustment, new_value);
}
static void
transition_closure_free (gpointer data)
{
TransitionClosure *clos;
ClutterTimeline *timeline;
if (G_UNLIKELY (data == NULL))
return;
clos = data;
timeline = CLUTTER_TIMELINE (clos->transition);
g_signal_handler_disconnect (clos->transition, clos->completed_id);
if (clutter_timeline_is_playing (timeline))
clutter_timeline_stop (timeline);
g_object_unref (clos->transition);
g_free (clos->name);
g_free (clos);
}
static void
remove_transition (StAdjustment *adjustment,
const char *name)
{
StAdjustmentPrivate *priv = st_adjustment_get_instance_private (adjustment);
g_hash_table_remove (priv->transitions, name);
if (g_hash_table_size (priv->transitions) == 0)
g_clear_pointer (&priv->transitions, g_hash_table_unref);
}
static void
on_transition_stopped (ClutterTransition *transition,
gboolean is_finished,
TransitionClosure *clos)
{
StAdjustment *adjustment = clos->adjustment;
if (!clutter_transition_get_remove_on_complete (transition))
return;
/* Take a reference, because removing the closure will
* release the reference on the transition, and we want
* it to survive the signal emission; ClutterTransition's
* own ::stopped signal closure will release it after all
* other handlers have run.
*/
g_object_ref (transition);
remove_transition (adjustment, clos->name);
}
/**
* st_adjustment_get_transition:
* Returns: (transfer none) (nullable):
*/
ClutterTransition *
st_adjustment_get_transition (StAdjustment *adjustment,
const char *name)
{
StAdjustmentPrivate *priv;
TransitionClosure *clos;
g_return_val_if_fail (ST_IS_ADJUSTMENT (adjustment), NULL);
priv = st_adjustment_get_instance_private (adjustment);
if (priv->transitions == NULL)
return NULL;
clos = g_hash_table_lookup (priv->transitions, name);
if (clos == NULL)
return NULL;
return clos->transition;
}
void
st_adjustment_add_transition (StAdjustment *adjustment,
const char *name,
ClutterTransition *transition)
{
StAdjustmentPrivate *priv;
TransitionClosure *clos;
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
g_return_if_fail (name != NULL);
g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
priv = st_adjustment_get_instance_private (adjustment);
if (priv->transitions == NULL)
priv->transitions = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL,
transition_closure_free);
if (g_hash_table_lookup (priv->transitions, name) != NULL)
{
g_warning ("A transition with name '%s' already exists for "
"adjustment '%p'", name, adjustment);
return;
}
clutter_transition_set_animatable (transition, CLUTTER_ANIMATABLE (adjustment));
clos = g_new (TransitionClosure, 1);
clos->adjustment = adjustment;
clos->transition = g_object_ref (transition);
clos->name = g_strdup (name);
clos->completed_id = g_signal_connect (transition, "stopped",
G_CALLBACK (on_transition_stopped),
clos);
g_hash_table_insert (priv->transitions, clos->name, clos);
clutter_timeline_start (CLUTTER_TIMELINE (transition));
}
void
st_adjustment_remove_transition (StAdjustment *adjustment,
const char *name)
{
StAdjustmentPrivate *priv;
TransitionClosure *clos;
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
g_return_if_fail (name != NULL);
priv = st_adjustment_get_instance_private (adjustment);
if (priv->transitions == NULL)
return;
clos = g_hash_table_lookup (priv->transitions, name);
if (clos == NULL)
return;
remove_transition (adjustment, name);
}

View File

@ -78,6 +78,14 @@ void st_adjustment_get_values (StAdjustment *adjustment,
void st_adjustment_adjust_for_scroll_event (StAdjustment *adjustment,
gdouble delta);
ClutterTransition * st_adjustment_get_transition (StAdjustment *adjustment,
const char *name);
void st_adjustment_add_transition (StAdjustment *adjustment,
const char *name,
ClutterTransition *transition);
void st_adjustment_remove_transition (StAdjustment *adjustment,
const char *name);
G_END_DECLS
#endif /* __ST_ADJUSTMENT_H__ */