[gtk/wip/otte/listview: 12/138] expression: Make property expression allow subexpressions



commit e6af6b3bee53222c9d4eb405240d511d0efb09b1
Author: Benjamin Otte <otte redhat com>
Date:   Sat Nov 23 05:21:17 2019 +0100

    expression: Make property expression allow subexpressions

 gtk/gtkbuilderparser.c     |  50 +++++++++++++++-
 gtk/gtkbuilderprivate.h    |  13 +++-
 gtk/gtkexpression.c        | 144 ++++++++++++++++++++++++++++++++++++++++-----
 gtk/gtkexpression.h        |   1 +
 testsuite/gtk/expression.c |   8 +--
 5 files changed, 190 insertions(+), 26 deletions(-)
---
diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c
index b3e6a59e70..0fd2cd258a 100644
--- a/gtk/gtkbuilderparser.c
+++ b/gtk/gtkbuilderparser.c
@@ -994,6 +994,11 @@ free_expression_info (ExpressionInfo *info)
       g_slist_free_full (info->closure.params, (GDestroyNotify) free_expression_info);
       break;
 
+    case EXPRESSION_PROPERTY:
+      g_clear_pointer (&info->property.expression, free_expression_info);
+      g_free (info->property.property_name);
+      break;
+
     default:
       g_assert_not_reached ();
       break;
@@ -1020,7 +1025,19 @@ check_expression_parent (ParserData *data)
     {
       ExpressionInfo *expr_info = (ExpressionInfo *) common_info;
 
-      return expr_info->expression_type = EXPRESSION_CLOSURE;
+      switch (expr_info->expression_type)
+        {
+        case EXPRESSION_CLOSURE:
+          return TRUE;
+        case EXPRESSION_CONSTANT:
+          return FALSE;
+        case EXPRESSION_PROPERTY:
+          return expr_info->property.expression == NULL;
+        case EXPRESSION_EXPRESSION:
+        default:
+          g_assert_not_reached ();
+          return FALSE;
+        }
     }
 
   return FALSE;
@@ -1173,8 +1190,9 @@ parse_lookup_expression (ParserData   *data,
 
   info = g_slice_new0 (ExpressionInfo);
   info->tag_type = TAG_EXPRESSION;
-  info->expression_type = EXPRESSION_EXPRESSION;
-  info->expression = gtk_property_expression_new (type, property_name);
+  info->expression_type = EXPRESSION_PROPERTY;
+  info->property.this_type = type;
+  info->property.property_name = g_strdup (property_name);
 
   state_push (data, info);
 }
@@ -1250,6 +1268,29 @@ expression_info_construct (GtkBuilder      *builder,
       }
       break;
 
+    case EXPRESSION_PROPERTY:
+      {
+        GtkExpression *expression;
+
+        if (info->property.expression)
+          {
+            expression = expression_info_construct (builder, info->property.expression, error);
+            if (expression == NULL)
+              return NULL;
+            free_expression_info (info->property.expression);
+          }
+        else
+          expression = NULL;
+
+        expression = gtk_property_expression_new (info->property.this_type,
+                                                  expression,
+                                                  info->property.property_name);
+        g_free (info->property.property_name);
+        info->expression_type = EXPRESSION_EXPRESSION;
+        info->expression = expression;
+      }
+      break;
+
     default:
       g_return_val_if_reached (NULL);
     }
