From 934bfc8887bc62e3f8131b691d889236ca32e6f6 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Mon, 25 Nov 2019 08:15:31 +0100 Subject: [PATCH] builder: Add tag The tag contains an expression that it then gtk_expression_bind()s to the object it is contained in. --- gtk/gtkbuilder.c | 66 ++++++++++++------- gtk/gtkbuilderparser.c | 141 ++++++++++++++++++++++++++++++++++++++-- gtk/gtkbuilderprivate.h | 20 +++++- 3 files changed, 200 insertions(+), 27 deletions(-) diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c index 244f8e8a9a..5b5a66eb90 100644 --- a/gtk/gtkbuilder.c +++ b/gtk/gtkbuilder.c @@ -219,6 +219,7 @@ #include "gtkbuildable.h" #include "gtkbuilderscopeprivate.h" #include "gtkdebug.h" +#include "gtkexpression.h" #include "gtkmain.h" #include "gtkicontheme.h" #include "gtkintl.h" @@ -685,8 +686,22 @@ gtk_builder_take_bindings (GtkBuilder *builder, for (l = bindings; l; l = l->next) { - BindingInfo *info = l->data; - info->target = target; + CommonInfo *common_info = l->data; + + if (common_info->tag_type == TAG_BINDING) + { + BindingInfo *info = l->data; + info->target = target; + } + else if (common_info->tag_type == TAG_BINDING_EXPRESSION) + { + BindingExpressionInfo *info = l->data; + info->target = target; + } + else + { + g_assert_not_reached (); + } } priv->bindings = g_slist_concat (priv->bindings, bindings); @@ -1013,17 +1028,6 @@ gtk_builder_apply_delayed_properties (GtkBuilder *builder, return result; } -static inline void -free_binding_info (gpointer data, - gpointer user) -{ - BindingInfo *info = data; - - g_free (info->source); - g_free (info->source_property); - g_slice_free (BindingInfo, data); -} - static inline gboolean gtk_builder_create_bindings (GtkBuilder *builder, GError **error) @@ -1034,26 +1038,44 @@ gtk_builder_create_bindings (GtkBuilder *builder, for (l = priv->bindings; l; l = l->next) { - BindingInfo *info = l->data; - GObject *source; + CommonInfo *common_info = l->data; - if (result) + if (common_info->tag_type == TAG_BINDING) { - source = gtk_builder_lookup_object (builder, info->source, info->line, info->col, error); + BindingInfo *info = l->data; + GObject *source; + + source = _gtk_builder_lookup_object (builder, info->source, info->line, info->col); if (source) g_object_bind_property (source, info->source_property, info->target, info->target_pspec->name, info->flags); - else - result = FALSE; - } - free_binding_info (info, NULL); + _free_binding_info (info, NULL); + } + else if (common_info->tag_type == TAG_BINDING_EXPRESSION) + { + BindingExpressionInfo *info = l->data; + GtkExpression *expression; + + 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); + } + + free_binding_expression_info (info); + } } g_slist_free (priv->bindings); priv->bindings = NULL; - return result; } diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c index 0b98d68ae6..d6e4b87e95 100644 --- a/gtk/gtkbuilderparser.c +++ b/gtk/gtkbuilderparser.c @@ -913,7 +913,8 @@ parse_property (ParserData *data, { BindingInfo *binfo; - binfo = g_slice_new (BindingInfo); + binfo = g_slice_new0 (BindingInfo); + binfo->tag_type = TAG_BINDING; binfo->target = NULL; binfo->target_pspec = pspec; binfo->source = g_strdup (bind_source); @@ -945,6 +946,78 @@ parse_property (ParserData *data, state_push (data, info); } +static void +parse_binding (ParserData *data, + const gchar *element_name, + const gchar **names, + const gchar **values, + GError **error) +{ + BindingExpressionInfo *info; + const gchar *name = NULL; + ObjectInfo *object_info; + GParamSpec *pspec = NULL; + + object_info = state_peek_info (data, ObjectInfo); + if (!object_info || + !(object_info->tag_type == TAG_OBJECT || + object_info->tag_type == TAG_TEMPLATE)) + { + error_invalid_tag (data, element_name, NULL, error); + return; + } + + if (!g_markup_collect_attributes (element_name, names, values, error, + G_MARKUP_COLLECT_STRING, "name", &name, + G_MARKUP_COLLECT_INVALID)) + { + _gtk_builder_prefix_error (data->builder, &data->ctx, error); + return; + } + + pspec = g_object_class_find_property (object_info->oclass, name); + + if (!pspec) + { + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_PROPERTY, + "Invalid property: %s.%s", + g_type_name (object_info->type), name); + _gtk_builder_prefix_error (data->builder, &data->ctx, error); + return; + } + else if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) + { + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_PROPERTY, + "%s.%s is a construct-only property", + g_type_name (object_info->type), name); + _gtk_builder_prefix_error (data->builder, &data->ctx, error); + return; + } + else if (!(pspec->flags & G_PARAM_WRITABLE)) + { + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_PROPERTY, + "%s.%s is a non-writable property", + g_type_name (object_info->type), name); + _gtk_builder_prefix_error (data->builder, &data->ctx, error); + return; + } + + + info = g_slice_new0 (BindingExpressionInfo); + info->tag_type = TAG_BINDING_EXPRESSION; + info->target = NULL; + info->target_pspec = pspec; + gtk_buildable_parse_context_get_position (&data->ctx, &info->line, &info->col); + + state_push (data, info); +} + static void free_property_info (PropertyInfo *info) { @@ -1005,8 +1078,13 @@ check_expression_parent (ParserData *data) return G_PARAM_SPEC_VALUE_TYPE (prop_info->pspec) == GTK_TYPE_EXPRESSION; } + else if (common_info->tag_type == TAG_BINDING_EXPRESSION) + { + BindingExpressionInfo *expr_info = (BindingExpressionInfo *) common_info; - if (common_info->tag_type == TAG_EXPRESSION) + return expr_info->expr == NULL; + } + else if (common_info->tag_type == TAG_EXPRESSION) { ExpressionInfo *expr_info = (ExpressionInfo *) common_info; @@ -1194,7 +1272,7 @@ parse_lookup_expression (ParserData *data, state_push (data, info); } -static GtkExpression * +GtkExpression * expression_info_construct (GtkBuilder *builder, ExpressionInfo *info, GError **error) @@ -1446,6 +1524,23 @@ _free_signal_info (SignalInfo *info, g_slice_free (SignalInfo, info); } +void +_free_binding_info (BindingInfo *info, + gpointer user) +{ + g_free (info->source); + g_free (info->source_property); + g_slice_free (BindingInfo, info); +} + +void +free_binding_expression_info (BindingExpressionInfo *info) +{ + if (info->expr) + free_expression_info (info->expr); + g_slice_free (BindingExpressionInfo, info); +} + static void free_requires_info (RequiresInfo *info, gpointer user_data) @@ -1686,6 +1781,8 @@ start_element (GtkBuildableParseContext *context, } else if (strcmp (element_name, "property") == 0) parse_property (data, element_name, names, values, error); + else if (strcmp (element_name, "binding") == 0) + parse_binding (data, element_name, names, values, error); else if (strcmp (element_name, "child") == 0) parse_child (data, element_name, names, values, error); else if (strcmp (element_name, "signal") == 0) @@ -1777,6 +1874,30 @@ end_element (GtkBuildableParseContext *context, else g_assert_not_reached (); } + else if (strcmp (element_name, "binding") == 0) + { + BindingExpressionInfo *binfo = state_pop_info (data, BindingExpressionInfo); + CommonInfo *info = state_peek_info (data, CommonInfo); + + g_assert (info != NULL); + + if (binfo->expr == NULL) + { + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_TAG, + "Binding tag requires an expression"); + free_binding_expression_info (binfo); + } + else if (info->tag_type == TAG_OBJECT || + info->tag_type == TAG_TEMPLATE) + { + ObjectInfo *object_info = (ObjectInfo*)info; + object_info->bindings = g_slist_prepend (object_info->bindings, binfo); + } + else + g_assert_not_reached (); + } else if (strcmp (element_name, "object") == 0 || strcmp (element_name, "template") == 0) { @@ -1845,7 +1966,13 @@ end_element (GtkBuildableParseContext *context, CommonInfo *parent_info = state_peek_info (data, CommonInfo); g_assert (parent_info != NULL); - if (parent_info->tag_type == TAG_PROPERTY) + if (parent_info->tag_type == TAG_BINDING_EXPRESSION) + { + BindingExpressionInfo *expr_info = (BindingExpressionInfo *) parent_info; + + expr_info->expr = expression_info; + } + else if (parent_info->tag_type == TAG_PROPERTY) { PropertyInfo *prop_info = (PropertyInfo *) parent_info; @@ -1996,6 +2123,12 @@ free_info (CommonInfo *info) case TAG_CHILD: free_child_info ((ChildInfo *)info); break; + case TAG_BINDING: + _free_binding_info ((BindingInfo *)info, NULL); + break; + case TAG_BINDING_EXPRESSION: + free_binding_expression_info ((BindingExpressionInfo *) info); + break; case TAG_PROPERTY: free_property_info ((PropertyInfo *)info); break; diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h index 32227c10f1..506a082887 100644 --- a/gtk/gtkbuilderprivate.h +++ b/gtk/gtkbuilderprivate.h @@ -25,7 +25,8 @@ enum { TAG_PROPERTY, - TAG_MENU, + TAG_BINDING, + TAG_BINDING_EXPRESSION, TAG_REQUIRES, TAG_OBJECT, TAG_CHILD, @@ -117,6 +118,7 @@ typedef struct { typedef struct { + guint tag_type; GObject *target; GParamSpec *target_pspec; gchar *source; @@ -126,6 +128,16 @@ typedef struct gint col; } BindingInfo; +typedef struct +{ + guint tag_type; + GObject *target; + GParamSpec *target_pspec; + ExpressionInfo *expr; + gint line; + gint col; +} BindingExpressionInfo; + typedef struct { guint tag_type; gchar *library; @@ -212,6 +224,12 @@ gboolean _gtk_builder_finish (GtkBuilder *builder, GError **error); void _free_signal_info (SignalInfo *info, gpointer user_data); +void _free_binding_info (BindingInfo *info, + gpointer user_data); +void free_binding_expression_info (BindingExpressionInfo *info); +GtkExpression * expression_info_construct (GtkBuilder *builder, + ExpressionInfo *info, + GError **error); /* Internal API which might be made public at some point */ gboolean _gtk_builder_boolean_from_string (const gchar *string,