[gtk/wip/baedert/parser: 3/3] foo



commit 791e95c604a293cb15eb8a806e21d92a791fe0c8
Author: Timm Bäder <mail baedert org>
Date:   Tue Mar 3 14:40:18 2020 +0100

    foo

 gtk/css/gtkcsstokenizer.c        |   1 +
 gtk/gtkbuildable.h               |  30 +--
 gtk/gtkbuildercssparser.c        | 523 +++++++++++++++++++++++++++++++++------
 gtk/gtkbuildercssparserprivate.h |   9 +-
 gtk/gtkcombobox.cssui            |  45 ++++
 gtk/gtklockbutton.cssui          |  33 +--
 gtk/gtkstatusbar.cssui           |  26 ++
 gtk/gtkvolumebutton.cssui        |  14 ++
 gtk/gtkwidget.c                  |  89 +++++--
 9 files changed, 640 insertions(+), 130 deletions(-)
---
diff --git a/gtk/css/gtkcsstokenizer.c b/gtk/css/gtkcsstokenizer.c
index ec965eb420..bfb4bf7e30 100644
--- a/gtk/css/gtkcsstokenizer.c
+++ b/gtk/css/gtkcsstokenizer.c
@@ -412,6 +412,7 @@ gtk_css_token_print (const GtkCssToken *token,
       break;
 
     case GTK_CSS_TOKEN_EOF:
+      g_string_append (string, "EOF");
       break;
 
     case GTK_CSS_TOKEN_WHITESPACE:
diff --git a/gtk/gtkbuildable.h b/gtk/gtkbuildable.h
index 36bbed636b..81cc5cf317 100644
--- a/gtk/gtkbuildable.h
+++ b/gtk/gtkbuildable.h
@@ -25,9 +25,6 @@
 
 #include <gtk/gtkbuilder.h>
 
-// TODO FUCK
-#include "css/gtkcssparserprivate.h"
-
 G_BEGIN_DECLS
 
 
@@ -47,24 +44,14 @@ struct _GtkCssBuildableIface
 {
   GTypeInterface g_iface;
 
-  /* We set one value on the buildable TODO: Take multiple?*/
-  gboolean    (* set_gvalue)   (GtkCssBuildable *self,
-                                const char      *property_name,
-                                size_t           property_name_len,
-                                const GValue    *value);
-
-  /* Add child objects to the buildable */
-  gboolean    (* add_children) (GtkCssBuildable  *self,
-                                const char       *property_name,
-                                size_t            property_name_len,
-                                int               n_children,
-                                GObject         **children);
-
+  void          (* set_name)               (GtkCssBuildable    *self,
+                                            const char         *name);
 
-  gboolean    (* parse_declaration) (GtkCssBuildable *self,
-                                     GtkCssParser    *parser,
-                                     const char      *decl_name,
-                                     size_t           decl_name_len);
+  gboolean      (* set_property)           (GtkCssBuildable    *self,
+                                            const char         *prop_name,
+                                            size_t              prop_name_len,
+                                            GType               value_type,
+                                            gpointer            value);
 };
 
 
@@ -72,9 +59,6 @@ GDK_AVAILABLE_IN_ALL
 GType     gtk_css_buildable_get_type               (void) G_GNUC_CONST;
 
 
-
-
-
 #define GTK_TYPE_BUILDABLE            (gtk_buildable_get_type ())
 #define GTK_BUILDABLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_BUILDABLE, GtkBuildable))
 #define GTK_BUILDABLE_CLASS(obj)      (G_TYPE_CHECK_CLASS_CAST ((obj), GTK_TYPE_BUILDABLE, 
GtkBuildableIface))
diff --git a/gtk/gtkbuildercssparser.c b/gtk/gtkbuildercssparser.c
index cff05edabc..bb03e37a74 100644
--- a/gtk/gtkbuildercssparser.c
+++ b/gtk/gtkbuildercssparser.c
@@ -1,23 +1,315 @@
 
 
 #include "gtkbuildercssparserprivate.h"
+#include "gtkbuilderscopeprivate.h"
 #include "gtkbuildable.h"
 
