interval: Add variadic arguments for initial/final setters

As a convenience for the C API.

Language bindings should already be using the GValue variants.

This commit also moves the custom progress functions out of the
clutter-interval.c, as they are meant to be generic interpolation
functions and not ClutterInterval-specific.
This commit is contained in:
Emmanuele Bassi 2012-03-15 11:02:30 +00:00
parent 06314e9ed8
commit beb91d7676
5 changed files with 322 additions and 171 deletions

View File

@ -63,14 +63,6 @@
#include "clutter-private.h"
#include "clutter-units.h"
typedef struct
{
GType value_type;
ClutterProgressFunc func;
} ProgressData;
static GHashTable *progress_funcs = NULL;
enum
{
PROP_0,
@ -195,23 +187,15 @@ clutter_interval_real_compute_value (ClutterInterval *interval,
value_type = clutter_interval_get_value_type (interval);
if (G_UNLIKELY (progress_funcs != NULL))
if (_clutter_has_progress_function (value_type))
{
ProgressData *p_data;
p_data =
g_hash_table_lookup (progress_funcs, GUINT_TO_POINTER (value_type));
/* if we have a progress function, and that function was
* successful in computing the progress, then we bail out
* as fast as we can
*/
if (p_data != NULL)
{
retval = p_data->func (initial, final, factor, value);
if (retval)
return retval;
}
retval = _clutter_run_progress_function (value_type,
initial,
final,
factor,
value);
if (retval)
return TRUE;
}
switch (G_TYPE_FUNDAMENTAL (value_type))
@ -413,16 +397,44 @@ clutter_interval_init (ClutterInterval *self)
priv->values = g_malloc0 (sizeof (GValue) * N_VALUES);
}
static void
clutter_interval_set_interval_valist (ClutterInterval *interval,
va_list var_args)
static inline void
clutter_interval_set_value_internal (ClutterInterval *interval,
gint index_,
const GValue *value)
{
ClutterIntervalPrivate *priv = interval->priv;
g_assert (index_ >= INITIAL && index_ <= RESULT);
if (G_IS_VALUE (&priv->values[index_]))
g_value_unset (&priv->values[index_]);
g_value_init (&priv->values[index_], priv->value_type);
g_value_copy (value, &priv->values[index_]);
}
static inline void
clutter_interval_get_value_internal (ClutterInterval *interval,
gint index_,
GValue *value)
{
ClutterIntervalPrivate *priv = interval->priv;
g_assert (index_ >= INITIAL && index_ <= RESULT);
g_value_copy (&priv->values[index_], value);
}
static gboolean
clutter_interval_set_initial_internal (ClutterInterval *interval,
va_list *args)
{
GType gtype = interval->priv->value_type;
GValue value = { 0, };
gchar *error;
/* initial value */
G_VALUE_COLLECT_INIT (&value, gtype, var_args, 0, &error);
G_VALUE_COLLECT_INIT (&value, gtype, *args, 0, &error);
if (error)
{
@ -433,26 +445,42 @@ clutter_interval_set_interval_valist (ClutterInterval *interval,
* undefined behaviour
*/
g_free (error);
return;
return FALSE;
}
clutter_interval_set_initial_value (interval, &value);
clutter_interval_set_value_internal (interval, INITIAL, &value);
g_value_unset (&value);
/* final value */
G_VALUE_COLLECT_INIT (&value, gtype, var_args, 0, &error);
return TRUE;
}
static gboolean
clutter_interval_set_final_internal (ClutterInterval *interval,
va_list *args)
{
GType gtype = interval->priv->value_type;
GValue value = { 0, };
gchar *error;
/* initial value */
G_VALUE_COLLECT_INIT (&value, gtype, *args, 0, &error);
if (error)
{
g_warning ("%s: %s", G_STRLOC, error);
/* see above */
/* we leak the value here as it might not be in a valid state
* given the error and calling g_value_unset() might lead to
* undefined behaviour
*/
g_free (error);
return;
return FALSE;
}
clutter_interval_set_final_value (interval, &value);
clutter_interval_set_value_internal (interval, FINAL, &value);
g_value_unset (&value);
return TRUE;
}
static void
@ -524,7 +552,13 @@ clutter_interval_new (GType gtype,
retval = g_object_new (CLUTTER_TYPE_INTERVAL, "value-type", gtype, NULL);
va_start (args, gtype);
clutter_interval_set_interval_valist (retval, args);
if (!clutter_interval_set_initial_internal (retval, &args))
goto out;
clutter_interval_set_final_internal (retval, &args);
out:
va_end (args);
return retval;
@ -616,34 +650,6 @@ clutter_interval_get_value_type (ClutterInterval *interval)
return interval->priv->value_type;
}
static inline void
clutter_interval_set_value_internal (ClutterInterval *interval,
gint index_,
const GValue *value)
{
ClutterIntervalPrivate *priv = interval->priv;
g_assert (index_ >= INITIAL && index_ <= RESULT);
if (G_IS_VALUE (&priv->values[index_]))
g_value_unset (&priv->values[index_]);
g_value_init (&priv->values[index_], priv->value_type);
g_value_copy (value, &priv->values[index_]);
}
static inline void
clutter_interval_get_value_internal (ClutterInterval *interval,
gint index_,
GValue *value)
{
ClutterIntervalPrivate *priv = interval->priv;
g_assert (index_ >= INITIAL && index_ <= RESULT);
g_value_copy (&priv->values[index_], value);
}
/**
* clutter_interval_set_initial_value:
* @interval: a #ClutterInterval
@ -652,6 +658,8 @@ clutter_interval_get_value_internal (ClutterInterval *interval,
* Sets the initial value of @interval to @value. The value is copied
* inside the #ClutterInterval.
*
* Rename to: clutter_interval_set_initial
*
* Since: 1.0
*/
void
@ -670,6 +678,33 @@ clutter_interval_set_initial_value (ClutterInterval *interval,
clutter_interval_set_value_internal (interval, INITIAL, value);
}
/**
* clutter_interval_set_initial: (skip)
* @interval: a #ClutterInterval
* @...: the initial value of the interval.
*
* Variadic arguments version of clutter_interval_set_initial_value().
*
* This function is meant as a convenience for the C API.
*
* Language bindings should use clutter_interval_set_initial_value()
* instead.
*
* Since: 1.10
*/
void
clutter_interval_set_initial (ClutterInterval *interval,
...)
{
va_list args;
g_return_if_fail (CLUTTER_IS_INTERVAL (interval));
va_start (args, interval);
clutter_interval_set_initial_internal (interval, &args);
va_end (args);
}
/**
* clutter_interval_get_initial_value:
* @interval: a #ClutterInterval
@ -721,6 +756,8 @@ clutter_interval_peek_initial_value (ClutterInterval *interval)
* Sets the final value of @interval to @value. The value is
* copied inside the #ClutterInterval.
*
* Rename to: clutter_interval_set_final
*
* Since: 1.0
*/
void
@ -762,6 +799,32 @@ clutter_interval_get_final_value (ClutterInterval *interval,
clutter_interval_get_value_internal (interval, FINAL, value);
}
/**
* clutter_interval_set_final: (skip)
* @interval: a #ClutterInterval
* @...: the final value of the interval
*
* Variadic arguments version of clutter_interval_set_final_value().
*
* This function is meant as a convenience for the C API.
*
* Language bindings should use clutter_interval_set_final_value() instead.
*
* Since: 1.10
*/
void
clutter_interval_set_final (ClutterInterval *interval,
...)
{
va_list args;
g_return_if_fail (CLUTTER_IS_INTERVAL (interval));
va_start (args, interval);
clutter_interval_set_final_internal (interval, &args);
va_end (args);
}
/**
* clutter_interval_peek_final_value:
* @interval: a #ClutterInterval
@ -812,7 +875,13 @@ clutter_interval_set_interval (ClutterInterval *interval,
g_return_if_fail (interval->priv->value_type != G_TYPE_INVALID);
va_start (args, interval);
clutter_interval_set_interval_valist (interval, args);
if (!clutter_interval_set_initial_internal (interval, &args))
goto out;
clutter_interval_set_final_internal (interval, &args);
out:
va_end (args);
}
@ -941,81 +1010,3 @@ clutter_interval_compute (ClutterInterval *interval,
return NULL;
}
/**
* clutter_interval_register_progress_func: (skip)
* @value_type: a #GType
* @func: a #ClutterProgressFunc, or %NULL to unset a previously
* set progress function
*
* Sets the progress function for a given @value_type, like:
*
* |[
* clutter_interval_register_progress_func (MY_TYPE_FOO,
* my_foo_progress);
* ]|
*
* Whenever a #ClutterInterval instance using the default
* #ClutterInterval::compute_value implementation is set as an
* interval between two #GValue of type @value_type, it will call
* @func to establish the value depending on the given progress,
* for instance:
*
* |[
* static gboolean
* my_int_progress (const GValue *a,
* const GValue *b,
* gdouble progress,
* GValue *retval)
* {
* gint ia = g_value_get_int (a);
* gint ib = g_value_get_int (b);
* gint res = factor * (ib - ia) + ia;
*
* g_value_set_int (retval, res);
*
* return TRUE;
* }
*
* clutter_interval_register_progress_func (G_TYPE_INT, my_int_progress);
* ]|
*
* To unset a previously set progress function of a #GType, pass %NULL
* for @func.
*
* Since: 1.0
*/
void
clutter_interval_register_progress_func (GType value_type,
ClutterProgressFunc func)
{
ProgressData *progress_func;
g_return_if_fail (value_type != G_TYPE_INVALID);
if (G_UNLIKELY (progress_funcs == NULL))
progress_funcs = g_hash_table_new (NULL, NULL);
progress_func =
g_hash_table_lookup (progress_funcs, GUINT_TO_POINTER (value_type));
if (G_UNLIKELY (progress_func))
{
if (func == NULL)
{
g_hash_table_remove (progress_funcs, GUINT_TO_POINTER (value_type));
g_slice_free (ProgressData, progress_func);
}
else
progress_func->func = func;
}
else
{
progress_func = g_slice_new (ProgressData);
progress_func->value_type = value_type;
progress_func->func = func;
g_hash_table_replace (progress_funcs,
GUINT_TO_POINTER (value_type),
progress_func);
}
}

View File

@ -43,34 +43,6 @@ G_BEGIN_DECLS
typedef struct _ClutterIntervalPrivate ClutterIntervalPrivate;
typedef struct _ClutterIntervalClass ClutterIntervalClass;
/**
* ClutterProgressFunc:
* @a: the initial value of an interval
* @b: the final value of an interval
* @progress: the progress factor, between 0 and 1
* @retval: the value used to store the progress
*
* Prototype of the progress function used to compute the value
* between the two ends @a and @b of an interval depending on
* the value of @progress.
*
* The #GValue in @retval is already initialized with the same
* type as @a and @b.
*
* This function will be called by #ClutterInterval if the
* type of the values of the interval was registered using
* clutter_interval_register_progress_func().
*
* Return value: %TRUE if the function successfully computed
* the value and stored it inside @retval
*
* Since: 1.0
*/
typedef gboolean (* ClutterProgressFunc) (const GValue *a,
const GValue *b,
gdouble progress,
GValue *retval);
/**
* ClutterInterval:
*
@ -131,11 +103,16 @@ ClutterInterval *clutter_interval_new_with_values (GType gtype,
ClutterInterval *clutter_interval_clone (ClutterInterval *interval);
GType clutter_interval_get_value_type (ClutterInterval *interval);
void clutter_interval_set_initial (ClutterInterval *interval,
...);
void clutter_interval_set_initial_value (ClutterInterval *interval,
const GValue *value);
void clutter_interval_get_initial_value (ClutterInterval *interval,
GValue *value);
GValue * clutter_interval_peek_initial_value (ClutterInterval *interval);
void clutter_interval_set_final (ClutterInterval *interval,
...);
void clutter_interval_set_final_value (ClutterInterval *interval,
const GValue *value);
void clutter_interval_get_final_value (ClutterInterval *interval,

View File

@ -265,6 +265,13 @@ typedef enum _ClutterCullResult
CLUTTER_CULL_RESULT_PARTIAL
} ClutterCullResult;
gboolean _clutter_has_progress_function (GType gtype);
gboolean _clutter_run_progress_function (GType gtype,
const GValue *initial,
const GValue *final,
gdouble progress,
GValue *retval);
G_END_DECLS
#endif /* __CLUTTER_PRIVATE_H__ */

View File

@ -287,6 +287,34 @@ ClutterMargin * clutter_margin_new (void) G_GNUC_MALLOC;
ClutterMargin * clutter_margin_copy (const ClutterMargin *margin_);
void clutter_margin_free (ClutterMargin *margin_);
/**
* ClutterProgressFunc:
* @a: the initial value of an interval
* @b: the final value of an interval
* @progress: the progress factor, between 0 and 1
* @retval: the value used to store the progress
*
* Prototype of the progress function used to compute the value
* between the two ends @a and @b of an interval depending on
* the value of @progress.
*
* The #GValue in @retval is already initialized with the same
* type as @a and @b.
*
* This function will be called by #ClutterInterval if the
* type of the values of the interval was registered using
* clutter_interval_register_progress_func().
*
* Return value: %TRUE if the function successfully computed
* the value and stored it inside @retval
*
* Since: 1.0
*/
typedef gboolean (* ClutterProgressFunc) (const GValue *a,
const GValue *b,
gdouble progress,
GValue *retval);
G_END_DECLS
#endif /* __CLUTTER_TYPES_H__ */

View File

@ -37,6 +37,7 @@
#include <glib/gi18n-lib.h>
#include "clutter-main.h"
#include "clutter-interval.h"
#include "clutter-private.h"
#include "deprecated/clutter-util.h"
@ -180,3 +181,150 @@ _clutter_util_rectangle_union (const cairo_rectangle_int_t *src1,
dest->x = dest_x;
dest->y = dest_y;
}
typedef struct
{
GType value_type;
ClutterProgressFunc func;
} ProgressData;
G_LOCK_DEFINE_STATIC (progress_funcs);
static GHashTable *progress_funcs = NULL;
gboolean
_clutter_has_progress_function (GType gtype)
{
const char *type_name = g_type_name (gtype);
if (progress_funcs == NULL)
return FALSE;
return g_hash_table_lookup (progress_funcs, type_name) != NULL;
}
gboolean
_clutter_run_progress_function (GType gtype,
const GValue *initial,
const GValue *final,
gdouble progress,
GValue *retval)
{
ProgressData *pdata;
gboolean res;
G_LOCK (progress_funcs);
if (G_UNLIKELY (progress_funcs == NULL))
{
res = FALSE;
goto out;
}
pdata = g_hash_table_lookup (progress_funcs, g_type_name (gtype));
if (G_UNLIKELY (pdata == NULL))
{
res = FALSE;
goto out;
}
res = pdata->func (initial, final, progress, retval);
out:
G_UNLOCK (progress_funcs);
return res;
}
static void
progress_data_destroy (gpointer data_)
{
g_slice_free (ProgressData, data_);
}
/**
* clutter_interval_register_progress_func: (skip)
* @value_type: a #GType
* @func: a #ClutterProgressFunc, or %NULL to unset a previously
* set progress function
*
* Sets the progress function for a given @value_type, like:
*
* |[
* clutter_interval_register_progress_func (MY_TYPE_FOO,
* my_foo_progress);
* ]|
*
* Whenever a #ClutterInterval instance using the default
* #ClutterInterval::compute_value implementation is set as an
* interval between two #GValue of type @value_type, it will call
* @func to establish the value depending on the given progress,
* for instance:
*
* |[
* static gboolean
* my_int_progress (const GValue *a,
* const GValue *b,
* gdouble progress,
* GValue *retval)
* {
* gint ia = g_value_get_int (a);
* gint ib = g_value_get_int (b);
* gint res = factor * (ib - ia) + ia;
*
* g_value_set_int (retval, res);
*
* return TRUE;
* }
*
* clutter_interval_register_progress_func (G_TYPE_INT, my_int_progress);
* ]|
*
* To unset a previously set progress function of a #GType, pass %NULL
* for @func.
*
* Since: 1.0
*/
void
clutter_interval_register_progress_func (GType value_type,
ClutterProgressFunc func)
{
ProgressData *progress_func;
const char *type_name;
g_return_if_fail (value_type != G_TYPE_INVALID);
type_name = g_type_name (value_type);
G_LOCK (progress_funcs);
if (G_UNLIKELY (progress_funcs == NULL))
progress_funcs = g_hash_table_new_full (NULL, NULL,
NULL,
progress_data_destroy);
progress_func =
g_hash_table_lookup (progress_funcs, type_name);
if (G_UNLIKELY (progress_func))
{
if (func == NULL)
{
g_hash_table_remove (progress_funcs, type_name);
g_slice_free (ProgressData, progress_func);
}
else
progress_func->func = func;
}
else
{
progress_func = g_slice_new (ProgressData);
progress_func->value_type = value_type;
progress_func->func = func;
g_hash_table_replace (progress_funcs,
(gpointer) type_name,
progress_func);
}
G_UNLOCK (progress_funcs);
}