forked from AuroraMiddleware/gtk
Added bindings support to GtkBuilder by introducing 3 new <property> attributes "bind-source" to specify the source object of the binding "bind-property" to specify the source property and "bind-flags" to specify the binding flags (optional)
Binding an object sensitive property with a check button active property will look like this: <object class="GtkButton" id="button"> <property name="sensitive" bind-source="checkbutton" bind-property="active"/> </object> This is based on the original work done by Denis Washington for his GSoC project This closes Bug 654417 "[GSoC] Add <binding> element to GtkBuilder syntax"
This commit is contained in:
parent
be9d1e0b3b
commit
887fc60cce
@ -134,6 +134,14 @@
|
||||
* object has to be constructed before it can be used as the value of
|
||||
* a construct-only property.
|
||||
*
|
||||
* It is also possible to bind a property value to another object's
|
||||
* property value using the attributes
|
||||
* "bind-source" to specify the source object of the binding,
|
||||
* "bind-property" to specify the source property and optionally
|
||||
* "bind-flags" to specify the binding flags
|
||||
* Internally builder implement this using GBinding objects.
|
||||
* For more information see g_object_bind_property()
|
||||
*
|
||||
* Signal handlers are set up with the <signal> element. The “name”
|
||||
* attribute specifies the name of the signal, and the “handler” attribute
|
||||
* specifies the function to connect to the signal. By default, GTK+ tries
|
||||
@ -244,6 +252,7 @@ struct _GtkBuilderPrivate
|
||||
GHashTable *callbacks;
|
||||
GSList *delayed_properties;
|
||||
GSList *signals;
|
||||
GSList *bindings;
|
||||
gchar *filename;
|
||||
gchar *resource_prefix;
|
||||
GType template_type;
|
||||
@ -499,6 +508,13 @@ gtk_builder_get_parameters (GtkBuilder *builder,
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (prop->bound && (!prop->data || *prop->data == '\0'))
|
||||
{
|
||||
/* Ignore properties with a binding and no value since they are
|
||||
* only there for to express the binding.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
else if (!gtk_builder_value_from_string (builder, pspec,
|
||||
prop->data, ¶meter.value, &error))
|
||||
{
|
||||
@ -572,6 +588,15 @@ object_set_name (GObject *object, const gchar *name)
|
||||
g_object_set_data_full (object, "gtk-builder-name", g_strdup (name), g_free);
|
||||
}
|
||||
|
||||
static inline const gchar *
|
||||
object_get_name (GObject *object)
|
||||
{
|
||||
if (GTK_IS_BUILDABLE (object))
|
||||
return gtk_buildable_get_name (GTK_BUILDABLE (object));
|
||||
else
|
||||
return g_object_get_data (object, "gtk-builder-name");
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_builder_add_object (GtkBuilder *builder,
|
||||
const gchar *id,
|
||||
@ -581,6 +606,22 @@ _gtk_builder_add_object (GtkBuilder *builder,
|
||||
g_hash_table_insert (builder->priv->objects, g_strdup (id), g_object_ref (object));
|
||||
}
|
||||
|
||||
static inline void
|
||||
gtk_builder_take_bindings (GtkBuilder *builder,
|
||||
GObject *target,
|
||||
GSList *bindings)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
for (l = bindings; l; l = g_slist_next (l))
|
||||
{
|
||||
BindingInfo *info = l->data;
|
||||
info->target = target;
|
||||
}
|
||||
|
||||
builder->priv->bindings = g_slist_concat (builder->priv->bindings, bindings);
|
||||
}
|
||||
|
||||
GObject *
|
||||
_gtk_builder_construct (GtkBuilder *builder,
|
||||
ObjectInfo *info,
|
||||
@ -741,6 +782,9 @@ _gtk_builder_construct (GtkBuilder *builder,
|
||||
}
|
||||
g_array_free (parameters, TRUE);
|
||||
|
||||
if (info->bindings)
|
||||
gtk_builder_take_bindings (builder, obj, info->bindings);
|
||||
|
||||
/* put it in the hash table. */
|
||||
_gtk_builder_add_object (builder, info->id, obj);
|
||||
|
||||
@ -909,10 +953,46 @@ gtk_builder_apply_delayed_properties (GtkBuilder *builder)
|
||||
g_slist_free (props);
|
||||
}
|
||||
|
||||
static inline void
|
||||
free_binding_info (gpointer data, gpointer user)
|
||||
{
|
||||
BindingInfo *info = data;
|
||||
g_free (info->target_property);
|
||||
g_free (info->source);
|
||||
g_free (info->source_property);
|
||||
g_slice_free (BindingInfo, data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gtk_builder_create_bindings (GtkBuilder *builder)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
for (l = builder->priv->bindings; l; l = g_slist_next (l))
|
||||
{
|
||||
BindingInfo *info = l->data;
|
||||
GObject *source;
|
||||
|
||||
if ((source = gtk_builder_get_object (builder, info->source)))
|
||||
g_object_bind_property (source, info->source_property,
|
||||
info->target, info->target_property,
|
||||
info->flags);
|
||||
else
|
||||
g_warning ("Could not find source object '%s' to bind property '%s'",
|
||||
info->source, info->source_property);
|
||||
|
||||
free_binding_info (info, NULL);
|
||||
}
|
||||
|
||||
g_slist_free (builder->priv->bindings);
|
||||
builder->priv->bindings = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_builder_finish (GtkBuilder *builder)
|
||||
{
|
||||
gtk_builder_apply_delayed_properties (builder);
|
||||
gtk_builder_create_bindings (builder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,6 +27,9 @@ property = element property {
|
||||
attribute translatable { "yes" | "no" } ?,
|
||||
attribute comments { text } ?,
|
||||
attribute context { text } ?,
|
||||
(attribute bind-source { text },
|
||||
attribute bind-property { text },
|
||||
attribute bind-flags { text } ?) ?,
|
||||
text ?
|
||||
}
|
||||
|
||||
|
@ -98,6 +98,21 @@
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<group>
|
||||
<attribute name="bind-source">
|
||||
<text/>
|
||||
</attribute>
|
||||
<attribute name="bind-property">
|
||||
<text/>
|
||||
</attribute>
|
||||
<optional>
|
||||
<attribute name="bind-flags">
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</group>
|
||||
</optional>
|
||||
<optional>
|
||||
<text/>
|
||||
</optional>
|
||||
|
@ -587,8 +587,11 @@ parse_property (ParserData *data,
|
||||
GError **error)
|
||||
{
|
||||
PropertyInfo *info;
|
||||
gchar *name = NULL;
|
||||
gchar *context = NULL;
|
||||
const gchar *name = NULL;
|
||||
const gchar *context = NULL;
|
||||
const gchar *bind_source = NULL;
|
||||
const gchar *bind_property = NULL;
|
||||
GBindingFlags bind_flags = G_BINDING_DEFAULT;
|
||||
gboolean translatable = FALSE;
|
||||
ObjectInfo *object_info;
|
||||
int i;
|
||||
@ -605,7 +608,7 @@ parse_property (ParserData *data,
|
||||
for (i = 0; names[i] != NULL; i++)
|
||||
{
|
||||
if (strcmp (names[i], "name") == 0)
|
||||
name = g_strdelimit (g_strdup (values[i]), "_", '-');
|
||||
name = values[i];
|
||||
else if (strcmp (names[i], "translatable") == 0)
|
||||
{
|
||||
if (!_gtk_builder_boolean_from_string (values[i], &translatable,
|
||||
@ -618,7 +621,21 @@ parse_property (ParserData *data,
|
||||
}
|
||||
else if (strcmp (names[i], "context") == 0)
|
||||
{
|
||||
context = g_strdup (values[i]);
|
||||
context = values[i];
|
||||
}
|
||||
else if (strcmp (names[i], "bind-source") == 0)
|
||||
{
|
||||
bind_source = values[i];
|
||||
}
|
||||
else if (strcmp (names[i], "bind-property") == 0)
|
||||
{
|
||||
bind_property = values[i];
|
||||
}
|
||||
else if (strcmp (names[i], "bind-flags") == 0)
|
||||
{
|
||||
if (!_gtk_builder_flags_from_string (G_TYPE_BINDING_FLAGS, values[i],
|
||||
&bind_flags, error))
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -633,10 +650,30 @@ parse_property (ParserData *data,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bind_source && bind_property)
|
||||
{
|
||||
BindingInfo *binfo = g_slice_new0 (BindingInfo);
|
||||
|
||||
binfo->target_property = g_strdup (name);
|
||||
binfo->source = g_strdup (bind_source);
|
||||
binfo->source_property = g_strdup (bind_property);
|
||||
binfo->flags = bind_flags;
|
||||
|
||||
object_info->bindings = g_slist_prepend (object_info->bindings, binfo);
|
||||
}
|
||||
else if (bind_source || bind_property)
|
||||
{
|
||||
error_missing_attribute (data, element_name,
|
||||
(bind_source) ? "bind-property" : "bind-source",
|
||||
error);
|
||||
return;
|
||||
}
|
||||
|
||||
info = g_slice_new0 (PropertyInfo);
|
||||
info->name = name;
|
||||
info->name = g_strdelimit (g_strdup (name), "_", '-');
|
||||
info->translatable = translatable;
|
||||
info->context = context;
|
||||
info->bound = (bind_source != NULL && bind_property != NULL);
|
||||
info->context = g_strdup (context);
|
||||
info->text = g_string_new ("");
|
||||
state_push (data, info);
|
||||
|
||||
@ -648,6 +685,8 @@ free_property_info (PropertyInfo *info)
|
||||
{
|
||||
g_free (info->data);
|
||||
g_free (info->name);
|
||||
g_free (info->context);
|
||||
/* info->text is already freed */
|
||||
g_slice_free (PropertyInfo, info);
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ typedef struct {
|
||||
gchar *constructor;
|
||||
GSList *properties;
|
||||
GSList *signals;
|
||||
GSList *bindings;
|
||||
GObject *object;
|
||||
CommonInfo *parent;
|
||||
gboolean applied_properties;
|
||||
@ -62,7 +63,8 @@ typedef struct {
|
||||
gchar *name;
|
||||
GString *text;
|
||||
gchar *data;
|
||||
gboolean translatable;
|
||||
gboolean translatable:1;
|
||||
gboolean bound:1;
|
||||
gchar *context;
|
||||
} PropertyInfo;
|
||||
|
||||
@ -75,6 +77,15 @@ typedef struct {
|
||||
gchar *connect_object_name;
|
||||
} SignalInfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GObject *target;
|
||||
gchar *target_property;
|
||||
gchar *source;
|
||||
gchar *source_property;
|
||||
GBindingFlags flags;
|
||||
} BindingInfo;
|
||||
|
||||
typedef struct {
|
||||
TagInfo tag;
|
||||
gchar *library;
|
||||
|
@ -2777,6 +2777,62 @@ test_no_ids (void)
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
static void
|
||||
test_property_bindings (void)
|
||||
{
|
||||
const gchar *buffer =
|
||||
"<interface>"
|
||||
" <object class=\"GtkWindow\" id=\"window\">"
|
||||
" <child>"
|
||||
" <object class=\"GtkVBox\" id=\"vbox\">"
|
||||
" <property name=\"visible\">True</property>"
|
||||
" <property name=\"orientation\">vertical</property>"
|
||||
" <child>"
|
||||
" <object class=\"GtkCheckButton\" id=\"checkbutton\">"
|
||||
" <property name=\"active\">false</property>"
|
||||
" </object>"
|
||||
" </child>"
|
||||
" <child>"
|
||||
" <object class=\"GtkButton\" id=\"button\">"
|
||||
" <property name=\"sensitive\" bind-source=\"checkbutton\" bind-property=\"active\" bind-flags=\"sync-create\">false</property>"
|
||||
" </object>"
|
||||
" </child>"
|
||||
" <child>"
|
||||
" <object class=\"GtkButton\" id=\"button2\">"
|
||||
" <property name=\"sensitive\" bind-source=\"checkbutton\" bind-property=\"active\" />"
|
||||
" </object>"
|
||||
" </child>"
|
||||
" </object>"
|
||||
" </child>"
|
||||
" </object>"
|
||||
"</interface>";
|
||||
|
||||
GtkBuilder *builder;
|
||||
GObject *checkbutton, *button, *button2, *window;
|
||||
|
||||
builder = builder_new_from_string (buffer, -1, NULL);
|
||||
|
||||
checkbutton = gtk_builder_get_object (builder, "checkbutton");
|
||||
g_assert (GTK_IS_CHECK_BUTTON (checkbutton));
|
||||
g_assert (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbutton)));
|
||||
|
||||
button = gtk_builder_get_object (builder, "button");
|
||||
g_assert (GTK_IS_BUTTON (button));
|
||||
g_assert (!gtk_widget_get_sensitive (GTK_WIDGET (button)));
|
||||
|
||||
button2 = gtk_builder_get_object (builder, "button2");
|
||||
g_assert (GTK_IS_BUTTON (button2));
|
||||
g_assert (gtk_widget_get_sensitive (GTK_WIDGET (button2)));
|
||||
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton), TRUE);
|
||||
g_assert (gtk_widget_get_sensitive (GTK_WIDGET (button)));
|
||||
g_assert (gtk_widget_get_sensitive (GTK_WIDGET (button2)));
|
||||
|
||||
window = gtk_builder_get_object (builder, "window");
|
||||
gtk_widget_destroy (GTK_WIDGET (window));
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
@ -2827,6 +2883,7 @@ main (int argc, char **argv)
|
||||
g_test_add_func ("/Builder/LevelBar", test_level_bar);
|
||||
g_test_add_func ("/Builder/Expose Object", test_expose_object);
|
||||
g_test_add_func ("/Builder/No IDs", test_no_ids);
|
||||
g_test_add_func ("/Builder/Property Bindings", test_property_bindings);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user