+static GObject *          parse_object     (GtkBuilderCssParser *self,
+                                            GObject             *template_object);
+
+typedef struct {
+  guint signal_id;
+  GQuark signal_detail;
+  char *signal_name; // TODO: Remove?
+  char *handler_name;
+  guint after: 1;
+  guint swapped: 1;
+} SignalConnectionData;
+
 GtkBuilderCssParser *
 gtk_builder_css_parser_new (void)
 {
-  return g_new0 (GtkBuilderCssParser, 1);
+  GtkBuilderCssParser *self = g_new0 (GtkBuilderCssParser, 1);
+
+  self->object_table  = g_hash_table_new (g_str_hash,
+                                          g_str_equal);
+
+  return self;
+}
+
+static guint
+translated_string_parse_func (GtkCssParser *parser,
+                              guint         arg,
+                              gpointer      data)
+{
+  char **str = data;
+  g_assert (arg == 0);
+
+  *str = g_strdup (gtk_css_parser_get_token (parser)->string.string);
+
+  return 0;
+}
+
+static SignalConnectionData *
+parse_signals (GtkBuilderCssParser *self,
+               GType                object_type,
+               guint               *out_n_connections)
+{
+  GtkCssParser *parser = self->css_parser;
+  GArray *connection_data;
+
+  if (!gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COLON))
+    {
+      gtk_css_parser_error_syntax (parser, "Expected colon after signal keyword");
+      *out_n_connections = 0;
+      return NULL;
+    }
+
+  connection_data = g_array_new (FALSE, TRUE, sizeof (SignalConnectionData));
+  gtk_css_parser_end_block_prelude (parser);
+  while (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF))
+    {
+      const GtkCssToken *token = gtk_css_parser_get_token (parser);
+      SignalConnectionData connection;
+
+      if (token->type == GTK_CSS_TOKEN_IDENT ||
+          token->type == GTK_CSS_TOKEN_STRING)
+        {
+          connection.signal_name = g_strdup (token->string.string);
+          if (!g_signal_parse_name (token->string.string, object_type,
+                                    &connection.signal_id,
+                                    &connection.signal_detail,
+                                    FALSE))
+            {
+              // TODO: Check for proper signal detail
+              gtk_css_parser_error_syntax (parser, "Signal '%s' is invalid for type '%s'",
+                                           token->string.string,
+                                           g_type_name (object_type));
+              goto next_signal;
+            }
+        }
+      else
+        {
+          gtk_css_parser_error_syntax (parser, "Expected signal name");
+          goto next_signal;
+        }
+
+      gtk_css_parser_start_semicolon_block (parser, GTK_CSS_TOKEN_OPEN_CURLY);
+
+      gtk_css_parser_consume_token (parser); /* Skip signal name */
+      if (!gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COLON))
+        {
+          gtk_css_parser_error_syntax (parser, "Expected colon after signal name, but got %s",
+                                       gtk_css_token_to_string (gtk_css_parser_get_token (parser)));
+          goto next_signal;
+        }
+
+      /* Now parse the actual signal */
+      if (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_STRING))
+        {
+          token = gtk_css_parser_get_token (parser);
+        }
+      else
+        {
+          gtk_css_parser_end_block_prelude (parser);
+
+          // TODO Implement other signal stuff, like swapped.
+          connection.after = FALSE;
+          connection.swapped = FALSE;
+
+          if (!gtk_css_parser_has_ident (parser, "handler"))
+            {
+              gtk_css_parser_error_syntax (parser, "Expected 'handler'");
+              goto next_signal;
+            }
+          gtk_css_parser_consume_token (parser);
+          if (!gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COLON))
+            {
+              gtk_css_parser_error_syntax (parser, "Expected colon");
+              goto next_signal;
+            }
+          token = gtk_css_parser_get_token (parser);
+        }
+      connection.handler_name = g_strdup (token->string.string);
+
+      g_array_append_val (connection_data, connection);
+
+next_signal:
+      gtk_css_parser_end_block (parser); /* Signal block */
+    }
+
+  *out_n_connections = connection_data->len;
+  return (SignalConnectionData *)g_array_free (connection_data, FALSE);
+}
+
+static gboolean
+parse_property (GtkBuilderCssParser *self,
+                GParamSpec          *pspec,
+                GValue              *out_value)
+{
+  GtkCssParser *parser = self->css_parser;
+
+  if (pspec->value_type == G_TYPE_BOOLEAN)
+    {
+      const GtkCssToken *token = gtk_css_parser_get_token (parser);
+
+      if (token->type != GTK_CSS_TOKEN_IDENT)
+        {
+          gtk_css_parser_error_syntax (parser, "Invalid boolean value: '%s'",
+                                       token->string.string);
+          return FALSE;
+        }
+
+      if (strcmp (token->string.string, "true") == 0)
+        {
+          g_value_init (out_value, G_TYPE_BOOLEAN);
+          g_value_set_boolean (out_value, TRUE);
+        }
+      else if (strcmp (token->string.string, "false") == 0)
+        {
+          g_value_init (out_value, G_TYPE_BOOLEAN);
+          g_value_set_boolean (out_value, FALSE);
+        }
+      else
+        {
+          gtk_css_parser_error_syntax (parser, "Invalid boolean value: '%s'",
+                                       token->string.string);
+          return FALSE;
+        }
+
+      gtk_css_parser_consume_token (parser);
+    }
+  else if (pspec->value_type == G_TYPE_STRING)
+    {
+      const GtkCssToken *token = gtk_css_parser_get_token (parser);
+
+      if (gtk_css_parser_has_function (parser, "_"))
+        {
+          char *string;
+
+          gtk_css_parser_consume_function (parser, 1, 1, translated_string_parse_func, &string);
+          // TODO: Imlpement translation stuff
+
+          g_value_init (out_value, G_TYPE_STRING);
+          g_value_take_string (out_value, string);
+        }
+      else if (token->type == GTK_CSS_TOKEN_STRING)
+        {
+          g_value_init (out_value, G_TYPE_STRING);
+          g_value_set_string (out_value, token->string.string);
+        }
+      else
+        {
+          gtk_css_parser_error_syntax (parser, "Expected a string value");
+          return FALSE;
+        }
+    }
+  else if (pspec->value_type == G_TYPE_FLOAT ||
+           pspec->value_type == G_TYPE_DOUBLE ||
+           pspec->value_type == G_TYPE_INT)
+    {
+      double d;
+
+      if (!gtk_css_parser_consume_number (parser, &d))
+        {
+          gtk_css_parser_error_syntax (parser, "Expected a number");
+          return FALSE;
+        }
+      // TODO: We should probably handle int differently, so we show a warning when
+      //   finding a float/double?
+
+      g_value_init (out_value, pspec->value_type);
+      if (pspec->value_type == G_TYPE_FLOAT)
+        g_value_set_float (out_value, d);
+      else if (pspec->value_type == G_TYPE_DOUBLE)
+        g_value_set_double (out_value, d);
+      else if (pspec->value_type == G_TYPE_INT)
+        g_value_set_int (out_value, d);
+      else
+        g_assert_not_reached ();
+    }
+  else if (G_TYPE_IS_ENUM (pspec->value_type))
+    {
+      const GtkCssToken *token = gtk_css_parser_get_token (parser);
+      const GEnumValue *enum_value;
+      GEnumClass *enum_class;
+
+      if (token->type != GTK_CSS_TOKEN_IDENT)
+        {
+          gtk_css_parser_error_syntax (parser, "Expected an enum value name");
+          return FALSE;
+        }
+
+      enum_class = g_type_class_ref (pspec->value_type);
+      enum_value = g_enum_get_value_by_nick (enum_class, token->string.string);
+
+      g_value_init (out_value, pspec->value_type);
+      g_value_set_enum (out_value, enum_value->value);
+      g_type_class_unref (enum_class);
+    }
+  else if (pspec->value_type == G_TYPE_STRV)
+    {
+      const GtkCssToken *token = gtk_css_parser_get_token (parser);
+      GPtrArray *strings = g_ptr_array_sized_new (16);
+
+      while (token->type != GTK_CSS_TOKEN_EOF)
+        {
+          if (token->type != GTK_CSS_TOKEN_STRING)
+            {
+              gtk_css_parser_error_syntax (parser, "Expected a string");
+              return FALSE;
+            }
+
+          g_ptr_array_add (strings, gtk_css_parser_consume_string (parser));
+
+          token = gtk_css_parser_get_token (parser);
+          if (token->type == GTK_CSS_TOKEN_EOF)
+            break;
+
+          if (token->type != GTK_CSS_TOKEN_COMMA)
+            {
+              gtk_css_parser_error_syntax (parser, "Expected comma after string when parsing GStrv typed 
property");
+              return FALSE;
+            }
+
+          gtk_css_parser_consume_token (parser);
+          token = gtk_css_parser_get_token (parser);
+        }
+
+      g_ptr_array_add (strings, NULL);
+
+      g_value_init (out_value, pspec->value_type);
+      g_value_set_boxed (out_value, g_ptr_array_free (strings, FALSE));
+      return TRUE;
+    }
+  else if (g_type_is_a (pspec->value_type, G_TYPE_OBJECT))
+    {
+      GObject *obj = parse_object (self, NULL);
+
+      g_value_init (out_value, pspec->value_type);
+      g_value_set_object (out_value, obj);
+      return TRUE;
+    }
+  else
+    {
+      gtk_css_parser_error_syntax (parser, "Unable to parse properties of type %s",
+                                   g_type_name (pspec->value_type));
+      return FALSE;
+    }
+
+  return TRUE;
 }
 
 static GObject *
 parse_object (GtkBuilderCssParser *self,
               GObject             *template_object)
 {
+#define MAX_PROPERTIES 64
   GtkCssParser *parser = self->css_parser;
   GObject *object = NULL;
   GObjectClass *object_class;
   const char *type_name;
   GType type;
+  char *buildable_id = NULL;
+  char *property_names[MAX_PROPERTIES];
+  GValue property_values[MAX_PROPERTIES];
+  int n_properties = 0;
+  char *buildable_prop_names[MAX_PROPERTIES];
+  GValue buildable_prop_values[MAX_PROPERTIES];
+  int n_buildable_properties = 0;
+  guint n_signal_connections = 0;
+  SignalConnectionData *signal_connections;
+  int i;
 
   gtk_css_parser_start_semicolon_block (parser, GTK_CSS_TOKEN_OPEN_CURLY);
 
@@ -36,106 +328,177 @@ parse_object (GtkBuilderCssParser *self,
       gtk_css_parser_error_syntax (parser, "Unknown type name '%s'", type_name);
       goto fail;
     }
-
-  if (template_object)
+  if (template_object &&
+      strcmp (type_name, G_OBJECT_TYPE_NAME (template_object)) != 0)
     {
-      object = g_object_ref (template_object);
-      // TODO Check that typeof(object) == given type
-    }
-  else
-    {
-      object = g_object_new (type, NULL);
+      gtk_css_parser_error_syntax (parser,
+                                   "Expected object type '%s' but found '%s'",
+                                   G_OBJECT_TYPE_NAME (template_object),
+                                   type_name);
+      g_object_unref (object);
+      goto fail;
     }
-
   gtk_css_parser_consume_token (parser);
 
-  object_class = G_OBJECT_GET_CLASS (object);
+  object_class = g_type_class_ref (type);
+  g_assert (object_class);
 
+  memset (property_values, 0, MAX_PROPERTIES * sizeof (GValue));
+  memset (buildable_prop_values, 0, MAX_PROPERTIES * sizeof (GValue));
   gtk_css_parser_end_block_prelude (parser);
   while (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF))
     {
-      gtk_css_parser_start_semicolon_block (parser, GTK_CSS_TOKEN_OPEN_CURLY);
+      const char *token_string;
+      char *prop_name = NULL;
+      GParamSpec *pspec;
 
       if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_IDENT))
         {
           gtk_css_parser_error_syntax (parser, "Expected property name");
+          goto next_prop;
+        }
+
+      token_string = gtk_css_parser_get_token (parser)->string.string;
+      /* Special cases */
+      if (strcmp (token_string, "signals") == 0)
+        {
+          gtk_css_parser_consume_token (parser);
+          gtk_css_parser_start_semicolon_block (parser, GTK_CSS_TOKEN_OPEN_CURLY);
+          signal_connections = parse_signals (self, type, &n_signal_connections);
+          goto next_prop;
+        }
+
+      prop_name = g_strdup (token_string);
+      pspec = g_object_class_find_property (object_class, prop_name);
+      gtk_css_parser_consume_token (parser);
+
+      gtk_css_parser_start_semicolon_block (parser, GTK_CSS_TOKEN_OPEN_CURLY);
+      if (pspec)
+        {
+          // TODO: Validate pspec for correct flags, e.g. writable etc.
+          if (!gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COLON))
+            {
+              gtk_css_parser_error_syntax (parser, "Expected ':' after property name");
+              goto next_prop;
+            }
+
+          if (!parse_property (self, pspec, &property_values[n_properties]))
+            goto next_prop;
+
+          property_names[n_properties] = g_steal_pointer (&prop_name);
+          n_properties++;
         }
       else
         {
-          const char *prop_name = gtk_css_parser_get_token (parser)->string.string;
-          GParamSpec *pspec;
+          if (!gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COLON))
+            {
+              gtk_css_parser_error_syntax (parser, "Expected colon after property name");
+              goto next_prop;
+            }
 
