[gtk/wip/otte/listview: 1/4] expression: Allow passing a this object to bind()
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/listview: 1/4] expression: Allow passing a this object to bind()
- Date: Sun, 15 Dec 2019 16:38:43 +0000 (UTC)
commit 7111036eb0f3b40344e2e3c72e80983192139b2e
Author: Benjamin Otte <otte redhat com>
Date: Thu Nov 28 02:32:12 2019 +0100
expression: Allow passing a this object to bind()
This gives a bit more control over the arguments passed to expressions.
demos/gtk-demo/listview_clocks.c | 6 +--
gtk/gtkbuilder.c | 38 ++++++++++++++----
gtk/gtkbuilderparser.c | 6 ++-
gtk/gtkbuilderprivate.h | 1 +
gtk/gtkexpression.c | 83 ++++++++++++++++++++++++----------------
gtk/gtkexpression.h | 5 ++-
6 files changed, 94 insertions(+), 45 deletions(-)
---
diff --git a/demos/gtk-demo/listview_clocks.c b/demos/gtk-demo/listview_clocks.c
index b463839051..ae0ad02538 100644
--- a/demos/gtk-demo/listview_clocks.c
+++ b/demos/gtk-demo/listview_clocks.c
@@ -401,7 +401,7 @@ setup_listitem_cb (GtkListItemFactory *factory,
"location");
/* Now create the label and bind the expression to it. */
location_label = gtk_label_new (NULL);
- gtk_expression_bind (expression, location_label, "label");
+ gtk_expression_bind (expression, location_label, "label", location_label);
gtk_container_add (GTK_CONTAINER (box), location_label);
@@ -411,7 +411,7 @@ setup_listitem_cb (GtkListItemFactory *factory,
expression = gtk_expression_ref (clock_expression);
/* Now create the widget and bind the expression to it. */
picture = gtk_picture_new ();
- gtk_expression_bind (expression, picture, "paintable");
+ gtk_expression_bind (expression, picture, "paintable", picture);
gtk_container_add (GTK_CONTAINER (box), picture);
@@ -430,7 +430,7 @@ setup_listitem_cb (GtkListItemFactory *factory,
NULL, NULL);
/* Now create the label and bind the expression to it. */
time_label = gtk_label_new (NULL);
- gtk_expression_bind (expression, time_label, "label");
+ gtk_expression_bind (expression, time_label, "label", time_label);
gtk_container_add (GTK_CONTAINER (box), time_label);
gtk_expression_unref (clock_expression);
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index 518b046110..a8393c44dc 100644
--- a/gtk/gtkbuilder.c
+++ b/gtk/gtkbuilder.c
@@ -1055,11 +1055,13 @@ gtk_builder_create_bindings (GtkBuilder *builder,
BindingInfo *info = l->data;
GObject *source;
- source = _gtk_builder_lookup_object (builder, info->source, info->line, info->col);
+ source = gtk_builder_lookup_object (builder, info->source, info->line, info->col, error);
if (source)
g_object_bind_property (source, info->source_property,
info->target, info->target_pspec->name,
info->flags);
+ else
+ error = NULL;
_free_binding_info (info, NULL);
}
@@ -1067,17 +1069,39 @@ gtk_builder_create_bindings (GtkBuilder *builder,
{
BindingExpressionInfo *info = l->data;
GtkExpression *expression;
+ GObject *object;
- expression = expression_info_construct (builder, info->expr, error);
- if (expression == NULL)
+ if (info->object_name)
+ {
+ object = gtk_builder_lookup_object (builder, info->object_name, info->line, info->col, error);
+ if (object == NULL)
+ {
+ error = NULL;
+ result = FALSE;
+ }
+ }
+ else if (priv->current_object)
{
- g_prefix_error (error, "%s:%d:%d: ", priv->filename, info->line, info->col);
- error = NULL;
- result = FALSE;
+ object = priv->current_object;
}
else
{
- gtk_expression_bind (expression, info->target, info->target_pspec->name);
+ object = info->target;
+ }
+
+ if (object)
+ {
+ expression = expression_info_construct (builder, info->expr, error);
+ if (expression == NULL)
+ {
+ g_prefix_error (error, "%s:%d:%d: ", priv->filename, info->line, info->col);
+ error = NULL;
+ result = FALSE;
+ }
+ else
+ {
+ gtk_expression_bind (expression, info->target, info->target_pspec->name, object);
+ }
}
free_binding_expression_info (info);
diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c
index e0f305dbc4..4bdb0af82b 100644
--- a/gtk/gtkbuilderparser.c
+++ b/gtk/gtkbuilderparser.c
@@ -954,7 +954,8 @@ parse_binding (ParserData *data,
GError **error)
{
BindingExpressionInfo *info;
- const gchar *name = NULL;
+ const char *name = NULL;
+ const char *object_name = NULL;
ObjectInfo *object_info;
GParamSpec *pspec = NULL;
@@ -969,6 +970,7 @@ parse_binding (ParserData *data,
if (!g_markup_collect_attributes (element_name, names, values, error,
G_MARKUP_COLLECT_STRING, "name", &name,
+ G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object",
&object_name,
G_MARKUP_COLLECT_INVALID))
{
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
@@ -1013,6 +1015,7 @@ parse_binding (ParserData *data,
info->tag_type = TAG_BINDING_EXPRESSION;
info->target = NULL;
info->target_pspec = pspec;
+ info->object_name = g_strdup (object_name);
gtk_buildable_parse_context_get_position (&data->ctx, &info->line, &info->col);
state_push (data, info);
@@ -1506,6 +1509,7 @@ free_binding_expression_info (BindingExpressionInfo *info)
{
if (info->expr)
free_expression_info (info->expr);
+ g_free (info->object_name);
g_slice_free (BindingExpressionInfo, info);
}
diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h
index a5e1f22861..8e690e0c88 100644
--- a/gtk/gtkbuilderprivate.h
+++ b/gtk/gtkbuilderprivate.h
@@ -133,6 +133,7 @@ typedef struct
guint tag_type;
GObject *target;
GParamSpec *target_pspec;
+ char *object_name;
ExpressionInfo *expr;
gint line;
gint col;
diff --git a/gtk/gtkexpression.c b/gtk/gtkexpression.c
index 493d29da9d..2cca672b8b 100644
--- a/gtk/gtkexpression.c
+++ b/gtk/gtkexpression.c
@@ -1258,8 +1258,7 @@ gtk_expression_watch_evaluate (GtkExpressionWatch *watch,
typedef struct {
GtkExpressionWatch *watch;
- GtkExpression *expression;
- GObject *object;
+ GObject *target;
GParamSpec *pspec;
} GtkExpressionBind;
@@ -1274,7 +1273,12 @@ invalidate_binds (gpointer unused,
{
GtkExpressionBind *bind = l->data;
- bind->object = NULL;
+ /* This guarantees we neither try to update bindings
+ * (which would wreck havoc because the object is
+ * dispose()ing itself) nor try to destroy bindings
+ * anymore, so destruction can be done in free_binds().
+ */
+ bind->target = NULL;
}
}
@@ -1287,8 +1291,10 @@ free_binds (gpointer data)
{
GtkExpressionBind *bind = l->data;
- bind->object = NULL;
- gtk_expression_watch_unwatch (bind->watch);
+ g_assert (bind->target == NULL);
+ if (bind->watch)
+ gtk_expression_watch_unwatch (bind->watch);
+ g_slice_free (GtkExpressionBind, bind);
}
g_slist_free (data);
}
@@ -1298,19 +1304,30 @@ gtk_expression_bind_free (gpointer data)
{
GtkExpressionBind *bind = data;
- if (bind->object)
+ if (bind->target)
{
GSList *binds;
- binds = g_object_steal_data (bind->object, "gtk-expression-binds");
+ binds = g_object_steal_data (bind->target, "gtk-expression-binds");
binds = g_slist_remove (binds, bind);
if (binds)
- g_object_set_data_full (bind->object, "gtk-expression-binds", binds, free_binds);
+ g_object_set_data_full (bind->target, "gtk-expression-binds", binds, free_binds);
else
- g_object_weak_unref (bind->object, invalidate_binds, NULL);
- }
- gtk_expression_unref (bind->expression);
+ g_object_weak_unref (bind->target, invalidate_binds, NULL);
- g_slice_free (GtkExpressionBind, bind);
+ g_slice_free (GtkExpressionBind, bind);
+ }
+ else
+ {
+ /* If a bind gets unwatched after invalidate_binds() but
+ * before free_binds(), we end up here. This can happen if
+ * the bind was watching itself or if the target's dispose()
+ * function freed the object that was watched.
+ * We make sure we don't destroy the binding or free_binds() will do
+ * bad stuff, but we clear the watch, so free_binds() won't try to
+ * unwatch() it.
+ */
+ bind->watch = NULL;
+ }
}
static void
@@ -1319,29 +1336,31 @@ gtk_expression_bind_notify (gpointer data)
GValue value = G_VALUE_INIT;
GtkExpressionBind *bind = data;
- if (bind->object == NULL)
+ if (bind->target == NULL)
return;
- if (!gtk_expression_evaluate (bind->expression, bind->object, &value))
+ if (!gtk_expression_watch_evaluate (bind->watch, &value))
return;
- g_object_set_property (bind->object, bind->pspec->name, &value);
+ g_object_set_property (bind->target, 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
+ * @target: (transfer none) (type GObject): the target object to bind to
+ * @property: name of the property on @target to bind to
+ * @this_: (transfer none) (type GObject): the this argument for
+ * the evaluation of @self
*
- * Bind @object's property named @property to @self.
+ * Bind @target'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
+ * @target. 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.
+ * If @self's evaluation fails, @target's @property is not updated.
* You can ensure that this doesn't happen by using a fallback
* expression.
*
@@ -1352,44 +1371,44 @@ gtk_expression_bind_notify (gpointer data)
**/
GtkExpressionWatch *
gtk_expression_bind (GtkExpression *self,
- gpointer object,
- const char *property)
+ gpointer target,
+ const char *property,
+ gpointer this_)
{
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 (G_IS_OBJECT (target), NULL);
g_return_val_if_fail (property != NULL, NULL);
- pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), property);
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (target), property);
if (G_UNLIKELY (pspec == NULL))
{
g_critical ("%s: Class '%s' has no property named '%s'",
- G_STRFUNC, G_OBJECT_TYPE_NAME (object), property);
+ G_STRFUNC, G_OBJECT_TYPE_NAME (target), 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));
+ G_STRFUNC, pspec->name, G_OBJECT_TYPE_NAME (target));
return NULL;
}
bind = g_slice_new0 (GtkExpressionBind);
- binds = g_object_steal_data (object, "gtk-expression-binds");
+ binds = g_object_steal_data (target, "gtk-expression-binds");
if (binds == NULL)
- g_object_weak_ref (object, invalidate_binds, NULL);
- bind->expression = self;
- bind->object = object;
+ g_object_weak_ref (target, invalidate_binds, NULL);
+ bind->target = target;
bind->pspec = pspec;
bind->watch = gtk_expression_watch (self,
- object,
+ this_,
gtk_expression_bind_notify,
bind,
gtk_expression_bind_free);
binds = g_slist_prepend (binds, bind);
- g_object_set_data_full (object, "gtk-expression-binds", binds, free_binds);
+ g_object_set_data_full (target, "gtk-expression-binds", binds, free_binds);
gtk_expression_bind_notify (bind);
diff --git a/gtk/gtkexpression.h b/gtk/gtkexpression.h
index bac7762561..6b642a30fe 100644
--- a/gtk/gtkexpression.h
+++ b/gtk/gtkexpression.h
@@ -58,8 +58,9 @@ GtkExpressionWatch * gtk_expression_watch (GtkExpression
GDestroyNotify
user_destroy);
GDK_AVAILABLE_IN_ALL
GtkExpressionWatch * gtk_expression_bind (GtkExpression *self,
- gpointer object,
- const char * property);
+ gpointer target,
+ const char * property,
+ gpointer this_);
GDK_AVAILABLE_IN_ALL
GtkExpressionWatch * gtk_expression_watch_ref (GtkExpressionWatch *watch);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]