[gtk/wip/otte/listview: 32/156] builder: Add <binding> tag



commit c6a76c86c3b1664032325c5c84ee12b1166bf276
Author: Benjamin Otte <otte redhat com>
Date:   Mon Nov 25 08:15:31 2019 +0100

    builder: Add <binding> tag
    
    The tag contains an expression that it then gtk_expression_bind()s to
    the object it is contained in.

 gtk/gtkbuilder.c        |  64 ++++++++++++++--------
 gtk/gtkbuilderparser.c  | 141 ++++++++++++++++++++++++++++++++++++++++++++++--
 gtk/gtkbuilderprivate.h |  20 ++++++-
 3 files changed, 199 insertions(+), 26 deletions(-)
---
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index 33410560b8..efc47863d4 100644
--- a/gtk/gtkbuilder.c
+++ b/gtk/gtkbuilder.c
@@ -218,6 +218,7 @@
 #include "gtkbuildable.h"
 #include "gtkbuilderscopeprivate.h"
 #include "gtkdebug.h"
+#include "gtkexpression.h"
 #include "gtkmain.h"
 #include "gtkintl.h"
 #include "gtkprivate.h"
@@ -676,8 +677,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);
@@ -1004,17 +1019,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)
@@ -1025,26 +1029,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);
         }
+      else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
+        {
+          BindingExpressionInfo *info = l->data;
+          GtkExpression *expression;
 
-      free_binding_info (info, NULL);
+          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 c746d3bb9e..e0f305dbc4 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)
@@ -1414,6 +1492,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)
@@ -1654,6 +1749,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)
@@ -1745,6 +1842,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)
     {
@@ -1812,7 +1933,13 @@ end_element (GtkBuildableParseContext  *context,
       ExpressionInfo *expression_info = state_pop_info (data, ExpressionInfo);
       CommonInfo *parent_info = state_peek_info (data, CommonInfo);
 
-      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;
 
@@ -1963,6 +2090,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 0e9f73b3fd..a5e1f22861 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,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]