-          pspec = g_object_class_find_property (object_class, prop_name);
-          if (!pspec)
+          /* Buildable ID special case */
+          if (strcmp (prop_name, "id") == 0)
             {
-              char *prop_name_dup = g_strdup (prop_name);
-              gboolean parsed = FALSE;
-
-              if (GTK_IS_CSS_BUILDABLE (object))
-                {
-                  GtkCssBuildableIface *iface = GTK_CSS_BUILDABLE_GET_IFACE (object);
-                  /*g_message ("%s Is a  css buildalbe!!", G_OBJECT_TYPE_NAME (object));*/
-
-                  /* TODO: WALK UP HIERARCHY AND SHIT */
-                  parsed = iface->parse_declaration (GTK_CSS_BUILDABLE (object),
-                                                     parser,
-                                                     prop_name,
-                                                     strlen (prop_name));
-                }
-
-              if (!parsed)
-                {
-                  gtk_css_parser_error_syntax (parser, "Invalid property '%s' for class '%s'",
-                                               prop_name, G_OBJECT_TYPE_NAME (object));
-                  goto fail;
-                }
+              buildable_id = gtk_css_parser_consume_string (parser);
+              if (!buildable_id)
+                gtk_css_parser_error_syntax (parser, "Expected string ID");
+
+              goto next_prop;
             }
