expression: Allow passing a this object to bind()

This gives a bit more control over the arguments passed to expressions.
This commit is contained in:
Benjamin Otte 2019-11-28 02:32:12 +01:00 committed by Matthias Clasen
parent 22e6fa3a64
commit b43c8ae646
6 changed files with 94 additions and 45 deletions

View File

@ -401,7 +401,7 @@ setup_listitem_cb (GtkListItemFactory *factory,
"location"); "location");
/* Now create the label and bind the expression to it. */ /* Now create the label and bind the expression to it. */
location_label = gtk_label_new (NULL); location_label = gtk_label_new (NULL);
gtk_expression_bind (expression, location_label, "label"); gtk_expression_bind (expression, location_label, "label", location_label);
gtk_box_append (GTK_BOX (box), location_label); gtk_box_append (GTK_BOX (box), location_label);
@ -411,7 +411,7 @@ setup_listitem_cb (GtkListItemFactory *factory,
expression = gtk_expression_ref (clock_expression); expression = gtk_expression_ref (clock_expression);
/* Now create the widget and bind the expression to it. */ /* Now create the widget and bind the expression to it. */
picture = gtk_picture_new (); picture = gtk_picture_new ();
gtk_expression_bind (expression, picture, "paintable"); gtk_expression_bind (expression, picture, "paintable", picture);
gtk_box_append (GTK_BOX (box), picture); gtk_box_append (GTK_BOX (box), picture);
@ -430,7 +430,7 @@ setup_listitem_cb (GtkListItemFactory *factory,
NULL, NULL); NULL, NULL);
/* Now create the label and bind the expression to it. */ /* Now create the label and bind the expression to it. */
time_label = gtk_label_new (NULL); time_label = gtk_label_new (NULL);
gtk_expression_bind (expression, time_label, "label"); gtk_expression_bind (expression, time_label, "label", time_label);
gtk_box_append (GTK_BOX (box), time_label); gtk_box_append (GTK_BOX (box), time_label);
gtk_expression_unref (clock_expression); gtk_expression_unref (clock_expression);

View File

