[gtk/wip/baedert/parser: 73/73] builder css parser prototype



commit 3153b8565d4052607a34fa7bc0c1ba1a8e4c6962
Author: Timm Bäder <mail baedert org>
Date:   Mon Feb 24 10:55:22 2020 +0100

    builder css parser prototype

 gtk/css/gtkcsstokenizer.c        |   1 +
 gtk/gtkbuildable.c               |  12 +
 gtk/gtkbuildable.h               |  34 ++-
 gtk/gtkbuildercssparser.c        | 561 +++++++++++++++++++++++++++++++++++++++
 gtk/gtkbuildercssparserprivate.h |  29 ++
 gtk/gtkcombobox.cssui            |  49 ++++
 gtk/gtklockbutton.cssui          |  36 +++
 gtk/gtkstatusbar.cssui           |  26 ++
 gtk/gtkvolumebutton.cssui        |  14 +
 gtk/gtkwidget.c                  | 129 ++++++++-
 gtk/meson.build                  |   1 +
 11 files changed, 885 insertions(+), 7 deletions(-)
---
diff --git a/gtk/css/gtkcsstokenizer.c b/gtk/css/gtkcsstokenizer.c
index fa0cb9f083..71423d4b98 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.c b/gtk/gtkbuildable.c
index 7211c3b300..d87c22570f 100644
--- a/gtk/gtkbuildable.c
+++ b/gtk/gtkbuildable.c
@@ -40,6 +40,18 @@
 #include "gtkintl.h"
 
 
+
+typedef GtkCssBuildableIface GtkCssBuildableInterface;
+G_DEFINE_INTERFACE (GtkCssBuildable, gtk_css_buildable, G_TYPE_OBJECT)
+
+static void
+gtk_css_buildable_default_init (GtkCssBuildableInterface *iface)
+{
+  g_message ("%s!!!!", __FUNCTION__);
+}
+
+
+
 typedef GtkBuildableIface GtkBuildableInterface;
 G_DEFINE_INTERFACE (GtkBuildable, gtk_buildable, G_TYPE_OBJECT)
 
diff --git a/gtk/gtkbuildable.h b/gtk/gtkbuildable.h
index 7808f0445d..81cc5cf317 100644
--- a/gtk/gtkbuildable.h
+++ b/gtk/gtkbuildable.h
@@ -27,6 +27,38 @@
 
 G_BEGIN_DECLS
 
+
+
+#define GTK_TYPE_CSS_BUILDABLE            (gtk_css_buildable_get_type ())
+#define GTK_CSS_BUILDABLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CSS_BUILDABLE, 
GtkCssBuildable))
+#define GTK_CSS_BUILDABLE_CLASS(obj)      (G_TYPE_CHECK_CLASS_CAST ((obj), GTK_TYPE_CSS_BUILDABLE, 
GtkCssBuildableIface))
+#define GTK_IS_CSS_BUILDABLE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CSS_BUILDABLE))
+#define GTK_CSS_BUILDABLE_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_CSS_BUILDABLE, 
GtkCssBuildableIface))
+
+
+typedef struct _GtkCssBuildable      GtkCssBuildable; /* Dummy typedef */
+typedef struct _GtkCssBuildableIface GtkCssBuildableIface;
+
+
+struct _GtkCssBuildableIface
+{
+  GTypeInterface g_iface;
+
+  void          (* set_name)               (GtkCssBuildable    *self,
+                                            const char         *name);
+
+  gboolean      (* set_property)           (GtkCssBuildable    *self,
+                                            const char         *prop_name,
+                                            size_t              prop_name_len,
+                                            GType               value_type,
+                                            gpointer            value);
+};
+
+
+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))
@@ -101,7 +133,7 @@ struct _GtkBuildableParser
  *  content below <child>. To handle an element, the implementation
  *  must fill in the @parser and @user_data and return %TRUE.
  *  #GtkWidget implements this to parse keyboard accelerators specified
- *  in <accelerator> elements. 
+ *  in <accelerator> elements.
  *  Note that @user_data must be freed in @custom_tag_end or @custom_finished.
  * @custom_tag_end: Called for the end tag of each custom element that is
  *  handled by the buildable (see @custom_tag_start).