-          else
+
+          gtk_css_parser_end_block_prelude (parser);
+
+          // TODO: Parse other things than objects
+          while (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF))
             {
-              // TODO: Validate pspec for correct flags, e.g. writable etc.
-              gtk_css_parser_consume_token (parser);
-              if (!gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COLON))
-                {
-                  gtk_css_parser_error_syntax (parser, "Expected ':' after property name");
-                  goto fail;
-                }
-
-              g_message ("parsing property %s", pspec->name);
-              if (pspec->value_type == G_TYPE_BOOLEAN)
-                {
-                  const GtkCssToken *token = gtk_css_parser_get_token (parser);
-
-                  if (token->type != GTK_CSS_TOKEN_IDENT)
-                    {
-                      gtk_css_parser_error_syntax (parser, "Invalid boolean value: '%s'",
-                                                   token->string.string);
-                      goto fail;
-                    }
-                  else
-                    {
-                      if (strcmp (token->string.string, "true") == 0)
-                        g_object_set (object, pspec->name, TRUE, NULL);
-                      else if (strcmp (token->string.string, "false") == 0)
-                        g_object_set (object, pspec->name, FALSE, NULL);
-                      else
-                        {
-                          gtk_css_parser_error_syntax (parser, "Invalid boolean value: '%s'",
-                                                       token->string.string);
-                          goto fail;
-                        }
-
-                      gtk_css_parser_consume_token (parser);
-                    }
-                }
-              else
-                g_warning ("TODO: Add parser for properties of type %s", g_type_name (pspec->value_type));
+              GObject *o = parse_object (self, NULL);
+
+              if (!o)
+                goto next_prop;
+
+              g_value_init (&buildable_prop_values[n_buildable_properties], G_TYPE_OBJECT);
+              g_value_set_object (&buildable_prop_values[n_buildable_properties], o);
+              buildable_prop_names[n_buildable_properties] = g_strdup (prop_name);
+              n_buildable_properties++;
             }
         }
 