@@ -1754,6 +1795,9 @@ end_element (GtkBuildableParseContext  *context,
             case EXPRESSION_CLOSURE:
               expr_info->closure.params = g_slist_prepend (expr_info->closure.params, expression_info);
               break;
+            case EXPRESSION_PROPERTY:
+              expr_info->property.expression = expression_info;
+              break;
             case EXPRESSION_EXPRESSION:
             case EXPRESSION_CONSTANT:
             default:
diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h
index 5160ab535d..56df5f9ecb 100644
--- a/gtk/gtkbuilderprivate.h
+++ b/gtk/gtkbuilderprivate.h
@@ -75,12 +75,14 @@ typedef struct {
   gint col;
 } PropertyInfo;
 
-typedef struct {
+typedef struct _ExpressionInfo ExpressionInfo;
+struct _ExpressionInfo {
   guint tag_type;
   enum {
     EXPRESSION_EXPRESSION,
     EXPRESSION_CONSTANT,
-    EXPRESSION_CLOSURE
+    EXPRESSION_CLOSURE,
+    EXPRESSION_PROPERTY
   } expression_type;
   union {
     GtkExpression *expression;
@@ -95,8 +97,13 @@ typedef struct {
       gboolean swapped;
       GSList *params;
     } closure;
+    struct {
+      GType this_type;
+      char *property_name;
+      ExpressionInfo *expression;
+    } property;
   };
-} ExpressionInfo;
+};
 
 typedef struct {
   guint tag_type;
diff --git a/gtk/gtkexpression.c b/gtk/gtkexpression.c
index 55b84d4205..d4fe4b3a61 100644
--- a/gtk/gtkexpression.c
+++ b/gtk/gtkexpression.c
@@ -223,12 +223,17 @@ struct _GtkPropertyExpression
 {
   GtkExpression parent;
 
+  GtkExpression *expr;
+
   GParamSpec *pspec;
 };
 
 static void
 gtk_property_expression_finalize (GtkExpression *expr)
 {
+  GtkPropertyExpression *self = (GtkPropertyExpression *) expr;
+
+  g_clear_pointer (&self->expr, gtk_expression_unref);
 }
 
 static gboolean
@@ -237,17 +242,50 @@ gtk_property_expression_is_static (GtkExpression *expr)
   return FALSE;
 }
 
+static GObject *
+gtk_property_expression_get_object (GtkPropertyExpression *self,
+                                    gpointer               this)
+{
+  GValue expr_value = G_VALUE_INIT;
+  GObject *object;
+
+  if (self->expr == NULL)
+    return g_object_ref (this);
+
+  if (!gtk_expression_evaluate (self->expr, this, &expr_value))
+    return NULL;
+
+  if (!G_VALUE_HOLDS_OBJECT (&expr_value))
+    {
+      g_value_unset (&expr_value);
+      return NULL;
+    }
+
+  object = g_value_dup_object (&expr_value);
+  g_value_unset (&expr_value);
+  return object;
+}
+
 static gboolean
 gtk_property_expression_evaluate (GtkExpression *expr,
                                   gpointer       this,
                                   GValue        *value)
 {
   GtkPropertyExpression *self = (GtkPropertyExpression *) expr;
+  GObject *object;
 
-  if (!G_TYPE_CHECK_INSTANCE_TYPE (this, self->pspec->owner_type))
+  object = gtk_property_expression_get_object (self, this);
+  if (object == NULL)
     return FALSE;
 
-  g_object_get_property (this, self->pspec->name, value);
+  if (!G_TYPE_CHECK_INSTANCE_TYPE (object, self->pspec->owner_type))
+    {
+      g_object_unref (object);
+      return FALSE;
+    }
+
+  g_object_get_property (object, self->pspec->name, value);
+  g_object_unref (object);
   return TRUE;
 }
 
@@ -257,16 +295,44 @@ struct _GtkPropertyExpressionWatch
   GtkExpressionWatch watch;
 
   GClosure *closure;
+  GObject *this;
+  GtkExpressionWatch *expr_watch; 
 };
 
 static void