diff --git a/gtk/gtkbuildercssparser.c b/gtk/gtkbuildercssparser.c
new file mode 100644
index 0000000000..defbd4c0f9
--- /dev/null
+++ b/gtk/gtkbuildercssparser.c
@@ -0,0 +1,561 @@
+
+
+#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)
+{
+  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);
+
+  /* Get us the ident, which will determine what we parse and how we parse it */
+  if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_IDENT))
+    {
+      gtk_css_parser_error_syntax (parser, "Expected type name");
+      goto fail;
+    }
+
+  type_name = gtk_css_parser_get_token (parser)->string.string;
+  type = g_type_from_name (type_name);
+
+  if (type == G_TYPE_INVALID)
+    {
+      gtk_css_parser_error_syntax (parser, "Unknown type name '%s'", type_name);
+      goto fail;
+    }
+  if (template_object &&
+      strcmp (type_name, G_OBJECT_TYPE_NAME (template_object)) != 0)
+    {
+      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_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))
+    {
+      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
+        {
+          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;
+            }
+
+          /* Buildable ID special case */
+          if (strcmp (prop_name, "id") == 0)
+            {
+              buildable_id = gtk_css_parser_consume_string (parser);
+              if (!buildable_id)
+                gtk_css_parser_error_syntax (parser, "Expected string ID");
+
+              goto next_prop;
+            }
+
+          if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_IDENT))
+            gtk_css_parser_end_block_prelude (parser);
+
+          // TODO: Parse other things than objects
+          while (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF))
+            {
+              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);
+    }
+
+  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;
+
+      // TODO vvvv
+      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:
+  gtk_css_parser_end_block (parser);
+
+  if (object)
+    g_object_unref (object);
+
+  return NULL;
+}
+
+static void
+parse_objects (GtkBuilderCssParser *self,
+               GObject             *template_object)
+{
+  const GtkCssToken *token;
+  GtkCssParser *parser = self->css_parser;
+
+  for (token = gtk_css_parser_get_token (parser);
+       !gtk_css_token_is (token, GTK_CSS_TOKEN_EOF);
+       token = gtk_css_parser_get_token (parser))
+    {
+      parse_object (self, template_object);
+    }
+}
+
+static void
+parser_error_func (GtkCssParser         *parser,
+                   const GtkCssLocation *start,
+                   const GtkCssLocation *end,
+                   const GError         *error,
+                   gpointer              user_data)
+{
+  GtkCssSection *section = gtk_css_section_new (gtk_css_parser_get_file (parser), start, end);
+
+  g_warning ("%s: %s", error->message, gtk_css_section_to_string (section));
+
+  gtk_css_section_unref (section);
+}
+
+
+void
+gtk_builder_css_parser_extend_with_template (GtkBuilderCssParser *self,
+                                             GType                template_type,
+                                             GObject             *template_object,
+                                             GBytes              *bytes)
+{
+  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
new file mode 100644
index 0000000000..4929bbfeb1
--- /dev/null
+++ b/gtk/gtkbuildercssparserprivate.h
@@ -0,0 +1,29 @@
+#ifndef __GTK_BUILDER_CSS_PARSER_PRIVATE_H__
+#define __GTK_BUILDER_CSS_PARSER_PRIVATE_H__
+
+#include "gtkwidget.h"
+#include <gtk/css/gtkcss.h>
+#include "gtk/css/gtkcssparserprivate.h"
+
+typedef struct _GtkBuilderCssParser GtkBuilderCssParser;
+
+struct _GtkBuilderCssParser
+{
+  GtkCssParser *css_parser;
+
+  GObject *template_object;
+  GtkBuilderScope *builder_scope;
+  GHashTable *object_table; /* Name -> Object */
+};
+
+
+GtkBuilderCssParser *       gtk_builder_css_parser_new         (void);
+
+void                        gtk_builder_css_parser_extend_with_template (GtkBuilderCssParser *self,
+                                                                         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..89d31c6131
--- /dev/null
+++ b/gtk/gtkcombobox.cssui
@@ -0,0 +1,49 @@
+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";
+      };
+
+      signals: {
+        menu-activate: "gtk_combo_box_menu_activate";
+        show: "gtk_combo_box_menu_show";
+        hide: "gtk_combo_box_menu_hide";
+      }
+
+      children:GtkEventControllerKey {
+        signals: {
+          key-pressed: "gtk_combo_box_menu_key";
+          key-released: "gtk_combo_box_menu_key";
+        }
+      };
+    }
+  }
+}
diff --git a/gtk/gtklockbutton.cssui b/gtk/gtklockbutton.cssui
new file mode 100644
index 0000000000..ed4028507c
--- /dev/null
+++ b/gtk/gtklockbutton.cssui
@@ -0,0 +1,36 @@
+GtkLockButton {
+  can-focus: true;
+  receives-default: true;
+
+  children: {
+    GtkBox {
+      id: "box";
+      halign: center;
+      valign: center;
+      spacing: 6;
+      children: {
+        GtkImage {
+          id: "image";
+          icon-name: "missing-image";
+        }
+
+        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 fe3277fd3e..5bd426de62 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -651,6 +651,10 @@ static gboolean         gtk_widget_real_can_activate_accel      (GtkWidget *widg
                                                                  guint      signal_id);
 
 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);
@@ -743,9 +747,16 @@ gtk_widget_get_type (void)
 
       const GInterfaceInfo buildable_info =
       {
-       (GInterfaceInitFunc) gtk_widget_buildable_interface_init,
-       (GInterfaceFinalizeFunc) NULL,
-       NULL /* interface data */
+        (GInterfaceInitFunc) gtk_widget_buildable_interface_init,
+        (GInterfaceFinalizeFunc) NULL,
+        NULL /* interface data */
+      };
+
+      const GInterfaceInfo css_buildable_info =
+      {
+        (GInterfaceInitFunc) gtk_widget_css_buildable_interface_init,
+        (GInterfaceFinalizeFunc) NULL,
+        NULL /* interface data */
       };
 
       const GInterfaceInfo constraint_target_info =
@@ -767,6 +778,9 @@ gtk_widget_get_type (void)
                                    &accessibility_info) ;
       g_type_add_interface_static (widget_type, GTK_TYPE_BUILDABLE,
                                    &buildable_info) ;
+      g_type_add_interface_static (widget_type, GTK_TYPE_CSS_BUILDABLE,
+                                   &css_buildable_info) ;
+
       g_type_add_interface_static (widget_type, GTK_TYPE_CONSTRAINT_TARGET,
                                    &constraint_target_info) ;
     }
@@ -8690,6 +8704,51 @@ 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_set_property (GtkCssBuildable *self,
+                                       const char      *prop_name,
+                                       size_t           prop_name_len,
+                                       GType            value_type,
+                                       gpointer         value)
+{
+  if (prop_name_len != strlen ("children"))
+    return FALSE;
+
+  if (strcmp (prop_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;
+}
+
+static void
+gtk_widget_css_buildable_interface_init (GtkCssBuildableIface *iface)
+{
+  iface->set_name = gtk_widget_css_buildable_set_name;
+  iface->set_property = gtk_widget_css_buildable_set_property;
+}
+
 static void
 gtk_widget_buildable_interface_init (GtkBuildableIface *iface)
 {
@@ -11314,6 +11373,7 @@ setup_template_child (GtkWidgetTemplate   *template_data,
  * before the construct properties are set. Properties passed to g_object_new()
  * should take precedence over properties set in the private template XML.
  */
+#include "gtkbuildercssparserprivate.h"
 void
 gtk_widget_init_template (GtkWidget *widget)
 {
@@ -11324,12 +11384,69 @@ 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), "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/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;
+    }
+
+
+
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
   object = G_OBJECT (widget);
-  class_type = G_OBJECT_TYPE (widget);
-
-  template = GTK_WIDGET_GET_CLASS (widget)->priv->template;
   g_return_if_fail (template != NULL);
 
   builder = gtk_builder_new ();
diff --git a/gtk/meson.build b/gtk/meson.build
index bf7e08a06c..f124aead24 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -173,6 +173,7 @@ gtk_public_sources = files([
   'gtkbuildable.c',
   'gtkbuilder.c',
   'gtkbuilderparser.c',
+  'gtkbuildercssparser.c',
   'gtkbuilderscope.c',
   'gtkbutton.c',
   'gtkcalendar.c',


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