+next_prop:
       gtk_css_parser_end_block (parser); /* Property block */
+      g_free (prop_name);
     }
 
-  gtk_css_parser_end_block (parser);
+  if (template_object)
+    {
+      object = g_object_ref (template_object);
+      g_object_setv (object, n_properties,
+                     (const char **)property_names,
+                     property_values);
+    }
+  else /* Create a new object */
+    {
+      object = g_object_new_with_properties (type, n_properties,
+                                             (const char **)property_names,
+                                             property_values);
+    }
+
+  /* Now set the buildable properties */
+  for (i = 0; i < n_buildable_properties; i++)
+    {
+      // TODO: Fix this to allow non-GObject values
+      if (GTK_IS_CSS_BUILDABLE (object))
+        {
+          GObject *o = g_value_get_object (&buildable_prop_values[i]);
+          GtkCssBuildableIface *iface = GTK_CSS_BUILDABLE_GET_IFACE (object);
+          /* TODO: WALK UP HIERARCHY AND SHIT */
+          iface->set_property (GTK_CSS_BUILDABLE (object),
+                               buildable_prop_names[i], strlen (buildable_prop_names[i]),
+                               G_OBJECT_TYPE (o), o);
+        }
+    }
+
+  if (buildable_id)
+    {
+      GtkCssBuildableIface *iface = GTK_CSS_BUILDABLE_GET_IFACE (object);
+      if (iface)
+        iface->set_name (GTK_CSS_BUILDABLE (object), buildable_id);
+
+      g_hash_table_insert (self->object_table,
+                           g_steal_pointer (&buildable_id),
+                           object);
+    }
+
+  /* Connect signal handlers */
+  for (i = 0; i < n_signal_connections; i++)
+    {
+      SignalConnectionData *data = &signal_connections[i];
+      GClosure *closure;
+      GError *error = NULL;
+
+      g_message ("Connecting %s to %s", data->handler_name, data->signal_name);
+
+      GtkBuilder *b = gtk_builder_new ();
+      closure = gtk_builder_scope_create_closure (self->builder_scope,
+                                                  b,
+                                                  data->handler_name,
+                                                  data->swapped,
+                                                 self->template_object ? self->template_object : object,
+                                                  &error);
+
+      if (error)
+        {
+          g_error ("%s", error->message);
+        }
+
+      g_signal_connect_closure_by_id (object,
+                                      data->signal_id,
+                                      data->signal_detail,
+                                      closure,
+                                      data->after);
+
+    }
+
+  gtk_css_parser_end_block (parser); /* Object block */
   return object;
 
 fail:
