[gtk/wip/baedert/parser: 3/3] foo
- From: Timm Bäder <baedert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/baedert/parser: 3/3] foo
- Date: Thu, 12 Mar 2020 18:02:13 +0000 (UTC)
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]