mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-05-02 20:16:52 +02:00
binding: Add a closure-based variant of bind_property_full()
Since using the function pointer version muddles the memory management requirements of language bindings, we should implement a GClosure-based variant on top of g_object_bind_property_full(). https://bugzilla.gnome.org/show_bug.cgi?id=622278
This commit is contained in:
parent
ca3b7b75bf
commit
3be3ad61d1
@ -867,6 +867,7 @@ g_binding_get_flags
|
|||||||
g_object_bind_property
|
g_object_bind_property
|
||||||
GBindingTransformFunc
|
GBindingTransformFunc
|
||||||
g_object_bind_property_full
|
g_object_bind_property_full
|
||||||
|
g_object_bind_property_with_closures
|
||||||
<SUBSECTION Standard>
|
<SUBSECTION Standard>
|
||||||
G_TYPE_BINDING
|
G_TYPE_BINDING
|
||||||
G_TYPE_BINDING_FLAGS
|
G_TYPE_BINDING_FLAGS
|
||||||
|
@ -809,12 +809,6 @@ g_object_bind_property_full (gpointer source,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transform_to == NULL)
|
|
||||||
transform_to = default_transform_to;
|
|
||||||
|
|
||||||
if (transform_from == NULL)
|
|
||||||
transform_from = default_transform_from;
|
|
||||||
|
|
||||||
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (source), source_property);
|
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (source), source_property);
|
||||||
if (pspec == NULL)
|
if (pspec == NULL)
|
||||||
{
|
{
|
||||||
@ -881,9 +875,12 @@ g_object_bind_property_full (gpointer source,
|
|||||||
"flags", flags,
|
"flags", flags,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
/* making these properties would be awkward, though not impossible */
|
if (transform_to != NULL)
|
||||||
binding->transform_s2t = transform_to;
|
binding->transform_s2t = transform_to;
|
||||||
binding->transform_t2s = transform_from;
|
|
||||||
|
if (transform_from != NULL)
|
||||||
|
binding->transform_t2s = transform_from;
|
||||||
|
|
||||||
binding->transform_data = user_data;
|
binding->transform_data = user_data;
|
||||||
binding->notify = notify;
|
binding->notify = notify;
|
||||||
|
|
||||||
@ -951,3 +948,171 @@ g_object_bind_property (gpointer source,
|
|||||||
NULL,
|
NULL,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct _TransformData
|
||||||
|
{
|
||||||
|
GClosure *transform_to_closure;
|
||||||
|
GClosure *transform_from_closure;
|
||||||
|
} TransformData;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
bind_with_closures_transform_to (GBinding *binding,
|
||||||
|
const GValue *source,
|
||||||
|
GValue *target,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
TransformData *t_data = data;
|
||||||
|
GValue params[3] = { { 0, }, { 0, }, { 0, } };
|
||||||
|
GValue retval = { 0, };
|
||||||
|
gboolean res;
|
||||||
|
|
||||||
|
g_value_init (¶ms[0], G_TYPE_BINDING);
|
||||||
|
g_value_set_object (¶ms[0], binding);
|
||||||
|
|
||||||
|
g_value_init (¶ms[1], G_TYPE_VALUE);
|
||||||
|
g_value_set_boxed (¶ms[1], source);
|
||||||
|
|
||||||
|
g_value_init (¶ms[2], G_TYPE_VALUE);
|
||||||
|
g_value_set_boxed (¶ms[2], target);
|
||||||
|
|
||||||
|
g_value_init (&retval, G_TYPE_BOOLEAN);
|
||||||
|
g_value_set_boolean (&retval, FALSE);
|
||||||
|
|
||||||
|
g_closure_invoke (t_data->transform_to_closure, &retval, 3, params, NULL);
|
||||||
|
|
||||||
|
res = g_value_get_boolean (&retval);
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
const GValue *out_value = g_value_get_boxed (¶ms[2]);
|
||||||
|
|
||||||
|
g_assert (out_value != NULL);
|
||||||
|
|
||||||
|
g_value_copy (out_value, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_value_unset (¶ms[0]);
|
||||||
|
g_value_unset (¶ms[1]);
|
||||||
|
g_value_unset (¶ms[2]);
|
||||||
|
g_value_unset (&retval);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
bind_with_closures_transform_from (GBinding *binding,
|
||||||
|
const GValue *source,
|
||||||
|
GValue *target,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
TransformData *t_data = data;
|
||||||
|
GValue params[3] = { { 0, }, { 0, }, { 0, } };
|
||||||
|
GValue retval = { 0, };
|
||||||
|
gboolean res;
|
||||||
|
|
||||||
|
g_value_init (¶ms[0], G_TYPE_BINDING);
|
||||||
|
g_value_set_object (¶ms[0], binding);
|
||||||
|
|
||||||
|
g_value_init (¶ms[1], G_TYPE_VALUE);
|
||||||
|
g_value_set_boxed (¶ms[1], source);
|
||||||
|
|
||||||
|
g_value_init (¶ms[2], G_TYPE_VALUE);
|
||||||
|
g_value_set_boxed (¶ms[2], target);
|
||||||
|
|
||||||
|
g_value_init (&retval, G_TYPE_BOOLEAN);
|
||||||
|
g_value_set_boolean (&retval, FALSE);
|
||||||
|
|
||||||
|
g_closure_invoke (t_data->transform_from_closure, &retval, 3, params, NULL);
|
||||||
|
|
||||||
|
res = g_value_get_boolean (&retval);
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
const GValue *out_value = g_value_get_boxed (¶ms[2]);
|
||||||
|
|
||||||
|
g_assert (out_value != NULL);
|
||||||
|
|
||||||
|
g_value_copy (out_value, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_value_unset (¶ms[0]);
|
||||||
|
g_value_unset (¶ms[1]);
|
||||||
|
g_value_unset (¶ms[2]);
|
||||||
|
g_value_unset (&retval);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bind_with_closures_free_func (gpointer data)
|
||||||
|
{
|
||||||
|
TransformData *t_data = data;
|
||||||
|
|
||||||
|
if (t_data->transform_to_closure != NULL)
|
||||||
|
g_closure_unref (t_data->transform_to_closure);
|
||||||
|
|
||||||
|
if (t_data->transform_from_closure != NULL)
|
||||||
|
g_closure_unref (t_data->transform_from_closure);
|
||||||
|
|
||||||
|
g_slice_free (TransformData, t_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_object_bind_property_with_closures:
|
||||||
|
* @source: the source #GObject
|
||||||
|
* @source_property: the property on @source to bind
|
||||||
|
* @target: the target #GObject
|
||||||
|
* @target_property: the property on @target to bind
|
||||||
|
* @flags: flags to pass to #GBinding
|
||||||
|
* @transform_to: a #GClosure wrapping the transformation function
|
||||||
|
* from the @source to the @target, or %NULL to use the default
|
||||||
|
* @transform_from: a #GClosure wrapping the transformation function
|
||||||
|
* from the @target to the @source, or %NULL to use the default
|
||||||
|
*
|
||||||
|
* Creates a binding between @source_property on @source and @target_property
|
||||||
|
* on @target, allowing you to set the transformation functions to be used by
|
||||||
|
* the binding.
|
||||||
|
*
|
||||||
|
* This function is the language bindings friendly version of
|
||||||
|
* g_object_bind_property_full(), using #GClosure<!-- -->s instead of
|
||||||
|
* function pointers.
|
||||||
|
*
|
||||||
|
* Rename to: g_object_bind_property_full
|
||||||
|
*
|
||||||
|
* Return value: (transfer none): the #GBinding instance representing the
|
||||||
|
* binding between the two #GObject instances. The binding is released
|
||||||
|
* whenever the #GBinding reference count reaches zero.
|
||||||
|
*
|
||||||
|
* Since: 2.26
|
||||||
|
*/
|
||||||
|
GBinding *
|
||||||
|
g_object_bind_property_with_closures (gpointer source,
|
||||||
|
const gchar *source_property,
|
||||||
|
gpointer target,
|
||||||
|
const gchar *target_property,
|
||||||
|
GBindingFlags flags,
|
||||||
|
GClosure *transform_to,
|
||||||
|
GClosure *transform_from)
|
||||||
|
{
|
||||||
|
TransformData *data;
|
||||||
|
|
||||||
|
data = g_slice_new0 (TransformData);
|
||||||
|
|
||||||
|
if (transform_to != NULL)
|
||||||
|
{
|
||||||
|
data->transform_to_closure = g_closure_ref (transform_to);
|
||||||
|
g_closure_sink (data->transform_to_closure);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transform_from != NULL)
|
||||||
|
{
|
||||||
|
data->transform_from_closure = g_closure_ref (transform_from);
|
||||||
|
g_closure_sink (data->transform_from_closure);
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_object_bind_property_full (source, source_property,
|
||||||
|
target, target_property,
|
||||||
|
flags,
|
||||||
|
transform_to != NULL ? bind_with_closures_transform_to : NULL,
|
||||||
|
transform_from != NULL ? bind_with_closures_transform_from : NULL,
|
||||||
|
data,
|
||||||
|
bind_with_closures_free_func);
|
||||||
|
}
|
||||||
|
@ -103,20 +103,27 @@ GObject * g_binding_get_target (GBinding *binding);
|
|||||||
G_CONST_RETURN gchar *g_binding_get_source_property (GBinding *binding);
|
G_CONST_RETURN gchar *g_binding_get_source_property (GBinding *binding);
|
||||||
G_CONST_RETURN gchar *g_binding_get_target_property (GBinding *binding);
|
G_CONST_RETURN gchar *g_binding_get_target_property (GBinding *binding);
|
||||||
|
|
||||||
GBinding *g_object_bind_property (gpointer source,
|
GBinding *g_object_bind_property (gpointer source,
|
||||||
const gchar *source_property,
|
const gchar *source_property,
|
||||||
gpointer target,
|
gpointer target,
|
||||||
const gchar *target_property,
|
const gchar *target_property,
|
||||||
GBindingFlags flags);
|
GBindingFlags flags);
|
||||||
GBinding *g_object_bind_property_full (gpointer source,
|
GBinding *g_object_bind_property_full (gpointer source,
|
||||||
const gchar *source_property,
|
const gchar *source_property,
|
||||||
gpointer target,
|
gpointer target,
|
||||||
const gchar *target_property,
|
const gchar *target_property,
|
||||||
GBindingFlags flags,
|
GBindingFlags flags,
|
||||||
GBindingTransformFunc transform_to,
|
GBindingTransformFunc transform_to,
|
||||||
GBindingTransformFunc transform_from,
|
GBindingTransformFunc transform_from,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
GDestroyNotify notify);
|
GDestroyNotify notify);
|
||||||
|
GBinding *g_object_bind_property_with_closures (gpointer source,
|
||||||
|
const gchar *source_property,
|
||||||
|
gpointer target,
|
||||||
|
const gchar *target_property,
|
||||||
|
GBindingFlags flags,
|
||||||
|
GClosure *transform_to,
|
||||||
|
GClosure *transform_from);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ g_binding_get_source_property
|
|||||||
g_binding_get_target_property
|
g_binding_get_target_property
|
||||||
g_object_bind_property
|
g_object_bind_property
|
||||||
g_object_bind_property_full
|
g_object_bind_property_full
|
||||||
|
g_object_bind_property_with_closures
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -247,8 +247,8 @@ binding_default (void)
|
|||||||
target, "bar",
|
target, "bar",
|
||||||
G_BINDING_DEFAULT);
|
G_BINDING_DEFAULT);
|
||||||
|
|
||||||
g_assert (g_binding_get_source (binding) == G_OBJECT (source));
|
g_assert ((BindingSource *) g_binding_get_source (binding) == source);
|
||||||
g_assert (g_binding_get_target (binding) == G_OBJECT (target));
|
g_assert ((BindingTarget *) g_binding_get_target (binding) == target);
|
||||||
g_assert_cmpstr (g_binding_get_source_property (binding), ==, "foo");
|
g_assert_cmpstr (g_binding_get_source_property (binding), ==, "foo");
|
||||||
g_assert_cmpstr (g_binding_get_target_property (binding), ==, "bar");
|
g_assert_cmpstr (g_binding_get_target_property (binding), ==, "bar");
|
||||||
g_assert_cmpint (g_binding_get_flags (binding), ==, G_BINDING_DEFAULT);
|
g_assert_cmpint (g_binding_get_flags (binding), ==, G_BINDING_DEFAULT);
|
||||||
@ -329,6 +329,77 @@ binding_transform (void)
|
|||||||
g_assert (unused_data);
|
g_assert (unused_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
binding_transform_marshal (GClosure *closure,
|
||||||
|
GValue *return_value,
|
||||||
|
guint n_param_values,
|
||||||
|
const GValue *param_values,
|
||||||
|
gpointer invocation_hint G_GNUC_UNUSED,
|
||||||
|
gpointer marshal_data)
|
||||||
|
{
|
||||||
|
typedef gboolean (* GMarshalFunc_BOOLEAN__VALUE_VALUE) (gpointer data1,
|
||||||
|
gpointer arg_2,
|
||||||
|
gpointer arg_3,
|
||||||
|
gpointer data2);
|
||||||
|
register GMarshalFunc_BOOLEAN__VALUE_VALUE callback;
|
||||||
|
register GCClosure *cc = (GCClosure *) closure;
|
||||||
|
register gpointer data1, data2;
|
||||||
|
gboolean v_return;
|
||||||
|
|
||||||
|
if (G_CCLOSURE_SWAP_DATA (closure))
|
||||||
|
{
|
||||||
|
data1 = closure->data;
|
||||||
|
data2 = g_value_peek_pointer (param_values + 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data1 = g_value_peek_pointer (param_values + 0);
|
||||||
|
data2 = closure->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback = (GMarshalFunc_BOOLEAN__VALUE_VALUE) (marshal_data ? marshal_data : cc->callback);
|
||||||
|
v_return = callback (data1,
|
||||||
|
g_value_get_boxed (param_values + 1),
|
||||||
|
g_value_get_boxed (param_values + 2),
|
||||||
|
data2);
|
||||||
|
|
||||||
|
g_value_set_boolean (return_value, v_return);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
binding_transform_closure (void)
|
||||||
|
{
|
||||||
|
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
|
||||||
|
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
|
||||||
|
GBinding *binding;
|
||||||
|
gboolean unused_data_1 = FALSE, unused_data_2 = FALSE;
|
||||||
|
GClosure *c2f_clos, *f2c_clos;
|
||||||
|
|
||||||
|
c2f_clos = g_cclosure_new (G_CALLBACK (celsius_to_fahrenheit), &unused_data_1, (GClosureNotify) data_free);
|
||||||
|
g_closure_set_marshal (c2f_clos, binding_transform_marshal);
|
||||||
|
|
||||||
|
f2c_clos = g_cclosure_new (G_CALLBACK (fahrenheit_to_celsius), &unused_data_2, (GClosureNotify) data_free);
|
||||||
|
g_closure_set_marshal (f2c_clos, binding_transform_marshal);
|
||||||
|
|
||||||
|
binding = g_object_bind_property_with_closures (source, "value",
|
||||||
|
target, "value",
|
||||||
|
G_BINDING_BIDIRECTIONAL,
|
||||||
|
c2f_clos,
|
||||||
|
f2c_clos);
|
||||||
|
|
||||||
|
g_object_set (source, "value", 24.0, NULL);
|
||||||
|
g_assert_cmpfloat (target->value, ==, ((9 * 24.0 / 5) + 32.0));
|
||||||
|
|
||||||
|
g_object_set (target, "value", 69.0, NULL);
|
||||||
|
g_assert_cmpfloat (source->value, ==, (5 * (69.0 - 32.0) / 9));
|
||||||
|
|
||||||
|
g_object_unref (source);
|
||||||
|
g_object_unref (target);
|
||||||
|
|
||||||
|
g_assert (unused_data_1);
|
||||||
|
g_assert (unused_data_2);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
binding_chain (void)
|
binding_chain (void)
|
||||||
{
|
{
|
||||||
@ -407,6 +478,7 @@ main (int argc, char *argv[])
|
|||||||
g_test_add_func ("/binding/default", binding_default);
|
g_test_add_func ("/binding/default", binding_default);
|
||||||
g_test_add_func ("/binding/bidirectional", binding_bidirectional);
|
g_test_add_func ("/binding/bidirectional", binding_bidirectional);
|
||||||
g_test_add_func ("/binding/transform", binding_transform);
|
g_test_add_func ("/binding/transform", binding_transform);
|
||||||
|
g_test_add_func ("/binding/transform-closure", binding_transform_closure);
|
||||||
g_test_add_func ("/binding/chain", binding_chain);
|
g_test_add_func ("/binding/chain", binding_chain);
|
||||||
g_test_add_func ("/binding/sync-create", binding_sync_create);
|
g_test_add_func ("/binding/sync-create", binding_sync_create);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user