@@ -186,5 +549,13 @@ gtk_builder_css_parser_extend_with_template (GtkBuilderCssParser *self,
   self->css_parser = gtk_css_parser_new_for_bytes (bytes, NULL, NULL, parser_error_func,
                                                    NULL, NULL);
 
+  self->template_object = template_object;
   parse_objects (self, template_object);
 }
+
+GObject *
+gtk_builder_css_parser_get_object (GtkBuilderCssParser *self,
+                                   const char          *object_name)
+{
+  return g_hash_table_lookup (self->object_table, object_name);
+}
diff --git a/gtk/gtkbuildercssparserprivate.h b/gtk/gtkbuildercssparserprivate.h
index c14c96916a..4929bbfeb1 100644
--- a/gtk/gtkbuildercssparserprivate.h
+++ b/gtk/gtkbuildercssparserprivate.h
@@ -10,6 +10,10 @@ typedef struct _GtkBuilderCssParser GtkBuilderCssParser;
 struct _GtkBuilderCssParser
 {
   GtkCssParser *css_parser;
+
+  GObject *template_object;
+  GtkBuilderScope *builder_scope;
+  GHashTable *object_table; /* Name -> Object */
 };
 
 
@@ -19,8 +23,7 @@ void                        gtk_builder_css_parser_extend_with_template (GtkBuil
                                                                          GType                template_type,
                                                                          GObject             *object,
                                                                          GBytes              *buffer);
-
-
-
+GObject *                   gtk_builder_css_parser_get_object           (GtkBuilderCssParser *self,
+                                                                         const char          *object_name);
 
 #endif
