[gtk/wip/baedert/parser: 73/73] builder css parser prototype
- From: Timm Bäder <baedert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/baedert/parser: 73/73] builder css parser prototype
- Date: Fri, 13 Mar 2020 16:39:28 +0000 (UTC)
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]