diff options
author | Timm Bäder <mail@baedert.org> | 2020-02-24 10:55:22 +0100 |
---|---|---|
committer | Timm Bäder <mail@baedert.org> | 2020-03-13 17:38:04 +0100 |
commit | 3153b8565d4052607a34fa7bc0c1ba1a8e4c6962 (patch) | |
tree | 9015e6c0e3685f00477940860f98ef96eb125760 | |
parent | 52c866490f2450a7a10ab1dd338d230567cc31d5 (diff) | |
download | gtk+-wip/baedert/parser.tar.gz |
builder css parser prototypewip/baedert/parser
-rw-r--r-- | gtk/css/gtkcsstokenizer.c | 1 | ||||
-rw-r--r-- | gtk/gtkbuildable.c | 12 | ||||
-rw-r--r-- | gtk/gtkbuildable.h | 34 | ||||
-rw-r--r-- | gtk/gtkbuildercssparser.c | 561 | ||||
-rw-r--r-- | gtk/gtkbuildercssparserprivate.h | 29 | ||||
-rw-r--r-- | gtk/gtkcombobox.cssui | 49 | ||||
-rw-r--r-- | gtk/gtklockbutton.cssui | 36 | ||||
-rw-r--r-- | gtk/gtkstatusbar.cssui | 26 | ||||
-rw-r--r-- | gtk/gtkvolumebutton.cssui | 14 | ||||
-rw-r--r-- | gtk/gtkwidget.c | 129 | ||||
-rw-r--r-- | 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) ; } @@ -8691,6 +8705,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) { quark_builder_atk_relations = g_quark_from_static_string ("gtk-builder-atk-relations"); @@ -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', |