diff --git a/gtk/gtkcombobox.cssui b/gtk/gtkcombobox.cssui
new file mode 100644
index 0000000000..3cc173325d
--- /dev/null
+++ b/gtk/gtkcombobox.cssui
@@ -0,0 +1,45 @@
+GtkComboBox {
+  children: {
+    GtkBox {
+      id: "box";
+      css-classes: "linked";
+
+      children: {
+        GtkToggleButton {
+          id: "button";
+
+          children: {
+            GtkBox {
+              children: {
+                GtkBuiltinIcon {
+                  id: "arrow";
+                  css-name: "arrow";
+                }
+              }
+            }
+          }
+
+          signals: {
+            toggled: "gtk_combo_box_button_toggled";
+            "clicked": {
+              handler: "gtk_combo_box_button_toggled";
+            }
+          }
+        }
+      }
+
+    }
+    GtkTreePopover {
+      id: "popup_widget";
+      has_arrow: false;
+      cell-area: GtkCellAreaBox {
+        id: "area";
+      };
+
+      children: {
+        GtkEventControllerKey {
+        }
+      }
+    }
+  }
+}
diff --git a/gtk/gtklockbutton.cssui b/gtk/gtklockbutton.cssui
index 6d3c5ae730..ed4028507c 100644
--- a/gtk/gtklockbutton.cssui
+++ b/gtk/gtklockbutton.cssui
@@ -8,24 +8,29 @@ GtkLockButton {
       halign: center;
       valign: center;
       spacing: 6;
-      children: GtkImage {
-        icon-name: "missing-image";
-      }
-    }
-    GtkStack {
-      id: "stack";
       children: {
-        GtkLabel {
-          id: "label_lock";
-          xalign: 0;
-          label: _("Lock");
+        GtkImage {
+          id: "image";
+          icon-name: "missing-image";
         }
-        GtkLabel {
-          id: "label_unlock";
-          xalign: 0;
-          label: _("Unlock");
+
+        GtkStack {
+          id: "stack";
+          children: {
+            GtkLabel {
+              id: "label_lock";
+              xalign: 0;
+              label: _("Lock");
+            }
+            GtkLabel {
+              id: "label_unlock";
+              xalign: 0;
+              label: _("Unlock");
+            }
+          }
         }
       }
+
     }
   }
 }
