mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-14 08:16:24 +01:00
Merge branch '564-gsettings-bind-with-closures' into 'main'
Add g_settings_bind_with_mapping_closures() Closes #564 See merge request GNOME/glib!3971
This commit is contained in:
commit
768b7a6a03
142
gio/gsettings.c
142
gio/gsettings.c
@ -2991,6 +2991,148 @@ g_settings_bind_with_mapping (GSettings *settings,
|
||||
binding, g_settings_binding_free);
|
||||
}
|
||||
|
||||
typedef struct _BindWithMappingClosuresData
|
||||
{
|
||||
GClosure *get_mapping_closure;
|
||||
GClosure *set_mapping_closure;
|
||||
} BindWithMappingClosuresData;
|
||||
|
||||
static BindWithMappingClosuresData *
|
||||
bind_with_mapping_closures_data_new (GClosure *get_mapping_closure,
|
||||
GClosure *set_mapping_closure)
|
||||
{
|
||||
BindWithMappingClosuresData *data;
|
||||
|
||||
data = g_new0 (BindWithMappingClosuresData, 1);
|
||||
|
||||
if (get_mapping_closure != NULL)
|
||||
{
|
||||
data->get_mapping_closure = g_closure_ref (get_mapping_closure);
|
||||
g_closure_sink (get_mapping_closure);
|
||||
if (G_CLOSURE_NEEDS_MARSHAL (get_mapping_closure))
|
||||
g_closure_set_marshal (get_mapping_closure, g_cclosure_marshal_generic);
|
||||
}
|
||||
|
||||
if (set_mapping_closure != NULL)
|
||||
{
|
||||
data->set_mapping_closure = g_closure_ref (set_mapping_closure);
|
||||
g_closure_sink (set_mapping_closure);
|
||||
if (G_CLOSURE_NEEDS_MARSHAL (set_mapping_closure))
|
||||
g_closure_set_marshal (set_mapping_closure, g_cclosure_marshal_generic);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void
|
||||
bind_with_mapping_closures_data_free (BindWithMappingClosuresData *data)
|
||||
{
|
||||
if (data->get_mapping_closure != NULL)
|
||||
g_closure_unref (data->get_mapping_closure);
|
||||
|
||||
if (data->set_mapping_closure != NULL)
|
||||
g_closure_unref (data->set_mapping_closure);
|
||||
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
bind_with_mapping_invoke_get (GValue *value,
|
||||
GVariant *variant,
|
||||
void *user_data)
|
||||
{
|
||||
BindWithMappingClosuresData *data = (BindWithMappingClosuresData *) user_data;
|
||||
GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
|
||||
GValue out = G_VALUE_INIT;
|
||||
gboolean retval;
|
||||
|
||||
g_value_init (¶ms[0], G_TYPE_VALUE);
|
||||
g_value_set_boxed (¶ms[0], value);
|
||||
g_value_init (¶ms[1], G_TYPE_VARIANT);
|
||||
g_value_set_variant (¶ms[1], variant);
|
||||
g_value_init (&out, G_TYPE_BOOLEAN);
|
||||
|
||||
g_closure_invoke (data->get_mapping_closure, &out, 2, params, /* hint = */ NULL);
|
||||
|
||||
retval = g_value_get_boolean (&out);
|
||||
|
||||
g_value_unset (&out);
|
||||
g_value_unset (¶ms[0]);
|
||||
g_value_unset (¶ms[1]);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
bind_with_mapping_invoke_set (const GValue *value,
|
||||
const GVariantType *expected_type,
|
||||
void *user_data)
|
||||
{
|
||||
BindWithMappingClosuresData *data = (BindWithMappingClosuresData *) user_data;
|
||||
GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
|
||||
GValue out = G_VALUE_INIT;
|
||||
GVariant *retval;
|
||||
|
||||
g_value_init (¶ms[0], G_TYPE_VALUE);
|
||||
g_value_set_boxed (¶ms[0], value);
|
||||
g_value_init (¶ms[1], G_TYPE_VARIANT_TYPE);
|
||||
g_value_set_boxed (¶ms[1], expected_type);
|
||||
g_value_init (&out, G_TYPE_VARIANT);
|
||||
|
||||
g_closure_invoke (data->set_mapping_closure, &out, 2, params, /* hint = */ NULL);
|
||||
|
||||
retval = g_value_dup_variant (&out);
|
||||
|
||||
g_value_unset (&out);
|
||||
g_value_unset (¶ms[0]);
|
||||
g_value_unset (¶ms[1]);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
bind_with_mapping_destroy (void *user_data)
|
||||
{
|
||||
BindWithMappingClosuresData *data = (BindWithMappingClosuresData *) user_data;
|
||||
bind_with_mapping_closures_data_free (data);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_settings_bind_with_mapping_closures: (rename-to g_settings_bind_with_mapping):
|
||||
* @settings: a #GSettings object
|
||||
* @key: the key to bind
|
||||
* @object: a #GObject
|
||||
* @property: the name of the property to bind
|
||||
* @flags: flags for the binding
|
||||
* @get_mapping: (nullable): a function that gets called to convert values
|
||||
* from @settings to @object, or %NULL to use the default GIO mapping
|
||||
* @set_mapping: (nullable): a function that gets called to convert values
|
||||
* from @object to @settings, or %NULL to use the default GIO mapping
|
||||
*
|
||||
* Version of g_settings_bind_with_mapping() using closures instead of callbacks
|
||||
* for easier binding in other languages.
|
||||
*
|
||||
* Since: 2.82
|
||||
*/
|
||||
void
|
||||
g_settings_bind_with_mapping_closures (GSettings *settings,
|
||||
const char *key,
|
||||
GObject *object,
|
||||
const char *property,
|
||||
GSettingsBindFlags flags,
|
||||
GClosure *get_mapping,
|
||||
GClosure *set_mapping)
|
||||
{
|
||||
BindWithMappingClosuresData *data;
|
||||
|
||||
data = bind_with_mapping_closures_data_new (get_mapping, set_mapping);
|
||||
|
||||
g_settings_bind_with_mapping (settings, key, object, property, flags,
|
||||
bind_with_mapping_invoke_get,
|
||||
bind_with_mapping_invoke_set, data,
|
||||
bind_with_mapping_destroy);
|
||||
}
|
||||
|
||||
/* Writability binding {{{1 */
|
||||
typedef struct
|
||||
{
|
||||
|
@ -322,6 +322,14 @@ void g_settings_bind_with_mapping (GSettin
|
||||
GSettingsBindSetMapping set_mapping,
|
||||
gpointer user_data,
|
||||
GDestroyNotify destroy);
|
||||
GIO_AVAILABLE_IN_2_82
|
||||
void g_settings_bind_with_mapping_closures (GSettings *settings,
|
||||
const char *key,
|
||||
GObject *object,
|
||||
const char *property,
|
||||
GSettingsBindFlags flags,
|
||||
GClosure *get_mapping,
|
||||
GClosure *set_mapping);
|
||||
GIO_AVAILABLE_IN_ALL
|
||||
void g_settings_bind_writable (GSettings *settings,
|
||||
const gchar *key,
|
||||
|
@ -1714,6 +1714,146 @@ test_custom_binding (void)
|
||||
g_object_unref (settings);
|
||||
}
|
||||
|
||||
/* Same test as above, but with closures
|
||||
*/
|
||||
static void
|
||||
test_bind_with_mapping_closures (void)
|
||||
{
|
||||
TestObject *obj;
|
||||
GSettings *settings;
|
||||
char *s;
|
||||
gboolean b;
|
||||
GClosure *get;
|
||||
GClosure *set;
|
||||
|
||||
settings = g_settings_new ("org.gtk.test.binding");
|
||||
obj = test_object_new ();
|
||||
|
||||
g_settings_set_string (settings, "string", "true");
|
||||
|
||||
get = g_cclosure_new (G_CALLBACK (string_to_bool), NULL, NULL);
|
||||
set = g_cclosure_new (G_CALLBACK (bool_to_string), NULL, NULL);
|
||||
|
||||
g_settings_bind_with_mapping_closures (settings, "string",
|
||||
G_OBJECT (obj), "bool",
|
||||
G_SETTINGS_BIND_DEFAULT, get, set);
|
||||
|
||||
g_settings_set_string (settings, "string", "false");
|
||||
g_object_get (obj, "bool", &b, NULL);
|
||||
g_assert_cmpint (b, ==, FALSE);
|
||||
|
||||
g_settings_set_string (settings, "string", "not true");
|
||||
g_object_get (obj, "bool", &b, NULL);
|
||||
g_assert_cmpint (b, ==, FALSE);
|
||||
|
||||
g_object_set (obj, "bool", TRUE, NULL);
|
||||
s = g_settings_get_string (settings, "string");
|
||||
g_assert_cmpstr (s, ==, "true");
|
||||
g_free (s);
|
||||
|
||||
set = g_cclosure_new (G_CALLBACK (bool_to_bool), NULL, NULL);
|
||||
|
||||
g_settings_bind_with_mapping_closures (settings, "string",
|
||||
G_OBJECT (obj), "bool",
|
||||
G_SETTINGS_BIND_DEFAULT, get, set);
|
||||
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
|
||||
"*binding mapping function for key 'string' returned"
|
||||
" GVariant of type 'b' when type 's' was requested*");
|
||||
g_object_set (obj, "bool", FALSE, NULL);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
g_object_unref (obj);
|
||||
g_object_unref (settings);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gboolean get_called;
|
||||
gboolean set_called;
|
||||
gboolean get_freed;
|
||||
gboolean set_freed;
|
||||
} BindWithMappingData;
|
||||
|
||||
static gboolean
|
||||
get_callback (GValue *value,
|
||||
GVariant *variant,
|
||||
void *user_data)
|
||||
{
|
||||
BindWithMappingData *data = (BindWithMappingData *) user_data;
|
||||
data->get_called = TRUE;
|
||||
|
||||
g_assert_true (G_VALUE_HOLDS_BOOLEAN (value));
|
||||
g_assert_true (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING));
|
||||
|
||||
return string_to_bool (value, variant, NULL);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
set_callback (const GValue *value,
|
||||
const GVariantType *expected_type,
|
||||
void *user_data)
|
||||
{
|
||||
BindWithMappingData *data = (BindWithMappingData *) user_data;
|
||||
data->set_called = TRUE;
|
||||
|
||||
g_assert_true (G_VALUE_HOLDS_BOOLEAN (value));
|
||||
g_assert_true (g_variant_type_equal (expected_type, G_VARIANT_TYPE_STRING));
|
||||
|
||||
return bool_to_string (value, expected_type, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
teardown_get (void *user_data, GClosure *closure)
|
||||
{
|
||||
BindWithMappingData *data = (BindWithMappingData *) user_data;
|
||||
data->get_freed = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
teardown_set (void *user_data, GClosure *closure)
|
||||
{
|
||||
BindWithMappingData *data = (BindWithMappingData *) user_data;
|
||||
data->set_freed = TRUE;
|
||||
}
|
||||
|
||||
/* Tests the types of GValue and GVariant passed to the closures */
|
||||
static void
|
||||
test_bind_with_mapping_closures_parameters (void)
|
||||
{
|
||||
TestObject *obj;
|
||||
GSettings *settings;
|
||||
GClosure *get;
|
||||
GClosure *set;
|
||||
BindWithMappingData data = { FALSE, FALSE, FALSE, FALSE };
|
||||
|
||||
settings = g_settings_new ("org.gtk.test.binding");
|
||||
obj = test_object_new ();
|
||||
|
||||
g_settings_set_string (settings, "string", "true");
|
||||
|
||||
get = g_cclosure_new (G_CALLBACK (get_callback), &data, teardown_get);
|
||||
set = g_cclosure_new (G_CALLBACK (set_callback), &data, teardown_set);
|
||||
|
||||
g_settings_bind_with_mapping_closures (settings, "string",
|
||||
G_OBJECT (obj), "bool",
|
||||
G_SETTINGS_BIND_DEFAULT, get, set);
|
||||
|
||||
g_assert_true (data.get_called);
|
||||
g_assert_false (data.set_called);
|
||||
|
||||
data.get_called = FALSE;
|
||||
g_object_set (obj, "bool", FALSE, NULL);
|
||||
g_assert_true (data.set_called);
|
||||
g_assert_false (data.get_called);
|
||||
|
||||
g_object_unref (obj);
|
||||
|
||||
g_assert_true (data.get_freed);
|
||||
g_assert_true (data.set_freed);
|
||||
|
||||
g_object_unref (settings);
|
||||
}
|
||||
|
||||
/* Test that with G_SETTINGS_BIND_NO_CHANGES, the
|
||||
* initial settings value is transported to the object
|
||||
* side, but later settings changes do not affect the
|
||||
@ -3237,6 +3377,8 @@ main (int argc, char *argv[])
|
||||
g_test_add_func ("/gsettings/simple-binding", test_simple_binding);
|
||||
g_test_add_func ("/gsettings/directional-binding", test_directional_binding);
|
||||
g_test_add_func ("/gsettings/custom-binding", test_custom_binding);
|
||||
g_test_add_func ("/gsettings/bind-with-mapping-closures", test_bind_with_mapping_closures);
|
||||
g_test_add_func ("/gsettings/bind-with-mapping-closures-parameters", test_bind_with_mapping_closures_parameters);
|
||||
g_test_add_func ("/gsettings/no-change-binding", test_no_change_binding);
|
||||
g_test_add_func ("/gsettings/unbinding", test_unbind);
|
||||
g_test_add_func ("/gsettings/writable-binding", test_bind_writable);
|
||||
|
Loading…
Reference in New Issue
Block a user