@ -1064,11 +1064,13 @@ gtk_builder_create_bindings (GtkBuilder *builder,
BindingInfo *info = l->data; BindingInfo *info = l->data;
GObject *source; 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) if (source)
g_object_bind_property (source, info->source_property, g_object_bind_property (source, info->source_property,
info->target, info->target_pspec->name, info->target, info->target_pspec->name,
info->flags); info->flags);
else
error = NULL;
_free_binding_info (info, NULL); _free_binding_info (info, NULL);
} }
@ -1076,17 +1078,39 @@ gtk_builder_create_bindings (GtkBuilder *builder,
{ {
BindingExpressionInfo *info = l->data; BindingExpressionInfo *info = l->data;
GtkExpression *expression; GtkExpression *expression;
GObject *object;
expression = expression_info_construct (builder, info->expr, error); if (info->object_name)
if (expression == NULL)
{ {
g_prefix_error (error, "%s:%d:%d: ", priv->filename, info->line, info->col); object = gtk_builder_lookup_object (builder, info->object_name, info->line, info->col, error);
error = NULL; if (object == NULL)
result = FALSE; {
error = NULL;
result = FALSE;
}
}
else if (priv->current_object)
{
object = priv->current_object;
} }
else 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); free_binding_expression_info (info);

View File

@ -954,7 +954,8 @@ parse_binding (ParserData *data,
GError **error) GError **error)
{ {
BindingExpressionInfo *info; BindingExpressionInfo *info;
const gchar *name = NULL; const char *name = NULL;
const char *object_name = NULL;
ObjectInfo *object_info; ObjectInfo *object_info;
GParamSpec *pspec = NULL; GParamSpec *pspec = NULL;
@ -969,6 +970,7 @@ parse_binding (ParserData *data,
if (!g_markup_collect_attributes (element_name, names, values, error, if (!g_markup_collect_attributes (element_name, names, values, error,
G_MARKUP_COLLECT_STRING, "name", &name, G_MARKUP_COLLECT_STRING, "name", &name,
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object", &object_name,
G_MARKUP_COLLECT_INVALID)) G_MARKUP_COLLECT_INVALID))
{ {
_gtk_builder_prefix_error (data->builder, &data->ctx, error); _gtk_builder_prefix_error (data->builder, &data->ctx, error);
@ -1013,6 +1015,7 @@ parse_binding (ParserData *data,
info->tag_type = TAG_BINDING_EXPRESSION; info->tag_type = TAG_BINDING_EXPRESSION;
info->target = NULL; info->target = NULL;
info->target_pspec = pspec; info->target_pspec = pspec;
info->object_name = g_strdup (object_name);
gtk_buildable_parse_context_get_position (&data->ctx, &info->line, &info->col); gtk_buildable_parse_context_get_position (&data->ctx, &info->line, &info->col);
state_push (data, info); state_push (data, info);
@ -1538,6 +1541,7 @@ free_binding_expression_info (BindingExpressionInfo *info)
{ {
if (info->expr) if (info->expr)
free_expression_info (info->expr); free_expression_info (info->expr);
g_free (info->object_name);
g_slice_free (BindingExpressionInfo, info); g_slice_free (BindingExpressionInfo, info);
} }

View File

@ -133,6 +133,7 @@ typedef struct
guint tag_type; guint tag_type;
GObject *target; GObject *target;
GParamSpec *target_pspec; GParamSpec *target_pspec;
char *object_name;
ExpressionInfo *expr; ExpressionInfo *expr;
gint line; gint line;
gint col; gint col;

View File

@ -1287,8 +1287,7 @@ gtk_expression_watch_evaluate (GtkExpressionWatch *watch,
typedef struct { typedef struct {
GtkExpressionWatch *watch; GtkExpressionWatch *watch;
GtkExpression *expression; GObject *target;
GObject *object;
GParamSpec *pspec; GParamSpec *pspec;
} GtkExpressionBind; } GtkExpressionBind;
@ -1303,7 +1302,12 @@ invalidate_binds (gpointer unused,
{ {
GtkExpressionBind *bind = l->data; 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;
} }
} }
@ -1316,8 +1320,10 @@ free_binds (gpointer data)
{ {
GtkExpressionBind *bind = l->data; GtkExpressionBind *bind = l->data;
bind->object = NULL; g_assert (bind->target == NULL);
gtk_expression_watch_unwatch (bind->watch); if (bind->watch)
gtk_expression_watch_unwatch (bind->watch);
g_slice_free (GtkExpressionBind, bind);
} }
g_slist_free (data); g_slist_free (data);
} }
@ -1327,19 +1333,30 @@ gtk_expression_bind_free (gpointer data)
{ {
GtkExpressionBind *bind = data; GtkExpressionBind *bind = data;
if (bind->object) if (bind->target)
{ {
GSList *binds; 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); binds = g_slist_remove (binds, bind);
if (binds) 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 else
g_object_weak_unref (bind->object, invalidate_binds, NULL); g_object_weak_unref (bind->target, invalidate_binds, NULL);
}
gtk_expression_unref (bind->expression);
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 static void
@ -1348,29 +1365,31 @@ gtk_expression_bind_notify (gpointer data)
GValue value = G_VALUE_INIT; GValue value = G_VALUE_INIT;
GtkExpressionBind *bind = data; GtkExpressionBind *bind = data;
if (bind->object == NULL) if (bind->target == NULL)
return; return;
if (!gtk_expression_evaluate (bind->expression, bind->object, &value)) if (!gtk_expression_watch_evaluate (bind->watch, &value))
return; 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); g_value_unset (&value);
} }
/** /**
* gtk_expression_bind: * gtk_expression_bind:
* @self: (transfer full): a #GtkExpression * @self: (transfer full): a #GtkExpression
* @object: (transfer none) (type GObject): the object to bind * @target: (transfer none) (type GObject): the target object to bind to
* @property: name of the property 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 * 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. * 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 * You can ensure that this doesn't happen by using a fallback
* expression. * expression.
* *
@ -1381,44 +1400,44 @@ gtk_expression_bind_notify (gpointer data)
**/ **/
GtkExpressionWatch * GtkExpressionWatch *
gtk_expression_bind (GtkExpression *self, gtk_expression_bind (GtkExpression *self,
gpointer object, gpointer target,
const char *property) const char *property,
gpointer this_)
{ {
GtkExpressionBind *bind; GtkExpressionBind *bind;
GParamSpec *pspec; GParamSpec *pspec;
GSList *binds; GSList *binds;
g_return_val_if_fail (GTK_IS_EXPRESSION (self), NULL); 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); 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)) if (G_UNLIKELY (pspec == NULL))
{ {
g_critical ("%s: Class '%s' has no property named '%s'", 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; return NULL;
} }
if (G_UNLIKELY ((pspec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) != G_PARAM_WRITABLE)) 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_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; return NULL;
} }
bind = g_slice_new0 (GtkExpressionBind); 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) if (binds == NULL)
g_object_weak_ref (object, invalidate_binds, NULL); g_object_weak_ref (target, invalidate_binds, NULL);
bind->expression = self; bind->target = target;
bind->object = object;
bind->pspec = pspec; bind->pspec = pspec;
bind->watch = gtk_expression_watch (self, bind->watch = gtk_expression_watch (self,
object, this_,
gtk_expression_bind_notify, gtk_expression_bind_notify,
bind, bind,
gtk_expression_bind_free); gtk_expression_bind_free);
binds = g_slist_prepend (binds, bind); 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_unref (self); gtk_expression_unref (self);

View File

@ -65,8 +65,9 @@ GtkExpressionWatch * gtk_expression_watch (GtkExpression
GDestroyNotify user_destroy); GDestroyNotify user_destroy);
GDK_AVAILABLE_IN_ALL GDK_AVAILABLE_IN_ALL
GtkExpressionWatch * gtk_expression_bind (GtkExpression *self, GtkExpressionWatch * gtk_expression_bind (GtkExpression *self,
gpointer object, gpointer target,
const char * property); const char * property,
gpointer this_);
GDK_AVAILABLE_IN_ALL GDK_AVAILABLE_IN_ALL
GtkExpressionWatch * gtk_expression_watch_ref (GtkExpressionWatch *watch); GtkExpressionWatch * gtk_expression_watch_ref (GtkExpressionWatch *watch);