[units] Rework Units into logical distance value

Units as they have been implemented since Clutter 0.4 have always been
misdefined as "logical distance unit", while they were just pixels with
fractionary bits.

Units should be reworked to be opaque structures to hold a value and
its unit type, that can be then converted into pixels when Clutter needs
to paint or compute size requisitions and perform allocations.

The previous API should be completely removed to avoid collisions, and
a new type:

        ClutterUnits

should be added; the ability to install GObject properties using
ClutterUnits should be maintained.
This commit is contained in:
Emmanuele Bassi 2009-06-03 11:12:09 +01:00
parent 1580ffb884
commit 0d5e17ecd1
9 changed files with 714 additions and 593 deletions

2
.gitignore vendored
View File

@ -202,6 +202,8 @@ stamp-h1
/tests/conform/test-blend-strings
/tests/conform/test-color-from-string
/tests/conform/test-color-to-string
/tests/conform/test-units-constructors
/tests/conform/test-units-string
/tests/conform/test-conformance-result.xml
/tests/micro-bench/test-text-perf
/tests/micro-bench/test-text

View File

@ -2903,7 +2903,7 @@ clutter_actor_dispose (GObject *object)
object->ref_count);
/* avoid recursing when called from clutter_actor_destroy() */
if (priv->parent_actor)
if (priv->parent_actor != NULL)
{
ClutterActor *parent = priv->parent_actor;
@ -7147,92 +7147,30 @@ parse_units (ClutterActor *self,
if (G_VALUE_HOLDS (&value, G_TYPE_INT))
{
retval = g_value_get_int (&value);
retval = (gfloat) g_value_get_int (&value);
}
else if (G_VALUE_HOLDS (&value, G_TYPE_FLOAT))
{
retval = g_value_get_float (&value);
}
else if (G_VALUE_HOLDS (&value, G_TYPE_STRING))
{
gint64 val;
gchar *end;
ClutterUnits units;
gboolean res;
val = g_ascii_strtoll (g_value_get_string (&value), &end, 10);
/* skip whitespace */
while (g_ascii_isspace (*end))
end++;
/* assume pixels */
if (*end == '\0')
{
retval = val;
goto out;
}
if (strcmp (end, "px") == 0)
{
retval = val;
goto out;
}
if (strcmp (end, "em") == 0)
{
retval = CLUTTER_UNITS_FROM_EM (val);
goto out;
}
if (strcmp (end, "mm") == 0)
{
retval = CLUTTER_UNITS_FROM_MM (val);
goto out;
}
if (strcmp (end, "pt") == 0)
{
retval = CLUTTER_UNITS_FROM_POINTS (val);
goto out;
}
if (end[0] == '%' && end[1] == '\0')
{
ClutterActor *stage;
if (CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_IS_TOPLEVEL)
{
g_warning ("Unable to set percentage of %s on a top-level "
"actor of type '%s'",
(dimension == PARSE_X ||
dimension == PARSE_WIDTH ||
dimension == PARSE_ANCHOR_X) ? "width" : "height",
g_type_name (G_OBJECT_TYPE (self)));
retval = 0;
goto out;
}
stage = clutter_actor_get_stage (self);
if (stage == NULL)
stage = clutter_stage_get_default ();
if (dimension == PARSE_X ||
dimension == PARSE_WIDTH ||
dimension == PARSE_ANCHOR_X)
{
retval = clutter_actor_get_width (stage) * val;
}
res = clutter_units_from_string (&units, g_value_get_string (&value));
if (res)
retval = clutter_units_to_pixels (&units);
else
{
retval = clutter_actor_get_height (stage) * val;
}
goto out;
}
g_warning ("Invalid value '%s': integers, strings or floating point "
"values can be used for the x, y, width and height "
"properties. Valid modifiers for strings are 'px', 'mm' "
"and '%%'.",
"properties. Valid modifiers for strings are 'px', 'mm', "
"'pt' and 'em'.",
g_value_get_string (&value));
retval = 0;
}
}
else if (G_VALUE_HOLDS (&value, G_TYPE_DOUBLE))
{
ClutterActor *stage;
@ -7505,7 +7443,18 @@ clutter_actor_set_custom_property (ClutterScriptable *scriptable,
const gchar *name,
const GValue *value)
{
CLUTTER_NOTE (SCRIPT, "in ClutterActor::set_custom_property('%s')", name);
#ifdef CLUTTER_ENABLE_DEBUG
{
gchar *tmp = g_strdup_value_contents (value);
CLUTTER_NOTE (SCRIPT,
"in ClutterActor::set_custom_property('%s') = %s",
name,
tmp);
g_free (tmp);
}
#endif
if (strcmp (name, "rotation") == 0)
{

View File

@ -27,68 +27,39 @@
/**
* SECTION:clutter-units
* @short_description: A logical distance unit.
* @short_description: A logical distance unit
*
* Clutter units are logical units with granularity greater than that of the
* device units; they are used by #ClutterActorBox and the units-based family
* of #ClutterActor functions. To convert between Clutter units and device
* units, use %CLUTTER_UNITS_FROM_DEVICE and %CLUTTER_UNITS_TO_DEVICE macros.
* #ClutterUnits is a structure holding a logical distance value along with
* its type, expressed as a value of the #ClutterUnitType enumeration. It is
* possible to use #ClutterUnits to store a position or a size in units
* different than pixels, and convert them whenever needed (for instance
* inside the #ClutterActor::allocate() virtual function, or inside the
* #ClutterActor::get_preferred_width() and #ClutterActor::get_preferred_height()
* virtual functions.
*
* #ClutterUnit<!-- -->s can be converted from other units like millimeters,
* typographic points (at the current resolution) and percentages. It is
* also possible to convert fixed point values to and from #ClutterUnit
* values.
*
* In order to register a #ClutterUnit property, the #ClutterParamSpecUnit
* In order to register a #ClutterUnits property, the #ClutterParamSpecUnits
* #GParamSpec sub-class should be used:
*
* |[
* GParamSpec *pspec;
*
* pspec = clutter_param_spec_unit ("width",
* pspec = clutter_param_spec_units ("active-width",
* "Width",
* "Width of the actor, in units",
* 0, CLUTTER_MAXUNIT,
* 0,
* "Width of the active area, in millimeters",
* CLUTTER_UNIT_MM,
* 0.0, 12.0,
* 12.0,
* G_PARAM_READWRITE);
* g_object_class_install_property (gobject_class, PROP_WIDTH, pspec);
* ]|
*
* A #GValue holding units can be manipulated using clutter_value_set_unit()
* and clutter_value_get_unit(). #GValue<!-- -->s containing a #ClutterUnit
* value can also be transformed to #GValue<!-- -->s containing integer
* values - with a loss of precision:
* A #GValue holding units can be manipulated using clutter_value_set_units()
* and clutter_value_get_units(). #GValue<!-- -->s containing a #ClutterUnits
* value can also be transformed to #GValue<!-- -->s initialized with
* %G_TYPE_INT, %G_TYPE_FLOAT and %G_TYPE_STRING through implicit conversion
* and using g_value_transform().
*
* |[
* static gboolean
* units_to_int (const GValue *src,
* GValue *dest)
* {
* g_return_val_if_fail (CLUTTER_VALUE_HOLDS_UNIT (src), FALSE);
*
* g_value_init (dest, G_TYPE_INT);
* return g_value_transform (src, &dest);
* }
* ]|
*
* The code above is equivalent to:
*
* |[
* static gboolean
* units_to_int (const GValue *src,
* GValue *dest)
* {
* g_return_val_if_fail (CLUTTER_VALUE_HOLDS_UNIT (src), FALSE);
*
* g_value_init (dest, G_TYPE_INT);
* g_value_set_int (dest,
* CLUTTER_UNITS_TO_INT (clutter_value_get_unit (src)));
*
* return TRUE;
* }
* ]|
*
* #ClutterUnit is available since Clutter 0.4
* #ClutterUnits is available since Clutter 1.0
*/
#ifdef HAVE_CONFIG_H
@ -105,19 +76,8 @@
#define FLOAT_EPSILON (1e-30)
/**
* clutter_units_mm:
* @mm: millimeters to convert
*
* Converts a value in millimeters to #ClutterUnit<!-- -->s at
* the current DPI.
*
* Return value: the value in units
*
* Since: 1.0
*/
ClutterUnit
clutter_units_mm (gdouble mm)
static gfloat
units_mm_to_pixels (gfloat mm)
{
ClutterBackend *backend;
gdouble dpi;
@ -130,19 +90,8 @@ clutter_units_mm (gdouble mm)
return mm * dpi / 25.4;
}
/**
* clutter_units_pt:
* @pt: typographic points to convert
*
* Converts a value in typographic points to #ClutterUnit<!-- -->s
* at the current DPI.
*
* Return value: the value in units
*
* Since: 1.0
*/
ClutterUnit
clutter_units_pt (gdouble pt)
static gfloat
units_pt_to_pixels (gfloat pt)
{
ClutterBackend *backend;
gdouble dpi;
@ -155,44 +104,9 @@ clutter_units_pt (gdouble pt)
return pt * dpi / 72.0;
}
/**
* clutter_units_em:
* @em: em to convert
*
* Converts a value in em to #ClutterUnit<!-- -->s at the
* current DPI
*
* Return value: the value in units
*
* Since: 1.0
*/
ClutterUnit
clutter_units_em (gdouble em)
{
ClutterBackend *backend = clutter_get_default_backend ();
return em * _clutter_backend_get_units_per_em (backend, NULL);
}
/**
* clutter_units_em_for_font:
* @font_name: the font name and size
* @em: em to convert
*
* Converts a value in em to #ClutterUnit<!-- -->s at the
* current DPI for the given font name.
*
* The @font_name string must be in a format that
* pango_font_description_from_string() can parse, like
* for clutter_text_set_font_name() or clutter_backend_set_font_name().
*
* Return value: the value in units
*
* Since: 1.0
*/
ClutterUnit
clutter_units_em_for_font (const gchar *font_name,
gdouble em)
static gfloat
units_em_to_pixels (const gchar *font_name,
gfloat em)
{
ClutterBackend *backend = clutter_get_default_backend ();
@ -218,19 +132,185 @@ clutter_units_em_for_font (const gchar *font_name,
}
/**
* clutter_units_pixels:
* @px: pixels to convert
* clutter_units_mm:
* @units: a #ClutterUnits
* @mm: millimeters
*
* Converts a value in pixels to #ClutterUnit<!-- -->s
*
* Return value: the value in units
* Stores a value in millimiters inside @units
*
* Since: 1.0
*/
ClutterUnit
clutter_units_pixels (gint px)
void
clutter_units_mm (ClutterUnits *units,
gfloat mm)
{
return CLUTTER_UNITS_FROM_INT (px);
g_return_if_fail (units != NULL);
units->unit_type = CLUTTER_UNIT_MM;
units->value = mm;
units->pixels = units_mm_to_pixels (mm);
units->pixels_set = TRUE;
}
/**
* clutter_units_pt:
* @units: a #ClutterUnits
* @pt: typographic points
*
* Stores a value in typographic points inside @units
*
* Since: 1.0
*/
void
clutter_units_pt (ClutterUnits *units,
gfloat pt)
{
g_return_if_fail (units != NULL);
units->unit_type = CLUTTER_UNIT_POINT;
units->value = pt;
units->pixels = units_pt_to_pixels (pt);
units->pixels_set = TRUE;
}
/**
* clutter_units_em:
* @units: a #ClutterUnits
* @em: em
*
* Stores a value in em inside @units, using the default font
* name as returned by clutter_backend_get_font_name()
*
* Since: 1.0
*/
void
clutter_units_em (ClutterUnits *units,
gfloat em)
{
g_return_if_fail (units != NULL);
units->unit_type = CLUTTER_UNIT_EM;
units->value = em;
units->pixels = units_em_to_pixels (NULL, em);
units->pixels_set = TRUE;
}
/**
* clutter_units_em_for_font:
* @units: a #ClutterUnits
* @font_name: the font name and size
* @em: em
*
* Stores a value in em inside @units using @font_name
*
* Since: 1.0
*/
void
clutter_units_em_for_font (ClutterUnits *units,
const gchar *font_name,
gfloat em)
{
g_return_if_fail (units != NULL);
units->unit_type = CLUTTER_UNIT_EM;
units->value = em;
units->pixels = units_em_to_pixels (font_name, em);
units->pixels_set = TRUE;
}
/**
* clutter_units_pixels:
* @units: a #ClutterUnits
* @px: pixels
*
* Stores a value in pixels inside @units
*
* Since: 1.0
*/
void
clutter_units_pixels (ClutterUnits *units,
gint px)
{
g_return_if_fail (units != NULL);
units->unit_type = CLUTTER_UNIT_PIXEL;
units->value = px;
units->pixels = px;
units->pixels_set = TRUE;
}
/**
* clutter_units_get_unit_type:
* @units: a #ClutterUnits
*
* Retrieves the unit type of the value stored inside @units
*
* Return value: a unit type
*
* Since: 1.0
*/
ClutterUnitType
clutter_units_get_unit_type (const ClutterUnits *units)
{
g_return_val_if_fail (units != NULL, CLUTTER_UNIT_PIXEL);
return units->unit_type;
}
/**
* clutter_units_get_unit_value:
* @units: a #ClutterUnits
*
* Retrieves the value stored inside @units
*
* Return value: the value stored inside a #ClutterUnits
*
* Since: 1.0
*/
gfloat
clutter_units_get_unit_value (const ClutterUnits *units)
{
g_return_val_if_fail (units != NULL, 0.0);
return units->value;
}
/**
* clutter_units_copy:
* @units: the #ClutterUnits to copy
*
* Copies @units
*
* Return value: the newly created copy of a #ClutterUnits structure.
* Use clutter_units_free() to free the allocated resources
*
* Since: 1.0
*/
ClutterUnits *
clutter_units_copy (const ClutterUnits *units)
{
if (units != NULL)
return g_slice_dup (ClutterUnits, units);
return NULL;
}
/**
* clutter_units_free:
* @units: the #ClutterUnits to free
*
* Frees the resources allocated by @units
*
* You should only call this function on a #ClutterUnits
* created using clutter_units_copy()
*
* Since: 1.0
*/
void
clutter_units_free (ClutterUnits *units)
{
if (units != NULL)
g_slice_free (ClutterUnits, units);
}
/**
@ -243,156 +323,271 @@ clutter_units_pixels (gint px)
*
* Since: 1.0
*/
gint
clutter_units_to_pixels (ClutterUnit units)
gfloat
clutter_units_to_pixels (ClutterUnits *units)
{
return CLUTTER_UNITS_TO_INT (units);
g_return_val_if_fail (units != NULL, 0.0);
if (units->pixels_set)
return units->pixels;
switch (units->unit_type)
{
case CLUTTER_UNIT_MM:
units->pixels = units_mm_to_pixels (units->value);
break;
case CLUTTER_UNIT_POINT:
units->pixels = units_pt_to_pixels (units->value);
break;
case CLUTTER_UNIT_EM:
units->pixels = units_em_to_pixels (NULL, units->value);
break;
case CLUTTER_UNIT_PIXEL:
units->pixels = units->value;
break;
}
units->pixels_set = TRUE;
return units->pixels;
}
/**
* clutter_units_from_string:
* @units: a #ClutterUnits
* @str: the string to convert
*
* Parses a value and updates @units with it
*
* A #ClutterUnits expressed in string should match:
*
* |[
* number: [0-9]
* unit_value: <numbers>+
* unit_name: px|pt|mm|em
* units: <unit_value> <unit_name>
* ]|
*
* For instance, these are valid strings:
*
* |[
* 10 px
* 5 em
* 24 pt
* 12.6 mm
* ]|
*
* Return value: %TRUE if the string was successfully parsed
*
* Since: 1.0
*/
gboolean
clutter_units_from_string (ClutterUnits *units,
const gchar *str)
{
ClutterUnitType unit_type;
gfloat value;
g_return_val_if_fail (units != NULL, FALSE);
g_return_val_if_fail (str != NULL, FALSE);
/* Ensure that the first character is a digit */
while (g_ascii_isspace (*str))
str++;
if (*str == '\0')
return FALSE;
if (!g_ascii_isdigit (*str))
return FALSE;
/* integer part */
value = (gfloat) strtoul (str, (char **) &str, 10);
if (*str == '.' || *str == ',')
{
glong frac = 100000;
while (g_ascii_isdigit (*++str))
{
frac += (*str - '0') * frac;
frac /= 10;
}
value += (1.0f / (gfloat) frac);
}
/* assume pixels by default, if no unit is specified */
if (str == '\0')
unit_type = CLUTTER_UNIT_PIXEL;
else
{
while (g_ascii_isspace (*str))
str++;
if (strncmp (str, "em", 2) == 0)
unit_type = CLUTTER_UNIT_EM;
else if (strncmp (str, "mm", 2) == 0)
unit_type = CLUTTER_UNIT_MM;
else if (strncmp (str, "pt", 2) == 0)
unit_type = CLUTTER_UNIT_POINT;
else if (strncmp (str, "px", 2) == 0)
unit_type = CLUTTER_UNIT_PIXEL;
else
return FALSE;
}
units->unit_type = unit_type;
units->value = value;
units->pixels_set = FALSE;
return TRUE;
}
/**
* clutter_units_to_string:
* @units: a #ClutterUnits
*
* Converts @units into a string
*
* See clutter_units_from_string() for the units syntax and for
* examples of outputs
*
* Return value: a newly allocated string containing the encoded
* #ClutterUnits value. Use g_free() to free the string
*
* Since: 1.0
*/
gchar *
clutter_units_to_string (const ClutterUnits *units)
{
const gchar *unit_name;
gchar *fmt;
g_return_val_if_fail (units != NULL, NULL);
switch (units->unit_type)
{
case CLUTTER_UNIT_MM:
unit_name = "mm";
fmt = g_strdup_printf ("%.2f", units->value);
break;
case CLUTTER_UNIT_POINT:
unit_name = "pt";
fmt = g_strdup_printf ("%.1f", units->value);
break;
case CLUTTER_UNIT_EM:
unit_name = "em";
fmt = g_strdup_printf ("%.2f", units->value);
break;
case CLUTTER_UNIT_PIXEL:
unit_name = "px";
fmt = g_strdup_printf ("%d", (int) units->value);
break;
}
return g_strconcat (fmt, " ", unit_name, NULL);
}
/*
* GValue and GParamSpec integration
*/
static GTypeInfo _info = {
0,
NULL,
NULL,
NULL,
NULL,
NULL,
0,
0,
NULL,
NULL,
};
static GTypeFundamentalInfo _finfo = { 0, };
/* units to integer */
static void
clutter_value_init_unit (GValue *value)
{
value->data[0].v_float = 0.0;
}
static void
clutter_value_copy_unit (const GValue *src,
clutter_value_transform_units_int (const GValue *src,
GValue *dest)
{
dest->data[0].v_float = src->data[0].v_float;
}
static gchar *
clutter_value_collect_unit (GValue *value,
guint n_collect_values,
GTypeCValue *collect_values,
guint collect_flags)
{
value->data[0].v_float = collect_values[0].v_double;
return NULL;
}
static gchar *
clutter_value_lcopy_unit (const GValue *value,
guint n_collect_values,
GTypeCValue *collect_values,
guint collect_flags)
{
gfloat *units_p = collect_values[0].v_pointer;
if (!units_p)
return g_strdup_printf ("value location for '%s' passed as NULL",
G_VALUE_TYPE_NAME (value));
*units_p = value->data[0].v_float;
return NULL;
dest->data[0].v_int = clutter_units_to_pixels (src->data[0].v_pointer);
}
/* integer to units */
static void
clutter_value_transform_unit_int (const GValue *src,
clutter_value_transform_int_units (const GValue *src,
GValue *dest)
{
dest->data[0].v_int = CLUTTER_UNITS_TO_INT (src->data[0].v_float);
clutter_units_pixels (dest->data[0].v_pointer, src->data[0].v_int);
}
/* units to float */
static void
clutter_value_transform_int_unit (const GValue *src,
clutter_value_transform_units_float (const GValue *src,
GValue *dest)
{
dest->data[0].v_float = CLUTTER_UNITS_FROM_INT (src->data[0].v_int);
dest->data[0].v_float = clutter_units_to_pixels (src->data[0].v_pointer);
}
/* float to units */
static void
clutter_value_transform_unit_float (const GValue *src,
clutter_value_transform_float_units (const GValue *src,
GValue *dest)
{
dest->data[0].v_float = CLUTTER_UNITS_TO_FLOAT (src->data[0].v_float);
clutter_units_pixels (dest->data[0].v_pointer, src->data[0].v_float);
}
/* units to string */
static void
clutter_value_transform_float_unit (const GValue *src,
clutter_value_transform_units_string (const GValue *src,
GValue *dest)
{
dest->data[0].v_float = CLUTTER_UNITS_FROM_FLOAT (src->data[0].v_float);
gchar *string = clutter_units_to_string (src->data[0].v_pointer);
g_value_take_string (dest, string);
}
#if 0
/* string to units */
static void
clutter_value_transform_unit_fixed (const GValue *src,
clutter_value_transform_string_units (const GValue *src,
GValue *dest)
{
dest->data[0].v_int = CLUTTER_UNITS_TO_FIXED (src->data[0].v_float);
}
ClutterUnits units = { CLUTTER_UNIT_PIXEL, 0.0f };
static void
clutter_value_transform_fixed_unit (const GValue *src,
GValue *dest)
{
dest->data[0].v_float = CLUTTER_UNITS_FROM_FIXED (src->data[0].v_int);
}
#endif
clutter_units_from_string (&units, g_value_get_string (src));
static const GTypeValueTable _clutter_unit_value_table = {
clutter_value_init_unit,
NULL,
clutter_value_copy_unit,
NULL,
"d",
clutter_value_collect_unit,
"p",
clutter_value_lcopy_unit
};
clutter_value_set_units (dest, &units);
}
GType
clutter_unit_get_type (void)
clutter_units_get_type (void)
{
static GType _clutter_unit_type = 0;
static volatile gsize clutter_units_type__volatile = 0;
if (G_UNLIKELY (_clutter_unit_type == 0))
if (g_once_init_enter (&clutter_units_type__volatile))
{
_info.value_table = & _clutter_unit_value_table;
_clutter_unit_type =
g_type_register_fundamental (g_type_fundamental_next (),
I_("ClutterUnit"),
&_info, &_finfo, 0);
GType clutter_units_type =
g_boxed_type_register_static (I_("ClutterUnits"),
(GBoxedCopyFunc) clutter_units_copy,
(GBoxedFreeFunc) clutter_units_free);
g_value_register_transform_func (_clutter_unit_type, G_TYPE_INT,
clutter_value_transform_unit_int);
g_value_register_transform_func (G_TYPE_INT, _clutter_unit_type,
clutter_value_transform_int_unit);
g_value_register_transform_func (clutter_units_type, G_TYPE_INT,
clutter_value_transform_units_int);
g_value_register_transform_func (G_TYPE_INT, clutter_units_type,
clutter_value_transform_int_units);
g_value_register_transform_func (_clutter_unit_type, G_TYPE_FLOAT,
clutter_value_transform_unit_float);
g_value_register_transform_func (G_TYPE_FLOAT, _clutter_unit_type,
clutter_value_transform_float_unit);
g_value_register_transform_func (clutter_units_type, G_TYPE_FLOAT,
clutter_value_transform_units_float);
g_value_register_transform_func (G_TYPE_FLOAT, clutter_units_type,
clutter_value_transform_float_units);
g_value_register_transform_func (clutter_units_type, G_TYPE_STRING,
clutter_value_transform_units_string);
g_value_register_transform_func (G_TYPE_STRING, clutter_units_type,
clutter_value_transform_string_units);
g_once_init_leave (&clutter_units_type__volatile, clutter_units_type);
}
return _clutter_unit_type;
return clutter_units_type__volatile;
}
/**
* clutter_value_set_unit:
* clutter_value_set_units:
* @value: a #GValue initialized to #CLUTTER_TYPE_UNIT
* @units: the units to set
*
@ -401,16 +596,16 @@ clutter_unit_get_type (void)
* Since: 0.8
*/
void
clutter_value_set_unit (GValue *value,
ClutterUnit units)
clutter_value_set_units (GValue *value,
const ClutterUnits *units)
{
g_return_if_fail (CLUTTER_VALUE_HOLDS_UNIT (value));
g_return_if_fail (CLUTTER_VALUE_HOLDS_UNITS (value));
value->data[0].v_float = units;
value->data[0].v_pointer = clutter_units_copy (units);
}
/**
* clutter_value_get_unit:
* clutter_value_get_units:
* @value: a #GValue initialized to #CLUTTER_TYPE_UNIT
*
* Gets the #ClutterUnit<!-- -->s contained in @value.
@ -419,76 +614,98 @@ clutter_value_set_unit (GValue *value,
*
* Since: 0.8
*/
ClutterUnit
G_CONST_RETURN ClutterUnits *
clutter_value_get_unit (const GValue *value)
{
g_return_val_if_fail (CLUTTER_VALUE_HOLDS_UNIT (value), 0);
g_return_val_if_fail (CLUTTER_VALUE_HOLDS_UNITS (value), NULL);
return value->data[0].v_float;
return value->data[0].v_pointer;
}
static void
param_unit_init (GParamSpec *pspec)
param_units_init (GParamSpec *pspec)
{
ClutterParamSpecUnit *uspec = CLUTTER_PARAM_SPEC_UNIT (pspec);
ClutterParamSpecUnits *uspec = CLUTTER_PARAM_SPEC_UNITS (pspec);
uspec->minimum = CLUTTER_MINUNIT;
uspec->maximum = CLUTTER_MAXUNIT;
uspec->default_value = 0;
uspec->minimum = -G_MAXFLOAT;
uspec->maximum = G_MAXFLOAT;
uspec->default_value = 0.0f;
uspec->default_type = CLUTTER_UNIT_PIXEL;
}
static void
param_unit_set_default (GParamSpec *pspec,
param_units_set_default (GParamSpec *pspec,
GValue *value)
{
value->data[0].v_float = CLUTTER_PARAM_SPEC_UNIT (pspec)->default_value;
ClutterParamSpecUnits *uspec = CLUTTER_PARAM_SPEC_UNITS (pspec);
ClutterUnits units;
units.unit_type = uspec->default_type;
units.value = uspec->default_value;
units.pixels_set = FALSE;
clutter_value_set_units (value, &units);
}
static gboolean
param_unit_validate (GParamSpec *pspec,
param_units_validate (GParamSpec *pspec,
GValue *value)
{
ClutterParamSpecUnit *uspec = CLUTTER_PARAM_SPEC_UNIT (pspec);
gfloat oval = value->data[0].v_float;
ClutterParamSpecUnits *uspec = CLUTTER_PARAM_SPEC_UNITS (pspec);
ClutterUnits *units = value->data[0].v_pointer;
gfloat oval = units->value;
g_assert (CLUTTER_IS_PARAM_SPEC_UNIT (pspec));
g_assert (CLUTTER_IS_PARAM_SPEC_UNITS (pspec));
value->data[0].v_float = CLAMP (value->data[0].v_float,
units->value = CLAMP (units->value,
uspec->minimum,
uspec->maximum);
return value->data[0].v_float != oval;
return units->value != oval;
}
static gint
param_unit_values_cmp (GParamSpec *pspec,
param_units_values_cmp (GParamSpec *pspec,
const GValue *value1,
const GValue *value2)
{
gfloat epsilon = FLOAT_EPSILON;
ClutterUnits *units1 = value1->data[0].v_pointer;
ClutterUnits *units2 = value2->data[0].v_pointer;
gfloat v1, v2;
if (value1->data[0].v_float < value2->data[0].v_float)
return - (value2->data[0].v_float - value1->data[0].v_float > epsilon);
if (units1->unit_type == units2->unit_type)
{
v1 = units1->value;
v2 = units2->value;
}
else
return value1->data[0].v_float - value2->data[0].v_float > epsilon;
{
v1 = clutter_units_to_pixels (units1);
v2 = clutter_units_to_pixels (units2);
}
if (v1 < v2)
return - (v2 - v1 > FLOAT_EPSILON);
else
return v1 - v2 > FLOAT_EPSILON;
}
GType
clutter_param_unit_get_type (void)
clutter_param_units_get_type (void)
{
static GType pspec_type = 0;
if (G_UNLIKELY (pspec_type == 0))
{
const GParamSpecTypeInfo pspec_info = {
sizeof (ClutterParamSpecUnit),
sizeof (ClutterParamSpecUnits),
16,
param_unit_init,
CLUTTER_TYPE_UNIT,
param_units_init,
CLUTTER_TYPE_UNITS,
NULL,
param_unit_set_default,
param_unit_validate,
param_unit_values_cmp,
param_units_set_default,
param_units_validate,
param_units_values_cmp,
};
pspec_type = g_param_type_register_static (I_("ClutterParamSpecUnit"),
@ -499,38 +716,42 @@ clutter_param_unit_get_type (void)
}
/**
* clutter_param_spec_unit:
* clutter_param_spec_units:
* @name: name of the property
* @nick: short name
* @blurb: description (can be translatable)
* @default_type: the default type for the #ClutterUnits
* @minimum: lower boundary
* @maximum: higher boundary
* @default_value: default value
* @flags: flags for the param spec
*
* Creates a #GParamSpec for properties using #ClutterUnit<!-- -->s.
* Creates a #GParamSpec for properties using #ClutterUnits.
*
* Return value: the newly created #GParamSpec
*
* Since: 0.8
* Since: 1.0
*/
GParamSpec *
clutter_param_spec_unit (const gchar *name,
clutter_param_spec_units (const gchar *name,
const gchar *nick,
const gchar *blurb,
ClutterUnit minimum,
ClutterUnit maximum,
ClutterUnit default_value,
ClutterUnitType default_type,
gfloat minimum,
gfloat maximum,
gfloat default_value,
GParamFlags flags)
{
ClutterParamSpecUnit *uspec;
ClutterParamSpecUnits *uspec;
g_return_val_if_fail (default_value >= minimum && default_value <= maximum,
NULL);
uspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_UNIT,
uspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_UNITS,
name, nick, blurb,
flags);
uspec->default_type = default_type;
uspec->minimum = minimum;
uspec->maximum = maximum;
uspec->default_value = default_value;

View File

@ -25,7 +25,7 @@
*/
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <clutter/clutter.h> can be included directly.h"
#error "Only <clutter/clutter.h> can be included directly."
#endif
#ifndef __CLUTTER_UNITS_H__
@ -38,241 +38,132 @@
G_BEGIN_DECLS
/**
* ClutterUnit:
* ClutterUnitType:
* @CLUTTER_UNIT_PIXEL: Unit expressed in pixels (with subpixel precision)
* @CLUTTER_UNIT_EM: Unit expressed in em
* @CLUTTER_UNIT_MM: Unit expressed in millimeters
* @CLUTTER_UNIT_POINT: Unit expressed in points
*
* Device independent unit used by Clutter. The value held can be
* transformed into other units, likes pixels
* The type of unit in which a value is expressed
*
* Since: 0.4
*/
typedef float ClutterUnit;
/**
* CLUTTER_UNITS_FROM_INT:
* @x: integer value
*
* Converts @x from an integer value to #ClutterUnit<!-- -->s
*
* Since: 0.6
*/
#define CLUTTER_UNITS_FROM_INT(x) ((float)(x))
/**
* CLUTTER_UNITS_TO_INT:
* @x: value in #ClutterUnit<!-- -->s
*
* Converts @x from a #ClutterUnit value into an integer
*
* Since: 0.6
*/
#define CLUTTER_UNITS_TO_INT(x) ((int)(x))
/**
* CLUTTER_UNITS_FROM_FLOAT:
* @x: float value
*
* Converts @x from a floating point value to #ClutterUnit<!-- -->s
*
* Since: 0.6
*/
#define CLUTTER_UNITS_FROM_FLOAT(x) (x)
/**
* CLUTTER_UNITS_TO_FLOAT:
* @x: value in #ClutterUnit<!-- -->s
*
* Converts @x from a #ClutterUnit value into a float
*
* Since: 0.6
*/
#define CLUTTER_UNITS_TO_FLOAT(x) (x)
/**
* CLUTTER_UNITS_FROM_FIXED:
* @x: #CoglFixed value
*
* Converts @x from a fixed point value to #ClutterUnit<!-- -->s
*
* Since: 0.6
*/
#define CLUTTER_UNITS_FROM_FIXED(x) (COGL_FIXED_TO_FLOAT (x))
/**
* CLUTTER_UNITS_TO_FIXED:
* @x: value in #ClutterUnit<!-- -->s
*
* Converts @x from a #ClutterUnit value into a #CoglFixed
*
* Since: 0.6
*/
#define CLUTTER_UNITS_TO_FIXED(x) (COGL_FIXED_FROM_FLOAT (x))
/**
* CLUTTER_UNITS_FORMAT:
*
* Format string that should be used for scanning and printing units.
* It is a string literal, but it does not include the percent sign to
* allow precision and length modifiers between the percent sign and
* the format:
*
* |[
* g_print ("%" CLUTTER_UNITS_FORMAT, units);
* ]|
* This enumeration might be expanded at later date
*
* Since: 1.0
*/
#define CLUTTER_UNITS_FORMAT "f"
typedef enum {
CLUTTER_UNIT_PIXEL,
CLUTTER_UNIT_EM,
CLUTTER_UNIT_MM,
CLUTTER_UNIT_POINT
} ClutterUnitType;
/**
* CLUTTER_UNITS_FROM_DEVICE:
* @x: value in pixels
* ClutterUnits:
*
* Converts @x from pixels to #ClutterUnit<!-- -->s
*
* Since: 0.6
*/
#define CLUTTER_UNITS_FROM_DEVICE(x) (clutter_units_pixels ((x)))
/**
* CLUTTER_UNITS_TO_DEVICE:
* @x: value in #ClutterUnit<!-- -->s
*
* Converts @x from #ClutterUnit<!-- -->s to pixels
*
* Since: 0.6
*/
#define CLUTTER_UNITS_TO_DEVICE(x) (clutter_units_to_pixels ((x)))
/**
* CLUTTER_UNITS_FROM_PANGO_UNIT:
* @x: value in Pango units
*
* Converts a value in Pango units to #ClutterUnit<!-- -->s
*
* Since: 0.6
*/
#define CLUTTER_UNITS_FROM_PANGO_UNIT(x) ((float)((x) / 1024.0))
/**
* CLUTTER_UNITS_TO_PANGO_UNIT:
* @x: value in #ClutterUnit<!-- -->s
*
* Converts a value in #ClutterUnit<!-- -->s to Pango units
*
* Since: 0.6
*/
#define CLUTTER_UNITS_TO_PANGO_UNIT(x) ((int)((x) * 1024))
/**
* CLUTTER_UNITS_FROM_MM:
* @x: a value in millimeters
*
* Converts a value in millimeters into #ClutterUnit<!-- -->s
*
* Since: 0.6
*/
#define CLUTTER_UNITS_FROM_MM(x) (clutter_units_mm (x))
/**
* CLUTTER_UNITS_FROM_POINTS:
* @x: a value in typographic points
*
* Converts a value in typographic points into #ClutterUnit<!-- -->s
*
* Since: 0.6
*/
#define CLUTTER_UNITS_FROM_POINTS(x) (clutter_units_pt (x))
/**
* CLUTTER_UNITS_FROM_EM:
* @x: a value in em
*
* Converts a value in em into #ClutterUnit<!-- -->s
* An opaque structure, to be used to store sizing and positioning
* values along with their unit.
*
* Since: 1.0
*/
#define CLUTTER_UNITS_FROM_EM(x) (clutter_units_em (x))
typedef struct _ClutterUnits ClutterUnits;
ClutterUnit clutter_units_mm (gdouble mm);
ClutterUnit clutter_units_pt (gdouble pt);
ClutterUnit clutter_units_em (gdouble em);
ClutterUnit clutter_units_em_for_font (const gchar *font_name,
gdouble em);
ClutterUnit clutter_units_pixels (gint px);
struct _ClutterUnits
{
/*< private >*/
ClutterUnitType unit_type;
gint clutter_units_to_pixels (ClutterUnit units);
gfloat value;
#define CLUTTER_TYPE_UNIT (clutter_unit_get_type ())
#define CLUTTER_TYPE_PARAM_UNIT (clutter_param_unit_get_type ())
#define CLUTTER_PARAM_SPEC_UNIT(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), CLUTTER_TYPE_PARAM_UNIT, ClutterParamSpecUnit))
#define CLUTTER_IS_PARAM_SPEC_UNIT(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), CLUTTER_TYPE_PARAM_UNIT))
/* pre-filled by the provided constructors */
gfloat pixels;
guint pixels_set;
/**
* CLUTTER_MAXUNIT:
*
* Higher boundary for a #ClutterUnit
*
* Since: 0.8
*/
#define CLUTTER_MAXUNIT (G_MAXFLOAT)
/* padding for eventual expansion */
gint64 __padding_1;
gint64 __padding_2;
};
/**
* CLUTTER_MINUNIT:
*
* Lower boundary for a #ClutterUnit
*
* Since: 0.8
*/
#define CLUTTER_MINUNIT (-G_MAXFLOAT)
GType clutter_units_get_type (void) G_GNUC_CONST;
ClutterUnitType clutter_units_get_unit_type (const ClutterUnits *units);
gfloat clutter_units_get_unit_value (const ClutterUnits *units);
ClutterUnits * clutter_units_copy (const ClutterUnits *units);
void clutter_units_free (ClutterUnits *units);
void clutter_units_pixels (ClutterUnits *units,
gint px);
void clutter_units_em (ClutterUnits *units,
gfloat em);
void clutter_units_em_for_font (ClutterUnits *units,
const gchar *font_name,
gfloat em);
void clutter_units_mm (ClutterUnits *units,
gfloat mm);
void clutter_units_pt (ClutterUnits *units,
gfloat pt);
gfloat clutter_units_to_pixels (ClutterUnits *units);
gchar * clutter_units_to_string (const ClutterUnits *units);
gboolean clutter_units_from_string (ClutterUnits *units,
const gchar *str);
#define CLUTTER_TYPE_UNITS (clutter_units_get_type ())
#define CLUTTER_TYPE_PARAM_UNITS (clutter_param_units_get_type ())
#define CLUTTER_PARAM_SPEC_UNITS(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), CLUTTER_TYPE_PARAM_UNITS, ClutterParamSpecUnits))
#define CLUTTER_IS_PARAM_SPEC_UNITS(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), CLUTTER_TYPE_PARAM_UNITS))
/**
* CLUTTER_VALUE_HOLDS_UNIT:
* @x: a #GValue
*
* Evaluates to %TRUE if @x holds #ClutterUnit<!-- -->s.
* Evaluates to %TRUE if @x holds a #ClutterUnits value
*
* Since: 0.8
*/
#define CLUTTER_VALUE_HOLDS_UNIT(x) (G_VALUE_HOLDS ((x), CLUTTER_TYPE_UNIT))
#define CLUTTER_VALUE_HOLDS_UNITS(x) (G_VALUE_HOLDS ((x), CLUTTER_TYPE_UNITS))
typedef struct _ClutterParamSpecUnit ClutterParamSpecUnit;
typedef struct _ClutterParamSpecUnits ClutterParamSpecUnits;
/**
* ClutterParamSpecUnit:
* ClutterParamSpecUnits:
* @default_type: default type
* @default_value: default value
* @minimum: lower boundary
* @maximum: higher boundary
* @default_value: default value
*
* #GParamSpec subclass for unit based properties.
*
* Since: 0.8
* Since: 1.0
*/
struct _ClutterParamSpecUnit
struct _ClutterParamSpecUnits
{
/*< private >*/
GParamSpec parent_instance;
/*< public >*/
ClutterUnit minimum;
ClutterUnit maximum;
ClutterUnit default_value;
ClutterUnitType default_type;
gfloat default_value;
gfloat minimum;
gfloat maximum;
};
GType clutter_unit_get_type (void) G_GNUC_CONST;
GType clutter_param_unit_get_type (void) G_GNUC_CONST;
GType clutter_param_units_get_type (void) G_GNUC_CONST;
void clutter_value_set_unit (GValue *value,
ClutterUnit units);
ClutterUnit clutter_value_get_unit (const GValue *value);
GParamSpec *clutter_param_spec_unit (const gchar *name,
GParamSpec * clutter_param_spec_units (const gchar *name,
const gchar *nick,
const gchar *blurb,
ClutterUnit minimum,
ClutterUnit maximum,
ClutterUnit default_value,
ClutterUnitType default_type,
gfloat minimum,
gfloat maximum,
gfloat default_value,
GParamFlags flags);
void clutter_value_set_units (GValue *value,
const ClutterUnits *units);
G_CONST_RETURN ClutterUnits *clutter_value_get_units (const GValue *value);
G_END_DECLS
#endif /* __CLUTTER_UNITS_H__ */

View File

@ -27,45 +27,35 @@ clutter_media_get_type
<SECTION>
<FILE>clutter-units</FILE>
<TITLE>Unit conversion</TITLE>
ClutterUnit
CLUTTER_UNITS_FORMAT
CLUTTER_UNITS_FROM_FLOAT
CLUTTER_UNITS_TO_FLOAT
CLUTTER_UNITS_FROM_INT
CLUTTER_UNITS_TO_INT
<SUBSECTION>
CLUTTER_UNITS_FROM_DEVICE
CLUTTER_UNITS_TO_DEVICE
CLUTTER_UNITS_FROM_FIXED
CLUTTER_UNITS_TO_FIXED
CLUTTER_UNITS_FROM_PANGO_UNIT
CLUTTER_UNITS_TO_PANGO_UNIT
CLUTTER_UNITS_FROM_MM
CLUTTER_UNITS_FROM_POINTS
CLUTTER_UNITS_FROM_EM
ClutterUnitType
ClutterUnits
clutter_units_mm
clutter_units_pt
clutter_units_em
clutter_units_em_for_font
clutter_units_pixels
clutter_units_to_pixels
clutter_units_copy
clutter_units_free
clutter_units_get_unit_type
clutter_units_get_unit_value
clutter_units_from_string
clutter_units_to_string
<SUBSECTION>
CLUTTER_MAXUNIT
CLUTTER_MINUNIT
ClutterParamSpecUnit
clutter_param_spec_unit
CLUTTER_VALUE_HOLDS_UNIT
clutter_value_set_unit
clutter_value_get_unit
ClutterParamSpecUnits
clutter_param_spec_units
CLUTTER_VALUE_HOLDS_UNITS
clutter_value_set_units
clutter_value_get_units
<SUBSECTION Private>
CLUTTER_TYPE_UNIT
CLUTTER_TYPE_PARAM_UNIT
CLUTTER_PARAM_SPEC_UNIT
CLUTTER_IS_PARAM_SPEC_UNIT
clutter_unit_get_type
clutter_param_unit_get_type
CLUTTER_TYPE_UNITS
CLUTTER_TYPE_PARAM_UNITS
CLUTTER_PARAM_SPEC_UNITS
CLUTTER_IS_PARAM_SPEC_UNITS
clutter_units_get_type
clutter_param_units_get_type
</SECTION>
<SECTION>

View File

@ -30,6 +30,7 @@ test_conformance_SOURCES = \
test-model.c \
test-blend-strings.c \
test-color.c \
test-clutter-units.c \
$(NULL)
# For convenience, this provides a way to easily run individual unit tests:

View File

@ -0,0 +1,58 @@
#include <stdio.h>
#include <clutter/clutter.h>
#include "test-conform-common.h"
void
test_units_constructors (TestConformSimpleFixture *fixture,
gconstpointer data)
{
ClutterUnits units;
clutter_units_pixels (&units, 100);
g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_PIXEL);
g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 100.0);
g_assert_cmpfloat (clutter_units_to_pixels (&units), ==, 100.0);
clutter_units_em (&units, 5.0);
g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_EM);
g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 5.0);
g_assert_cmpfloat (clutter_units_to_pixels (&units), !=, 5.0);
}
void
test_units_string (TestConformSimpleFixture *fixture,
gconstpointer data)
{
ClutterUnits units;
gchar *string;
g_assert (clutter_units_from_string (&units, "5 em") == TRUE);
g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_EM);
g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 5);
g_assert (clutter_units_from_string (&units, " 16 mm") == TRUE);
g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_MM);
g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 16);
g_assert (clutter_units_from_string (&units, "1 pony") == FALSE);
clutter_units_pt (&units, 24.0);
string = clutter_units_to_string (&units);
g_assert_cmpstr (string, ==, "24.0 pt");
g_free (string);
clutter_units_em (&units, 3.0);
string = clutter_units_to_string (&units);
g_assert_cmpstr (string, ==, "3.00 em");
units.unit_type = CLUTTER_UNIT_PIXEL;
units.value = 0;
g_assert (clutter_units_from_string (&units, string) == TRUE);
g_assert (clutter_units_get_unit_type (&units) != CLUTTER_UNIT_PIXEL);
g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_EM);
g_assert_cmpint ((int) clutter_units_get_unit_value (&units), ==, 3);
g_free (string);
}

View File

@ -148,5 +148,8 @@ main (int argc, char **argv)
TEST_CONFORM_SIMPLE ("/color", test_color_from_string);
TEST_CONFORM_SIMPLE ("/color", test_color_to_string);
TEST_CONFORM_SIMPLE ("/units", test_units_constructors);
TEST_CONFORM_SIMPLE ("/units", test_units_string);
return g_test_run ();
}

View File

@ -90,18 +90,20 @@ test_text_field_main (gint argc,
ClutterColor entry_color = {0x33, 0xff, 0x33, 0xff};
ClutterColor label_color = {0xff, 0xff, 0xff, 0xff};
ClutterColor background_color = {0x00, 0x00, 0x00, 0xff};
ClutterUnits h_padding, v_padding;
gfloat width, height;
gfloat h_padding, v_padding;
clutter_init (&argc, &argv);
stage = clutter_stage_get_default ();
clutter_stage_set_color (CLUTTER_STAGE (stage), &background_color);
h_padding = clutter_units_em_for_font (FONT, 2.0); /* 2em */
v_padding = clutter_units_em_for_font (FONT, 3.0); /* 3em */
clutter_units_em_for_font (&h_padding, FONT, 2.0); /* 2em */
clutter_units_em_for_font (&v_padding, FONT, 3.0); /* 3em */
g_print ("padding: h:%.2f px, v:%.2f px\n", h_padding, v_padding);
g_print ("padding: h:%.2f px, v:%.2f px\n",
clutter_units_to_pixels (&h_padding),
clutter_units_to_pixels (&v_padding));
text = create_label (&label_color, "<b>Input field:</b> ");
clutter_actor_set_position (text, 10, 10);
@ -111,17 +113,21 @@ test_text_field_main (gint argc,
height = clutter_actor_get_height (text);
text = create_entry (&entry_color, "<i>some</i> text", 0, 0);
clutter_actor_set_position (text, 10 + width + h_padding, 10);
clutter_actor_set_position (text,
width + 10 + clutter_units_to_pixels (&h_padding),
10);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), text);
text = create_label (&label_color, "<i>A very long password field</i>: ");
clutter_actor_set_position (text, 10, height + v_padding);
clutter_actor_set_position (text,
10,
height + 10 + clutter_units_to_pixels (&v_padding));
clutter_container_add_actor (CLUTTER_CONTAINER (stage), text);
text = create_entry (&entry_color, "password", '*', 8);
clutter_actor_set_position (text,
width + 10 + h_padding,
height + 10 + v_padding);
width + 10 + clutter_units_to_pixels (&h_padding),
height + 10 + clutter_units_to_pixels (&v_padding));
clutter_container_add_actor (CLUTTER_CONTAINER (stage), text);
clutter_actor_show (stage);