[gnome-builder] libide/tweaks: implement bindings with transforms
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] libide/tweaks: implement bindings with transforms
- Date: Wed, 24 Aug 2022 19:58:20 +0000 (UTC)
commit 58c48f27e293742980ae055231d904d2c55e53c1
Author: Christian Hergert <chergert redhat com>
Date: Wed Aug 24 12:55:50 2022 -0700
libide/tweaks: implement bindings with transforms
src/libide/tweaks/ide-tweaks-binding.c | 170 +++++++++++++++++++++++++++++++-
src/libide/tweaks/ide-tweaks-binding.h | 43 +++++---
src/libide/tweaks/ide-tweaks-property.c | 56 +++++++++--
src/libide/tweaks/ide-tweaks-setting.c | 48 ++++++++-
4 files changed, 286 insertions(+), 31 deletions(-)
---
diff --git a/src/libide/tweaks/ide-tweaks-binding.c b/src/libide/tweaks/ide-tweaks-binding.c
index 0693cecbf..8c5331c3c 100644
--- a/src/libide/tweaks/ide-tweaks-binding.c
+++ b/src/libide/tweaks/ide-tweaks-binding.c
@@ -24,11 +24,20 @@
#include "ide-tweaks-binding.h"
+typedef struct
+{
+ IdeTweaksBindingTransform get_transform;
+ IdeTweaksBindingTransform set_transform;
+ gpointer user_data;
+ GDestroyNotify notify;
+} Binding;
+
typedef struct
{
GWeakRef instance;
GParamSpec *pspec;
int inhibit;
+ Binding *binding;
} IdeTweaksBindingPrivate;
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (IdeTweaksBinding, ide_tweaks_binding, IDE_TYPE_TWEAKS_ITEM)
@@ -40,6 +49,80 @@ enum {
static guint signals [N_SIGNALS];
+static void
+binding_finalize (gpointer data)
+{
+ Binding *binding = data;
+
+ if (binding->notify)
+ binding->notify (binding->user_data);
+
+ binding->get_transform = NULL;
+ binding->set_transform = NULL;
+ binding->user_data = NULL;
+ binding->notify = NULL;
+}
+
+static void
+binding_unref (Binding *binding)
+{
+ g_atomic_rc_box_release_full (binding, binding_finalize);
+}
+
+static Binding *
+binding_new (IdeTweaksBindingTransform get_transform,
+ IdeTweaksBindingTransform set_transform,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ Binding *binding;
+
+ binding = g_atomic_rc_box_new0 (Binding);
+ binding->get_transform = get_transform;
+ binding->set_transform = set_transform;
+ binding->user_data = user_data;
+ binding->notify = notify;
+
+ return binding;
+}
+
+static gboolean
+binding_get (Binding *binding,
+ const GValue *from_value,
+ GValue *to_value)
+{
+ if (binding->get_transform)
+ return binding->get_transform (from_value, to_value, binding->user_data);
+ g_value_copy (from_value, to_value);
+ return TRUE;
+}
+
+static gboolean
+binding_set (Binding *binding,
+ const GValue *from_value,
+ GValue *to_value)
+{
+ if (binding->set_transform)
+ return binding->set_transform (from_value, to_value, binding->user_data);
+ g_value_copy (from_value, to_value);
+ return TRUE;
+}
+
+static gboolean
+ide_tweaks_binding_get_expected_type (IdeTweaksBinding *self,
+ GType *type)
+{
+ g_assert (IDE_IS_TWEAKS_BINDING (self));
+ g_assert (type != NULL);
+
+ if (IDE_TWEAKS_BINDING_GET_CLASS (self)->get_expected_type)
+ *type = IDE_TWEAKS_BINDING_GET_CLASS (self)->get_expected_type (self);
+ else
+ *type = G_TYPE_INVALID;
+
+ return *type != G_TYPE_INVALID;
+}
+
static void
ide_tweaks_binding_inhibit (IdeTweaksBinding *self)
{
@@ -89,6 +172,7 @@ ide_tweaks_binding_dispose (GObject *object)
IdeTweaksBinding *self = (IdeTweaksBinding *)object;
IdeTweaksBindingPrivate *priv = ide_tweaks_binding_get_instance_private (self);
+ g_clear_pointer (&priv->binding, binding_unref);
g_weak_ref_set (&priv->instance, NULL);
priv->pspec = NULL;
@@ -148,22 +232,46 @@ gboolean
ide_tweaks_binding_get_value (IdeTweaksBinding *self,
GValue *value)
{
+ IdeTweaksBindingPrivate *priv = ide_tweaks_binding_get_instance_private (self);
+ g_auto(GValue) from_value = G_VALUE_INIT;
+
g_return_val_if_fail (IDE_IS_TWEAKS_BINDING (self), FALSE);
g_return_val_if_fail (value != NULL, FALSE);
g_return_val_if_fail (G_VALUE_TYPE (value) != G_TYPE_INVALID, FALSE);
- return IDE_TWEAKS_BINDING_GET_CLASS (self)->get_value (self, value);
+ if (priv->binding == NULL || priv->pspec == NULL)
+ return IDE_TWEAKS_BINDING_GET_CLASS (self)->get_value (self, value);
+
+ /* TODO: You could optimize an extra GValue copy out here */
+ g_value_init (&from_value, priv->pspec->value_type);
+ if (IDE_TWEAKS_BINDING_GET_CLASS (self)->get_value (self, &from_value))
+ return binding_get (priv->binding, &from_value, value);
+
+ return FALSE;
}
void
ide_tweaks_binding_set_value (IdeTweaksBinding *self,
const GValue *value)
{
+ IdeTweaksBindingPrivate *priv = ide_tweaks_binding_get_instance_private (self);
+ g_auto(GValue) to_value = G_VALUE_INIT;
+ GType type;
+
g_return_if_fail (IDE_IS_TWEAKS_BINDING (self));
g_return_if_fail (value != NULL);
g_return_if_fail (G_IS_VALUE (value));
- IDE_TWEAKS_BINDING_GET_CLASS (self)->set_value (self, value);
+ if (priv->binding == NULL || !ide_tweaks_binding_get_expected_type (self, &type))
+ {
+ IDE_TWEAKS_BINDING_GET_CLASS (self)->set_value (self, value);
+ return;
+ }
+
+ /* TODO: You could optimize an extra GValue copy out here */
+ g_value_init (&to_value, type);
+ if (binding_set (priv->binding, value, &to_value))
+ IDE_TWEAKS_BINDING_GET_CLASS (self)->set_value (self, &to_value);
}
static void
@@ -194,6 +302,8 @@ ide_tweaks_binding_unbind (IdeTweaksBinding *self)
g_return_if_fail (IDE_IS_TWEAKS_BINDING (self));
+ g_clear_pointer (&priv->binding, binding_unref);
+
if ((instance = g_weak_ref_get (&priv->instance)))
{
g_weak_ref_set (&priv->instance, NULL);
@@ -205,10 +315,28 @@ ide_tweaks_binding_unbind (IdeTweaksBinding *self)
}
}
+static gboolean dummy_cb (gpointer data) { return FALSE; };
+
+/**
+ * ide_tweaks_binding_bind_with_transform:
+ * @self: a #IdeTweaksBinding
+ * @instance: a #GObject
+ * @property_name: a property of @instance
+ * @get_transform: (nullable) (scope async): an #IdeTweaksBindingTransform or %NULL
+ * @set_transform: (nullable) (scope async): an #IdeTweaksBindingTransform or %NULL
+ * @user_data: closure data for @get_transform and @set_transform
+ * @notify: closure notify for @user_data
+ *
+ * Binds the value with an optional transform.
+ */
void
-ide_tweaks_binding_bind (IdeTweaksBinding *self,
- gpointer instance,
- const char *property_name)
+ide_tweaks_binding_bind_with_transform (IdeTweaksBinding *self,
+ gpointer instance,
+ const char *property_name,
+ IdeTweaksBindingTransform get_transform,
+ IdeTweaksBindingTransform set_transform,
+ gpointer user_data,
+ GDestroyNotify notify)
{
IdeTweaksBindingPrivate *priv = ide_tweaks_binding_get_instance_private (self);
g_autofree char *signal_name = NULL;
@@ -224,10 +352,12 @@ ide_tweaks_binding_bind (IdeTweaksBinding *self,
{
g_critical ("Object of type %s does not have a property named %s",
G_OBJECT_TYPE_NAME (instance), property_name);
+ g_idle_add_full (G_PRIORITY_LOW, dummy_cb, user_data, notify);
return;
}
g_weak_ref_set (&priv->instance, instance);
+ priv->binding = binding_new (get_transform, set_transform, user_data, notify);
/* Get notifications on property changes */
signal_name = g_strdup_printf ("notify::%s", property_name);
@@ -240,3 +370,33 @@ ide_tweaks_binding_bind (IdeTweaksBinding *self,
/* Copy state to the widget */
ide_tweaks_binding_changed (self);
}
+
+void
+ide_tweaks_binding_bind (IdeTweaksBinding *self,
+ gpointer instance,
+ const char *property_name)
+{
+ ide_tweaks_binding_bind_with_transform (self, instance, property_name, NULL, NULL, NULL, NULL);
+}
+
+/**
+ * ide_tweaks_binding_dup_string:
+ * @self: a #IdeTweaksBinding
+ *
+ * Gets the current value as a newly allocated string.
+ *
+ * Returns: (transfer full) (nullable): a string or %NULL
+ */
+char *
+ide_tweaks_binding_dup_string (IdeTweaksBinding *self)
+{
+ g_auto(GValue) value = G_VALUE_INIT;
+
+ g_return_val_if_fail (IDE_IS_TWEAKS_BINDING (self), NULL);
+
+ g_value_init (&value, G_TYPE_STRING);
+ if (ide_tweaks_binding_get_value (self, &value))
+ return g_value_dup_string (&value);
+
+ return NULL;
+}
diff --git a/src/libide/tweaks/ide-tweaks-binding.h b/src/libide/tweaks/ide-tweaks-binding.h
index c299ba7ab..b4b3e7843 100644
--- a/src/libide/tweaks/ide-tweaks-binding.h
+++ b/src/libide/tweaks/ide-tweaks-binding.h
@@ -30,6 +30,10 @@ G_BEGIN_DECLS
#define IDE_TYPE_TWEAKS_BINDING (ide_tweaks_binding_get_type())
+typedef gboolean (*IdeTweaksBindingTransform) (const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data);
+
IDE_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (IdeTweaksBinding, ide_tweaks_binding, IDE, TWEAKS_BINDING, IdeTweaksItem)
@@ -37,26 +41,37 @@ struct _IdeTweaksBindingClass
{
IdeTweaksItemClass parent_class;
- void (*changed) (IdeTweaksBinding *self);
- gboolean (*get_value) (IdeTweaksBinding *self,
- GValue *value);
- void (*set_value) (IdeTweaksBinding *self,
- const GValue *value);
+ void (*changed) (IdeTweaksBinding *self);
+ gboolean (*get_value) (IdeTweaksBinding *self,
+ GValue *value);
+ void (*set_value) (IdeTweaksBinding *self,
+ const GValue *value);
+ GType (*get_expected_type) (IdeTweaksBinding *self);
};
IDE_AVAILABLE_IN_ALL
-void ide_tweaks_binding_changed (IdeTweaksBinding *self);
+void ide_tweaks_binding_changed (IdeTweaksBinding *self);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_tweaks_binding_get_value (IdeTweaksBinding *self,
+ GValue *value);
+IDE_AVAILABLE_IN_ALL
+void ide_tweaks_binding_set_value (IdeTweaksBinding *self,
+ const GValue *value);
IDE_AVAILABLE_IN_ALL
-gboolean ide_tweaks_binding_get_value (IdeTweaksBinding *self,
- GValue *value);
+char *ide_tweaks_binding_dup_string (IdeTweaksBinding *self);
IDE_AVAILABLE_IN_ALL
-void ide_tweaks_binding_set_value (IdeTweaksBinding *self,
- const GValue *value);
+void ide_tweaks_binding_bind (IdeTweaksBinding *self,
+ gpointer instance,
+ const char *property_name);
IDE_AVAILABLE_IN_ALL
-void ide_tweaks_binding_bind (IdeTweaksBinding *self,
- gpointer instance,
- const char *property_name);
+void ide_tweaks_binding_bind_with_transform (IdeTweaksBinding *self,
+ gpointer instance,
+ const char *property_name,
+ IdeTweaksBindingTransform get_transform,
+ IdeTweaksBindingTransform set_transform,
+ gpointer user_data,
+ GDestroyNotify notify);
IDE_AVAILABLE_IN_ALL
-void ide_tweaks_binding_unbind (IdeTweaksBinding *self);
+void ide_tweaks_binding_unbind (IdeTweaksBinding *self);
G_END_DECLS
diff --git a/src/libide/tweaks/ide-tweaks-property.c b/src/libide/tweaks/ide-tweaks-property.c
index d14b964d3..bbcb66a37 100644
--- a/src/libide/tweaks/ide-tweaks-property.c
+++ b/src/libide/tweaks/ide-tweaks-property.c
@@ -28,6 +28,7 @@ struct _IdeTweaksProperty
{
IdeTweaksBinding parent_instance;
GWeakRef instance;
+ GParamSpec *pspec;
const char *name;
gulong notify_handler;
};
@@ -70,18 +71,39 @@ ide_tweaks_property_acquire (IdeTweaksProperty *self)
{
g_autofree char *signal_name = g_strdup_printf ("notify::%s", self->name);
- self->notify_handler =
- g_signal_connect_object (instance,
- signal_name,
- G_CALLBACK (ide_tweaks_property_object_notify_cb),
- self,
- G_CONNECT_SWAPPED);
+ self->pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (instance), self->name);
+
+ if (self->pspec == NULL)
+ g_critical ("Object %s has no property named %s",
+ G_OBJECT_TYPE_NAME (instance), self->name);
+ else
+ self->notify_handler =
+ g_signal_connect_object (instance,
+ signal_name,
+ G_CALLBACK (ide_tweaks_property_object_notify_cb),
+ self,
+ G_CONNECT_SWAPPED);
}
}
return g_steal_pointer (&instance);
}
+static void
+ide_tweaks_property_release (IdeTweaksProperty *self)
+{
+ g_autoptr(GObject) instance = NULL;
+
+ g_assert (IDE_IS_TWEAKS_PROPERTY (self));
+
+ if ((instance = g_weak_ref_get (&self->instance)))
+ g_clear_signal_handler (&self->notify_handler, instance);
+
+ self->pspec = NULL;
+ self->notify_handler = 0;
+ g_weak_ref_set (&self->instance, NULL);
+}
+
static gboolean
ide_tweaks_property_get_value (IdeTweaksBinding *binding,
GValue *value)
@@ -127,21 +149,38 @@ ide_tweaks_property_set_object_internal (IdeTweaksProperty *self,
if (previous == object)
return FALSE;
- g_clear_signal_handler (&self->notify_handler, previous);
+ ide_tweaks_property_release (self);
+
g_weak_ref_set (&self->instance, object);
return TRUE;
}
+static GType
+ide_tweaks_property_get_expected_type (IdeTweaksBinding *binding)
+{
+ IdeTweaksProperty *self = (IdeTweaksProperty *)binding;
+ g_autoptr(GObject) instance = NULL;
+
+ g_assert (IDE_IS_TWEAKS_PROPERTY (self));
+
+ if ((instance = ide_tweaks_property_acquire (self)))
+ return self->pspec->value_type;
+
+ return G_TYPE_INVALID;
+}
+
static void
ide_tweaks_property_dispose (GObject *object)
{
IdeTweaksProperty *self = (IdeTweaksProperty *)object;
- ide_tweaks_property_set_object_internal (self, NULL);
+ ide_tweaks_property_release (self);
+
self->name = NULL;
g_assert (self->name == NULL);
+ g_assert (self->pspec == NULL);
g_assert (self->notify_handler == 0);
g_assert (g_weak_ref_get (&self->instance) == NULL);
@@ -217,6 +256,7 @@ ide_tweaks_property_class_init (IdeTweaksPropertyClass *klass)
tweaks_binding_class->get_value = ide_tweaks_property_get_value;
tweaks_binding_class->set_value = ide_tweaks_property_set_value;
+ tweaks_binding_class->get_expected_type = ide_tweaks_property_get_expected_type;
properties[PROP_NAME] =
g_param_spec_string ("name", NULL, NULL,
diff --git a/src/libide/tweaks/ide-tweaks-setting.c b/src/libide/tweaks/ide-tweaks-setting.c
index 5c27cf083..9e786014f 100644
--- a/src/libide/tweaks/ide-tweaks-setting.c
+++ b/src/libide/tweaks/ide-tweaks-setting.c
@@ -149,7 +149,10 @@ ide_tweaks_setting_get_value (IdeTweaksBinding *binding,
g_autoptr(GVariant) variant = g_settings_get_value (settings, key);
if (variant != NULL)
- return g_settings_get_mapping (value, variant, NULL);
+ {
+ g_variant_ref_sink (variant);
+ return g_settings_get_mapping (value, variant, NULL);
+ }
}
return FALSE;
@@ -169,13 +172,49 @@ ide_tweaks_setting_set_value (IdeTweaksBinding *binding,
if ((settings = ide_tweaks_setting_acquire (self, &key, &expected_type)))
{
- g_autoptr(GVariant) variant = g_settings_set_mapping (value, expected_type, NULL);
+ g_autoptr(GVariant) new_value = g_settings_set_mapping (value, expected_type, NULL);
+ g_autoptr(GVariant) old_value = g_settings_get_value (settings, key);
- if (variant != NULL)
- g_settings_set_value (settings, key, variant);
+ if (new_value)
+ g_variant_take_ref (new_value);
+
+ if (new_value && old_value && !g_variant_equal (new_value, old_value))
+ g_settings_set_value (settings, key, new_value);
}
}
+static GType
+ide_tweaks_setting_get_expected_type (IdeTweaksBinding *binding)
+{
+ IdeTweaksSetting *self = IDE_TWEAKS_SETTING (binding);
+
+ if (self->expected_type == NULL)
+ return G_TYPE_INVALID;
+
+#define MAP_VARIANT_TYPE_TO_GTYPE(variant_type, gtype) \
+ G_STMT_START { \
+ if (g_variant_type_equal (self->expected_type, variant_type)) \
+ return gtype; \
+ } G_STMT_END
+
+ /* Just the basics really for GSettings */
+ MAP_VARIANT_TYPE_TO_GTYPE (G_VARIANT_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
+ MAP_VARIANT_TYPE_TO_GTYPE (G_VARIANT_TYPE_BYTE, G_TYPE_UCHAR);
+ MAP_VARIANT_TYPE_TO_GTYPE (G_VARIANT_TYPE_BYTESTRING, G_TYPE_STRING);
+ MAP_VARIANT_TYPE_TO_GTYPE (G_VARIANT_TYPE_DOUBLE, G_TYPE_DOUBLE);
+ MAP_VARIANT_TYPE_TO_GTYPE (G_VARIANT_TYPE_INT32, G_TYPE_INT);
+ MAP_VARIANT_TYPE_TO_GTYPE (G_VARIANT_TYPE_INT64, G_TYPE_INT64);
+ MAP_VARIANT_TYPE_TO_GTYPE (G_VARIANT_TYPE_STRING, G_TYPE_STRING);
+ MAP_VARIANT_TYPE_TO_GTYPE (G_VARIANT_TYPE_STRING_ARRAY, G_TYPE_STRV);
+ MAP_VARIANT_TYPE_TO_GTYPE (G_VARIANT_TYPE_UINT32, G_TYPE_UINT);
+ MAP_VARIANT_TYPE_TO_GTYPE (G_VARIANT_TYPE_UINT64, G_TYPE_UINT64);
+ MAP_VARIANT_TYPE_TO_GTYPE (G_VARIANT_TYPE_VARIANT, G_TYPE_VARIANT);
+
+#undef MAP_VARIANT_TYPE_TO_GTYPE
+
+ return G_TYPE_INVALID;
+}
+
static void
ide_tweaks_setting_dispose (GObject *object)
{
@@ -253,6 +292,7 @@ ide_tweaks_setting_class_init (IdeTweaksSettingClass *klass)
tweaks_binding_class->get_value = ide_tweaks_setting_get_value;
tweaks_binding_class->set_value = ide_tweaks_setting_set_value;
+ tweaks_binding_class->get_expected_type = ide_tweaks_setting_get_expected_type;
properties[PROP_SCHEMA_ID] =
g_param_spec_string ("schema-id", NULL, NULL,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]