-gtk_property_expression_watch (GtkExpression      *expr,
-                               gpointer            this_,
-                               GtkExpressionWatch *watch)
+gtk_property_expression_watch_weak_ref_cb (gpointer  data,
+                                           GObject  *object)
 {
-  GtkPropertyExpressionWatch *pwatch = (GtkPropertyExpressionWatch *) watch;
-  GtkPropertyExpression *self = (GtkPropertyExpression *) expr;
-  GObject *object = this_;
+  GtkPropertyExpressionWatch *pwatch = data;
+
+  pwatch->this = NULL;
+
+  gtk_expression_watch_notify (data);
+}
+
+static void
+gtk_property_expression_watch_destroy_closure (GtkPropertyExpressionWatch *pwatch)
+{
+  if (pwatch->closure == NULL)
+    return;
+
+  g_closure_invalidate (pwatch->closure);
+  g_closure_unref (pwatch->closure);
+  pwatch->closure = NULL;
+}
+
+static void
+gtk_property_expression_watch_create_closure (GtkPropertyExpressionWatch *pwatch)
+{
+  GtkExpressionWatch *watch = (GtkExpressionWatch *) pwatch;
+  GtkPropertyExpression *self = (GtkPropertyExpression *) watch->expression;
+  GObject *object;
+
+  if (pwatch->this == NULL)
+    return;
+  object = gtk_property_expression_get_object (self, pwatch->this);
+  if (object == NULL)
+    return;
 
   pwatch->closure = g_cclosure_new_swap (G_CALLBACK (gtk_expression_watch_notify), pwatch, NULL);
   if (!g_signal_connect_closure_by_id (object,
@@ -277,6 +343,41 @@ gtk_property_expression_watch (GtkExpression      *expr,
     {
       g_assert_not_reached ();
     }
+
+  g_object_unref (object);
+}
+
+static void
+gtk_property_expression_watch_expr_notify_cb (gpointer data)
+{
+  GtkPropertyExpressionWatch *pwatch = data;
+
+  gtk_property_expression_watch_destroy_closure (pwatch);
+  gtk_property_expression_watch_create_closure (pwatch);
+  gtk_expression_watch_notify (data);
+}
+
+static void
+gtk_property_expression_watch (GtkExpression      *expr,
+                               gpointer            this,
+                               GtkExpressionWatch *watch)
+{
+  GtkPropertyExpressionWatch *pwatch = (GtkPropertyExpressionWatch *) watch;
+  GtkPropertyExpression *self = (GtkPropertyExpression *) expr;
+
+  pwatch->this = this;
+  g_object_weak_ref (this, gtk_property_expression_watch_weak_ref_cb, pwatch);
+
+  if (self->expr && !gtk_expression_is_static (self->expr))
+    {
+      pwatch->expr_watch = gtk_expression_watch (self->expr,
+                                                 this,
+                                                 gtk_property_expression_watch_expr_notify_cb,
+                                                 pwatch,
+                                                 NULL);
+    }
+
+  gtk_property_expression_watch_create_closure (pwatch);
 }
 
 static void
@@ -285,8 +386,11 @@ gtk_property_expression_unwatch (GtkExpression      *expr,
 {
   GtkPropertyExpressionWatch *pwatch = (GtkPropertyExpressionWatch *) watch;
 
-  g_closure_invalidate (pwatch->closure);
-  g_closure_unref (pwatch->closure);
+  gtk_property_expression_watch_destroy_closure (pwatch);
+
+  g_clear_pointer (&pwatch->expr_watch, gtk_expression_watch_unwatch);
+  if (pwatch->this)
+    g_object_weak_unref (pwatch->this, gtk_property_expression_watch_weak_ref_cb, pwatch);
 }
 
 static const GtkExpressionClass GTK_PROPERTY_EXPRESSION_CLASS =
@@ -304,20 +408,27 @@ static const GtkExpressionClass GTK_PROPERTY_EXPRESSION_CLASS =
 /**
  * gtk_property_expression_new:
  * @this_type: The type to expect for the this type
+ * @expression: (nullable) (transfer full): Expression to
+ *     evaluate to get the object to query or %NULL to
+ *     query the `this` object
  * @property_name: name of the property
  *
- * Creates an expression that looks up a property on the
- * passed in object when it is evaluated.
- * If gtk_expresson_evaluate() is called with an object of
- * another type, this expression's evaluation will fail.
+ * Creates an expression that looks up a property via the
+ * given @expression or the `this` argument when @expression
+ * is %NULL.
+ *
+ * If the resulting object conforms to @this_type, its property
+ * named @property_name will be queried.
+ * Otherwise, this expression's evaluation will fail.
  *
  * The given @this_type must have a property with @property_name.  
  *
  * Returns: a new #GtkExpression
  **/
 GtkExpression *
-gtk_property_expression_new (GType       this_type,
-                             const char *property_name)
+gtk_property_expression_new (GType          this_type,
+                             GtkExpression *expression,
+                             const char    *property_name)
 {
   GtkPropertyExpression *result;
   GParamSpec *pspec;
@@ -345,6 +456,7 @@ gtk_property_expression_new (GType       this_type,
   result = gtk_expression_alloc (&GTK_PROPERTY_EXPRESSION_CLASS, pspec->value_type);
 
   result->pspec = pspec;
+  result->expr = expression;
 
   return (GtkExpression *) result;
 }
diff --git a/gtk/gtkexpression.h b/gtk/gtkexpression.h
index 77e78bbe75..85afbe76e4 100644
--- a/gtk/gtkexpression.h
+++ b/gtk/gtkexpression.h
@@ -61,6 +61,7 @@ void                    gtk_expression_watch_unwatch            (GtkExpressionWa
 
 GDK_AVAILABLE_IN_ALL
 GtkExpression *         gtk_property_expression_new             (GType                           this_type,
+                                                                 GtkExpression                  *expression,
                                                                  const char                     
*property_name);
 GDK_AVAILABLE_IN_ALL
 GtkExpression *         gtk_constant_expression_new             (GType                           value_type,
diff --git a/testsuite/gtk/expression.c b/testsuite/gtk/expression.c
index f0871853a4..6077315949 100644
--- a/testsuite/gtk/expression.c
+++ b/testsuite/gtk/expression.c
@@ -39,7 +39,7 @@ test_property (void)
   guint counter = 0;
 
   filter = GTK_STRING_FILTER (gtk_string_filter_new ());
-  expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, "search");
+  expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, NULL, "search");
   watch = gtk_expression_watch (expr, filter, inc_counter, &counter, NULL);
 
   g_assert (gtk_expression_evaluate (expr, filter, &value));
@@ -80,9 +80,9 @@ test_closure (void)
   guint counter = 0;
 
   filter = GTK_STRING_FILTER (gtk_string_filter_new ());
-  pexpr[0] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, "search");
-  pexpr[1] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, "ignore-case");
-  pexpr[2] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, "match-substring");
+  pexpr[0] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, NULL, "search");
+  pexpr[1] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, NULL, "ignore-case");
+  pexpr[2] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, NULL, "match-substring");
   expr = gtk_cclosure_expression_new (G_TYPE_STRING,
                                       NULL,
                                       3,


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