Compare commits

...

2 Commits

Author SHA1 Message Date
Timm Bäder
3153b8565d builder css parser prototype 2020-03-13 17:38:04 +01:00
Timm Bäder
52c866490f filechooserwidget: Avoid criticals when in recent mode
Don't try to use the browse_files_model just because we have a location
entry. The model might still be NULL at this point.
2020-03-13 17:37:13 +01:00
12 changed files with 887 additions and 8 deletions

View File

@@ -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:

View File

@@ -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)

View File

@@ -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))

561
gtk/gtkbuildercssparser.c Normal file
View File

@@ -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);
}

View File

@@ -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

49
gtk/gtkcombobox.cssui Normal file
View File

@@ -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";
}
};
}
}
}

View File

@@ -7169,7 +7169,8 @@ list_selection_changed (GtkTreeSelection *selection,
if (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view)) == NULL)
return;
if (priv->location_entry)
if (priv->location_entry &&
priv->browse_files_model)
update_chooser_entry (impl);
location_bar_update (impl);

36
gtk/gtklockbutton.cssui Normal file
View File

@@ -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");
}
}
}
}
}
}
}

26
gtk/gtkstatusbar.cssui Normal file
View File

@@ -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;
}
}
}
}
}
}
}

14
gtk/gtkvolumebutton.cssui Normal file
View File

@@ -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;
};
}

View File

@@ -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);
@@ -748,6 +752,13 @@ gtk_widget_get_type (void)
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 =
{
(GInterfaceInitFunc) NULL,
@@ -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 ();

View File

@@ -173,6 +173,7 @@ gtk_public_sources = files([
'gtkbuildable.c',
'gtkbuilder.c',
'gtkbuilderparser.c',
'gtkbuildercssparser.c',
'gtkbuilderscope.c',
'gtkbutton.c',
'gtkcalendar.c',