diff --git a/gtk/gtkstatusbar.cssui b/gtk/gtkstatusbar.cssui
new file mode 100644
index 0000000000..e957eba1a4
--- /dev/null
+++ b/gtk/gtkstatusbar.cssui
@@ -0,0 +1,26 @@
+GtkStatusBar {
+  children: {
+    GtkFrame {
+      id: "frame";
+      shadow-type: none;
+      hexpand: true;
+
+      children: {
+        GtkBox {
+          id: "message_area";
+          spacing: 4;
+
+          children: {
+            GtkLabel {
+              id: "label";
+              halign: start;
+              valign: center;
+              ellipsize: end;
+              single-line-mode: true;
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/gtk/gtkvolumebutton.cssui b/gtk/gtkvolumebutton.cssui
new file mode 100644
index 0000000000..19a1f698ae
--- /dev/null
+++ b/gtk/gtkvolumebutton.cssui
@@ -0,0 +1,14 @@
+GtkVolumeButton {
+  can-focus: true;
+  receives-default: true;
+  has-tooltip: true;
+  relief: none;
+  focus-on-click: false;
+  orientation: vertical;
+  icons: "audio-volume-high", "audio-volume-low", "audio-volume-medium";
+  adjustment: GtkAdjustment {
+    upper: 1;
+    step-increment: 0.02;
+    page-increment: 0.2;
+  };
+}
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 39da78f557..eb2386d959 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -652,6 +652,9 @@ static gboolean         gtk_widget_real_can_activate_accel      (GtkWidget *widg
 
 static void             gtk_widget_buildable_interface_init     (GtkBuildableIface  *iface);
 static void             gtk_widget_css_buildable_interface_init (GtkCssBuildableIface *iface);
+static void             gtk_widget_css_buildable_set_name       (GtkCssBuildable    *self,
+                                                                 const char         *name);
+
 static void             gtk_widget_buildable_set_name           (GtkBuildable       *buildable,
                                                                  const gchar        *name);
 static const gchar *    gtk_widget_buildable_get_name           (GtkBuildable       *buildable);
@@ -8710,19 +8713,39 @@ gtk_widget_buildable_add_child (GtkBuildable  *buildable,
     }
 }
 
+static void
+gtk_widget_css_buildable_set_name (GtkCssBuildable *self,
+                                   const char      *name)
+{
+  g_object_set_qdata_full (G_OBJECT (self), quark_builder_set_name,
+                           g_strdup (name), g_free);
+}
+
 static gboolean
-gtk_widget_css_buildable_parse_declaration (GtkCssBuildable *self,
-                                            GtkCssParser    *parser,
-                                            const char      *decl_name,
-                                            size_t           decl_name_len)
+gtk_widget_css_buildable_set_property (GtkCssBuildable *self,
+                                       const char      *prop_name,
+                                       size_t           prop_name_len,
+                                       GType            value_type,
+                                       gpointer         value)
 {
-  g_message (__FUNCTION__);
-  if (decl_name_len == strlen ("children"))
+  if (prop_name_len != strlen ("children"))
+    return FALSE;
+
+  if (strcmp (prop_name, "children") == 0)
     {
-      if (strcmp (decl_name, "children") == 0)
+      if (g_type_is_a (value_type, GTK_TYPE_WIDGET))
         {
+          if (GTK_IS_CONTAINER (self))
+            gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (value));
+          else
+            gtk_widget_set_parent (GTK_WIDGET (value), GTK_WIDGET (self));
+
           return TRUE;
         }
+      else if (g_type_is_a (value_type, GTK_TYPE_EVENT_CONTROLLER))
+        {
+          gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (value));
+        }
     }
 
   return FALSE;
@@ -8731,7 +8754,8 @@ gtk_widget_css_buildable_parse_declaration (GtkCssBuildable *self,
 static void
 gtk_widget_css_buildable_interface_init (GtkCssBuildableIface *iface)
 {
-  iface->parse_declaration = gtk_widget_css_buildable_parse_declaration;
+  iface->set_name = gtk_widget_css_buildable_set_name;
+  iface->set_property = gtk_widget_css_buildable_set_property;
 }
 
 static void
@@ -11369,23 +11393,62 @@ gtk_widget_init_template (GtkWidget *widget)
   GSList *l;
   GType class_type;
 
-
+  template = GTK_WIDGET_GET_CLASS (widget)->priv->template;
   class_type = G_OBJECT_TYPE (widget);
-   if (strcmp (G_OBJECT_TYPE_NAME (widget), "GtkLockButton") == 0 &&
-       g_type_from_name ("GtkLockButton") != G_TYPE_INVALID) {
+   if (strcmp (G_OBJECT_TYPE_NAME (widget), "GtkComboBox") == 0 &&
+       g_type_from_name ("GtkComboBox") != G_TYPE_INVALID) {
       GBytes *css_data;
       GtkBuilderCssParser *p = gtk_builder_css_parser_new ();
 
       char *d;
 
-      g_file_get_contents ("../gtk/gtklockbutton.cssui", &d, NULL, NULL);
+      g_file_get_contents ("../gtk/gtkcombobox.cssui", &d, NULL, NULL);
 
       css_data = g_bytes_new_static (d, strlen (d));
 
+      if (template->scope)
+        p->builder_scope = template->scope;
+      else
+        p->builder_scope = gtk_builder_cscope_new ();
+
       gtk_builder_css_parser_extend_with_template (p,
                                                    class_type,
                                                    G_OBJECT (widget),
                                                    css_data);
+    /* Build the automatic child data
+     */
+    template = GTK_WIDGET_GET_CLASS (widget)->priv->template;
+    for (l = template->children; l; l = l->next)
+      {
+        GHashTable *auto_child_hash;
+        AutomaticChildClass *child_class = l->data;
+        GObject *o;
+
+        /* This will setup the pointer of an automated child, and cause
+         * it to be available in any GtkBuildable.get_internal_child()
+         * invocations which may follow by reference in child classes.
+         */
+        o = gtk_builder_css_parser_get_object (p, child_class->name);
+        if (!o)
+          {
+            g_critical ("Unable to retrieve object '%s' from class template for type '%s' while building a 
'%s'",
+                        child_class->name, g_type_name (class_type), G_OBJECT_TYPE_NAME (widget));
+            continue;
+          }
+
+        auto_child_hash = get_auto_child_hash (widget, class_type, TRUE);
+        g_hash_table_insert (auto_child_hash, child_class->name, g_object_ref (o));
+
+        if (child_class->offset != 0)
+          {
+            gpointer field_p;
+
+            /* Assign 'object' to the specified offset in the instance (or private) data */
+            field_p = G_STRUCT_MEMBER_P (widget, child_class->offset);
+            (* (gpointer *) field_p) = o;
+          }
+      }
+      return;
     }
 
 
@@ -11393,8 +11456,6 @@ gtk_widget_init_template (GtkWidget *widget)
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
   object = G_OBJECT (widget);
-
-  template = GTK_WIDGET_GET_CLASS (widget)->priv->template;
   g_return_if_fail (template != NULL);
 
   builder = gtk_builder_new ();


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