builder: Add <binding> tag

The tag contains an expression that it then gtk_expression_bind()s to
the object it is contained in.
This commit is contained in:
Benjamin Otte 2019-11-25 08:15:31 +01:00 committed by Matthias Clasen
parent 713a6676ff
commit 934bfc8887
3 changed files with 200 additions and 27 deletions

View File

@ -219,6 +219,7 @@
#include "gtkbuildable.h"
#include "gtkbuilderscopeprivate.h"
#include "gtkdebug.h"
#include "gtkexpression.h"
#include "gtkmain.h"
#include "gtkicontheme.h"
#include "gtkintl.h"
@ -685,8 +686,22 @@ gtk_builder_take_bindings (GtkBuilder *builder,
for (l = bindings; l; l = l->next)
{
BindingInfo *info = l->data;
info->target = target;
CommonInfo *common_info = l->data;
if (common_info->tag_type == TAG_BINDING)
{
BindingInfo *info = l->data;
info->target = target;
}
else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
{
BindingExpressionInfo *info = l->data;
info->target = target;
}
else
{
g_assert_not_reached ();
}
}
priv->bindings = g_slist_concat (priv->bindings, bindings);
@ -1013,17 +1028,6 @@ gtk_builder_apply_delayed_properties (GtkBuilder *builder,
return result;
}
static inline void
free_binding_info (gpointer data,
gpointer user)
{
BindingInfo *info = data;
g_free (info->source);
g_free (info->source_property);
g_slice_free (BindingInfo, data);
}
static inline gboolean
gtk_builder_create_bindings (GtkBuilder *builder,
GError **error)
@ -1034,26 +1038,44 @@ gtk_builder_create_bindings (GtkBuilder *builder,
for (l = priv->bindings; l; l = l->next)
{
BindingInfo *info = l->data;
GObject *source;
CommonInfo *common_info = l->data;
if (result)
if (common_info->tag_type == TAG_BINDING)
{
source = gtk_builder_lookup_object (builder, info->source, info->line, info->col, error);
BindingInfo *info = l->data;
GObject *source;
source = _gtk_builder_lookup_object (builder, info->source, info->line, info->col);
if (source)
g_object_bind_property (source, info->source_property,
info->target, info->target_pspec->name,
info->flags);
else
result = FALSE;
}
free_binding_info (info, NULL);
_free_binding_info (info, NULL);
}
else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
{
BindingExpressionInfo *info = l->data;
GtkExpression *expression;
expression = expression_info_construct (builder, info->expr, error);
if (expression == NULL)
{
g_prefix_error (error, "%s:%d:%d: ", priv->filename, info->line, info->col);
error = NULL;
result = FALSE;
}
else
{
gtk_expression_bind (expression, info->target, info->target_pspec->name);
}
free_binding_expression_info (info);
}
}
g_slist_free (priv->bindings);
priv->bindings = NULL;
return result;
}

View File

