Compare commits
2 Commits
map-list-c
...
wip/baeder
Author | SHA1 | Date | |
---|---|---|---|
|
3153b8565d | ||
|
52c866490f |
@@ -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:
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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).
|
||||
|
561
gtk/gtkbuildercssparser.c
Normal file
561
gtk/gtkbuildercssparser.c
Normal 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);
|
||||
}
|
29
gtk/gtkbuildercssparserprivate.h
Normal file
29
gtk/gtkbuildercssparserprivate.h
Normal 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
49
gtk/gtkcombobox.cssui
Normal 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";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
36
gtk/gtklockbutton.cssui
Normal 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
26
gtk/gtkstatusbar.cssui
Normal 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
14
gtk/gtkvolumebutton.cssui
Normal 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;
|
||||
};
|
||||
}
|
129
gtk/gtkwidget.c
129
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 ();
|
||||
|
@@ -173,6 +173,7 @@ gtk_public_sources = files([
|
||||
'gtkbuildable.c',
|
||||
'gtkbuilder.c',
|
||||
'gtkbuilderparser.c',
|
||||
'gtkbuildercssparser.c',
|
||||
'gtkbuilderscope.c',
|
||||
'gtkbutton.c',
|
||||
'gtkcalendar.c',
|
||||
|
Reference in New Issue
Block a user