From d1d1708c870a341f7beeaae32a777a9c8a00cee2 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sun, 24 Nov 2019 08:05:41 +0100 Subject: [PATCH] expression: Add gtk_expression_bind() Add a simple way to bind expressions to object properties. This is essentially the thing to replace g_object_bind_property(). --- gtk/gtkexpression.c | 117 ++++++++++++++++++++++++++++++++++++++++++++ gtk/gtkexpression.h | 4 ++ 2 files changed, 121 insertions(+) diff --git a/gtk/gtkexpression.c b/gtk/gtkexpression.c index 81afae8ac7..e7cd1fcc84 100644 --- a/gtk/gtkexpression.c +++ b/gtk/gtkexpression.c @@ -1256,3 +1256,120 @@ gtk_expression_watch_evaluate (GtkExpressionWatch *watch, return gtk_expression_evaluate (watch->expression, watch->this, value); } +typedef struct { + GtkExpressionWatch *watch; + GtkExpression *expression; + GObject *object; + GParamSpec *pspec; +} GtkExpressionBind; + +static void +free_binds (gpointer data) +{ + GSList *l; + + for (l = data; l; l = l->next) + { + GtkExpressionBind *bind = l->data; + + bind->object = NULL; + gtk_expression_watch_unwatch (bind->watch); + } + g_slist_free (data); +} + +static void +gtk_expression_bind_free (gpointer data) +{ + GtkExpressionBind *bind = data; + + if (bind->object) + { + GSList *binds; + binds = g_object_steal_data (bind->object, "gtk-expression-binds"); + binds = g_slist_remove (binds, bind); + if (binds) + g_object_set_data_full (bind->object, "gtk-expression-binds", binds, free_binds); + } + gtk_expression_unref (bind->expression); + + g_slice_free (GtkExpressionBind, bind); +} + +static void +gtk_expression_bind_notify (gpointer data) +{ + GValue value = G_VALUE_INIT; + GtkExpressionBind *bind = data; + + if (!gtk_expression_evaluate (bind->expression, bind->object, &value)) + return; + + g_object_set_property (bind->object, bind->pspec->name, &value); + g_value_unset (&value); +} + +/** + * gtk_expression_bind: + * @self: (transfer full): a #GtkExpression + * @object: (transfer none) (type GObject): the object to bind + * @property: name of the property to bind to + * + * Bind @object's property named @property to @self. + * + * The value that @self evaluates to is set via g_object_set() on + * @object. This is repeated whenever @self changes to ensure that + * the object's property stays synchronized with @self. + * + * If @self's evaluation fails, @object's @property is not updated. + * You can ensure that this doesn't happen by using a fallback + * expression. + * + * Note that this function takes ownership of @self. If you want + * to keep it around, you should gtk_expression_ref() it beforehand. + * + * Returns: (transfer none): a #GtkExpressionWatch + **/ +GtkExpressionWatch * +gtk_expression_bind (GtkExpression *self, + gpointer object, + const char *property) +{ + GtkExpressionBind *bind; + GParamSpec *pspec; + GSList *binds; + + g_return_val_if_fail (GTK_IS_EXPRESSION (self), NULL); + g_return_val_if_fail (G_IS_OBJECT (object), NULL); + g_return_val_if_fail (property != NULL, NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), property); + if (G_UNLIKELY (pspec == NULL)) + { + g_critical ("%s: Class '%s' has no property named '%s'", + G_STRFUNC, G_OBJECT_TYPE_NAME (object), property); + return NULL; + } + if (G_UNLIKELY ((pspec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) != G_PARAM_WRITABLE)) + { + g_critical ("%s: property '%s' of class '%s' is not writable", + G_STRFUNC, pspec->name, G_OBJECT_TYPE_NAME (object)); + return NULL; + } + + bind = g_slice_new0 (GtkExpressionBind); + bind->expression = self; + bind->object = object; + bind->pspec = pspec; + bind->watch = gtk_expression_watch (self, + object, + gtk_expression_bind_notify, + bind, + gtk_expression_bind_free); + binds = g_object_steal_data (object, "gtk-expression-binds"); + binds = g_slist_prepend (binds, bind); + g_object_set_data_full (object, "gtk-expression-binds", binds, free_binds); + + gtk_expression_bind_notify (bind); + + return bind->watch; +} diff --git a/gtk/gtkexpression.h b/gtk/gtkexpression.h index 0ffde58aaf..bac7762561 100644 --- a/gtk/gtkexpression.h +++ b/gtk/gtkexpression.h @@ -56,6 +56,10 @@ GtkExpressionWatch * gtk_expression_watch (GtkExpression GtkExpressionNotify notify, gpointer user_data, GDestroyNotify user_destroy); +GDK_AVAILABLE_IN_ALL +GtkExpressionWatch * gtk_expression_bind (GtkExpression *self, + gpointer object, + const char * property); GDK_AVAILABLE_IN_ALL GtkExpressionWatch * gtk_expression_watch_ref (GtkExpressionWatch *watch);