@ -913,7 +913,8 @@ parse_property (ParserData *data,
{
BindingInfo *binfo;
binfo = g_slice_new (BindingInfo);
binfo = g_slice_new0 (BindingInfo);
binfo->tag_type = TAG_BINDING;
binfo->target = NULL;
binfo->target_pspec = pspec;
binfo->source = g_strdup (bind_source);
@ -945,6 +946,78 @@ parse_property (ParserData *data,
state_push (data, info);
}
static void
parse_binding (ParserData *data,
const gchar *element_name,
const gchar **names,
const gchar **values,
GError **error)
{
BindingExpressionInfo *info;
const gchar *name = NULL;
ObjectInfo *object_info;
GParamSpec *pspec = NULL;
object_info = state_peek_info (data, ObjectInfo);
if (!object_info ||
!(object_info->tag_type == TAG_OBJECT ||
object_info->tag_type == TAG_TEMPLATE))
{
error_invalid_tag (data, element_name, NULL, error);
return;
}
if (!g_markup_collect_attributes (element_name, names, values, error,
G_MARKUP_COLLECT_STRING, "name", &name,
G_MARKUP_COLLECT_INVALID))
{
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
pspec = g_object_class_find_property (object_info->oclass, name);
if (!pspec)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_PROPERTY,
"Invalid property: %s.%s",
g_type_name (object_info->type), name);
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
else if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_PROPERTY,
"%s.%s is a construct-only property",
g_type_name (object_info->type), name);
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
else if (!(pspec->flags & G_PARAM_WRITABLE))
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_PROPERTY,
"%s.%s is a non-writable property",
g_type_name (object_info->type), name);
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
return;
}
info = g_slice_new0 (BindingExpressionInfo);
info->tag_type = TAG_BINDING_EXPRESSION;
info->target = NULL;
info->target_pspec = pspec;
gtk_buildable_parse_context_get_position (&data->ctx, &info->line, &info->col);
state_push (data, info);
}
static void
free_property_info (PropertyInfo *info)
{
@ -1005,8 +1078,13 @@ check_expression_parent (ParserData *data)
return G_PARAM_SPEC_VALUE_TYPE (prop_info->pspec) == GTK_TYPE_EXPRESSION;
}
else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
{
BindingExpressionInfo *expr_info = (BindingExpressionInfo *) common_info;
if (common_info->tag_type == TAG_EXPRESSION)
return expr_info->expr == NULL;
}
else if (common_info->tag_type == TAG_EXPRESSION)
{
ExpressionInfo *expr_info = (ExpressionInfo *) common_info;
@ -1194,7 +1272,7 @@ parse_lookup_expression (ParserData *data,
state_push (data, info);
}
static GtkExpression *
GtkExpression *
expression_info_construct (GtkBuilder *builder,
ExpressionInfo *info,
GError **error)
@ -1446,6 +1524,23 @@ _free_signal_info (SignalInfo *info,
g_slice_free (SignalInfo, info);
}
void
_free_binding_info (BindingInfo *info,
gpointer user)
{
g_free (info->source);
g_free (info->source_property);
g_slice_free (BindingInfo, info);
}
void
free_binding_expression_info (BindingExpressionInfo *info)
{
if (info->expr)
free_expression_info (info->expr);
g_slice_free (BindingExpressionInfo, info);
}
static void
free_requires_info (RequiresInfo *info,
gpointer user_data)
@ -1686,6 +1781,8 @@ start_element (GtkBuildableParseContext *context,
}
else if (strcmp (element_name, "property") == 0)
parse_property (data, element_name, names, values, error);
else if (strcmp (element_name, "binding") == 0)
parse_binding (data, element_name, names, values, error);
else if (strcmp (element_name, "child") == 0)
parse_child (data, element_name, names, values, error);
else if (strcmp (element_name, "signal") == 0)
@ -1777,6 +1874,30 @@ end_element (GtkBuildableParseContext *context,
else
g_assert_not_reached ();
}
else if (strcmp (element_name, "binding") == 0)
{
BindingExpressionInfo *binfo = state_pop_info (data, BindingExpressionInfo);
CommonInfo *info = state_peek_info (data, CommonInfo);
g_assert (info != NULL);
if (binfo->expr == NULL)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_TAG,
"Binding tag requires an expression");
free_binding_expression_info (binfo);
}
else if (info->tag_type == TAG_OBJECT ||
info->tag_type == TAG_TEMPLATE)
{
ObjectInfo *object_info = (ObjectInfo*)info;
object_info->bindings = g_slist_prepend (object_info->bindings, binfo);
}
else
g_assert_not_reached ();
}
else if (strcmp (element_name, "object") == 0 ||
strcmp (element_name, "template") == 0)
{
@ -1845,7 +1966,13 @@ end_element (GtkBuildableParseContext *context,
CommonInfo *parent_info = state_peek_info (data, CommonInfo);
g_assert (parent_info != NULL);
if (parent_info->tag_type == TAG_PROPERTY)
if (parent_info->tag_type == TAG_BINDING_EXPRESSION)
{
BindingExpressionInfo *expr_info = (BindingExpressionInfo *) parent_info;
expr_info->expr = expression_info;
}
else if (parent_info->tag_type == TAG_PROPERTY)
{
PropertyInfo *prop_info = (PropertyInfo *) parent_info;
@ -1996,6 +2123,12 @@ free_info (CommonInfo *info)
case TAG_CHILD:
free_child_info ((ChildInfo *)info);
break;
case TAG_BINDING:
_free_binding_info ((BindingInfo *)info, NULL);
break;
case TAG_BINDING_EXPRESSION:
free_binding_expression_info ((BindingExpressionInfo *) info);
break;
case TAG_PROPERTY:
free_property_info ((PropertyInfo *)info);
break;

View File

@ -25,7 +25,8 @@
enum {
TAG_PROPERTY,
TAG_MENU,
TAG_BINDING,
TAG_BINDING_EXPRESSION,
TAG_REQUIRES,
TAG_OBJECT,
TAG_CHILD,
@ -117,6 +118,7 @@ typedef struct {
typedef struct
{
guint tag_type;
GObject *target;
GParamSpec *target_pspec;
gchar *source;
@ -126,6 +128,16 @@ typedef struct
gint col;
} BindingInfo;
typedef struct
{
guint tag_type;
GObject *target;
GParamSpec *target_pspec;
ExpressionInfo *expr;
gint line;
gint col;
} BindingExpressionInfo;
typedef struct {
guint tag_type;
gchar *library;
@ -212,6 +224,12 @@ gboolean _gtk_builder_finish (GtkBuilder *builder,
GError **error);
void _free_signal_info (SignalInfo *info,
gpointer user_data);
void _free_binding_info (BindingInfo *info,
gpointer user_data);
void free_binding_expression_info (BindingExpressionInfo *info);
GtkExpression * expression_info_construct (GtkBuilder *builder,
ExpressionInfo *info,
GError **error);
/* Internal API which might be made public at some point */
gboolean _gtk_builder_boolean_from_string (const gchar *string,