[Glade-devel] Property Binding Support: Present and Future
Denis Washington
denisw at online.de
Thu Sep 22 05:50:04 EDT 2011
Am 17.09.2011 19:32, schrieb Tristan Van Berkom:
> Hi Denis,
> Sorry it took me a while to get back to you, I just got back to
> montreal and should have a little time...
No problem. Thanks for the comments!
>
> On Thu, 2011-09-01 at 09:58 +0200, Denis Washington wrote:
>> Hello,
>>
>> As you may know, I took part in Google Summer of Code this year (thanks
>> to Juan for mentoring me!) and worked on "GObject property binding
>> support for GtkBuilder and Glade":
>>
>> https://live.gnome.org/DenisWashington_GtkBuilder
>>
>> While talking on IRC with Tristan yesterday, I realized that while I
>> sent out weekly reports to gnome-soc-list and blogged about my work
>> twice, I never actually wrote anything about the code's status or
>> technical details on this mailing list. I would like to apologize for my
>> failure to do so and will try to make up for it by telling you
>> *everything* in this message: what I have done, how I have done it, what
>> works now, where the remaining problems are, and how these issues could
>> be overcome (this is where I really need your feedback!).
>>
>> For those who don't know, the objective of my work is to extend Glade
>> with support for creating bindings between widget properties in a
>> project. What this means is that you can define a property's value as
>> being directly dependent on the value of another property - whenever the
>> "source" property's value is set, the value of the "target" property is
>> automatically updated, either to the same value or a user-defined
>> transformation thereof (more on this later). GLib supports this through
>> its GBinding API [1]. The goal is to expose this functionality in Glade
>> so that the user can create, modify and delete property bindings in
>> Glade and save them as part of a UI file.
>>
>> This feature requires changes to both GTK+ and Glade, so I created
>> git.gnome.org branches for both of them, named "gtkbuilder-gbinding" and
>> "gbinding", respectively [2][3]. The GTK+ branch adds support for a new
>> bit of GtkBuilder syntax - the<binding> element - which makes it
>> possible for GtkBuilder objects to read property bindings from UI files.
>> It is used like this:
>>
>> <object name="button">
>> ...
>> <binding to="sensitive" from="active" source="checkbutton"/>
>> </object>
>>
>> which means: "Let the 'sensitive' property of 'button' always have the
>> same value as the 'active' property of 'checkbutton'." Thanks to the
>> existence of GBinding, the code changes required for this are pretty
>> small. No new API is introduced; all bindings are automatically created
>> at the time they are read in by gtk_builder_add_from*(). (But this might
>> change slightly; see the problem discussion later in this epic mail
>> message).
>>
>> The "gbinding" Glade branch is where the bulk of my work happened. It
>> adds a new "binding-source" property to GladeProperty for representing
>> property bindings in the data model and supports serialization and
>> deserialization of this information to<binding> elements. (See
>> glade_property_binding_read() and glade_property_binding_write() in
>> glade-property.c, respectively.) Furthermore, it augments Glade's
>> undo/redo framework with a glade_command_bind_property() command for
>> creating and deleting property bindings. On the UI side, this command is
>> exposed through a "Bind to source..." context menu item in the property
>> inspector. (For screenshots of how the UI currently looks, see my blog
>> posts [4][5].)
>
> The dialog is nice enough in general, I think it needs to expose the
> real untranslated property name as well, perhaps with the translated
> name in italic/grey beside it. It would even be interesting to include
> the proper class name introducing that property in the list.
>
> i.e.:
>
> GtkWidget:tooltip-text /Tooltip Text/
> GtkEntry:text /Text/
>
> (its nice to have the translated titles around, but it's usually
> more important to show something untranslated and informative here).
I used the translated names because these are what the property
inspector shows, so it would be arguably inconsistent if we highlighted
the internal property names as the primary identification means. I think
better would be:
Tooltip Text /GtkWidget:tooltip-text/
>> The UI and data model has been adapted to reflect the defined property
>> bindings:
>>
>> - glade_command_set_property() was modified to recursively set the value
>> of all properties bound to the originally set property. This means that
>> the effect of a property binding is immediately visible in the Glade
>> workspace.
>
> It sounds like a nice feature but I'm not sure it's the right place.
>
> Perhaps the implementation of glade_property_sync() would be a better
> location for this.
I chose this location so that all property set operations are recorded
in the command system (grouped with the original set command,
naturally), which makes undo trivial.
> Which also brings to mind, there should be some protection to avoid
> circular references and feedback loops which could be easily introduced
> by using bindings, perhaps Glade should simply allow the user to create
> dangerous documents and forcefully avoid feedback internally, or just
> refuse to create circular loops all together (not such a bad limitation
> at first thought...).
I think prohibit cycles, we would need to provide explicit support for
two-way bindings (such as offered by a g_object_bind_property() flag) to
retain the whole power, but this probably wouldn't be worth the
complication in the code.
I don't know how well GBinding handles cycles currently. It would be
best if we could avoid infinite loops on that level (e.g. by suspending
the binding while syncing the target's value to the source, so that a
resulting update of the source itself doesn't trigger a new sync), but
I'm not sure how feasible that is.
>> - In the property inspector, the edit widgets for bound properties are
>> insensitive (setting the value of a bound property doesn't make much
>> sense). Also, the tooltip of a bound property shows which other property
>> it is bound to.
>> (See glade-editor-property.c)
>
> If I understand correctly, you are updating property sensitivity
> directly based on whether a property is bound or not, this will
> probably fail in some conditions as the backend is responsible
> currently for updating state.
>
> If for instance, you bind the "label" property of a GtkButton
> and then set the button to be "custom content" or such (using
> the radio buttons in the button editor), then undo... will the
> tooltip of the insensitive "label" property be correct ?
I don't set the sensitivity in the sense of
glade_property_set_sensitive(). Rather, I just control the sensitivity
of the associated GladeEditorProperty in
glade_editor_property_sensitivity_cb(). Thus, the example you described
works correctly.
>> Also, I made some precautions to avoid invalid property bindings:
>>
>> - The "Bind to source..." dialog for choosing the source of a property
>> binding only allows you to select properties that have the same, or a
>> compatible, type, and are enabled (if they are optional) and sensitive
>> (if there are one of multiple alternative properties, e.g. "text",
>> "stock" and "embedded widget" in GtkButton). All other properties are
>> greyed out and moved to the end of the list.
>> (See glade_editor_property_show_bind_dialog() and its helper functions
>> in glade-editor-property.c)
>
> Right, this should at least take care of possible runtime warnings.
>
>>
>> - If the source or target of a property binding disappears because the
>> widget it belongs to is deleted, the binding is automatically removed
>> too. This is properly integrated into the undo/redo system, so undoing
>> the widget removal also brings the property binding back.
>> (See glade_command_delete_binding_refs() in glade-command.c)
>
> Will have to eventually review that in detail, but sounds like the
> right approach there.
>
>> One remaining issue with the current code is that it does not react to
>> property binding sources becoming disabled or insensitive. This is
>> currently not possible in a sane way as changes to a property's
>> enabled/sensitivity state are not tracked with the undo/redo framework
>> at the moment. (I had code to do this in the branch before, but it
>> worked with manual signal handling hackery and was removed later on
>> Tristan's request.) The obvious solution would be to change this by
>> introducing glade_command_set_property_enabled() and
>> glade_command_set_property_sensitive() and porting all of Glade to that.
>> If there is general agreement to do so, I would be willing to do that
>> work. In any case, this has to be fixed in some way before the branch is
>> ready to be merged into master.
>
> Those are the blockers for this integration.
>
> Porting that will take time and effort though, take a look at
> glade-gtk.c and note how sensitivity is generally driven, all of
> that needs to be ported to control sensitivity while updating
> property values at the editor level (that is, things now belong
> on the 'calling' side of GladeCommand instead of the other,
> 'data model' side).
I will look into that.
> This might mean extending GladeEditorProperty api to give the
> widget class adaptor code easy control on when to control
> sensitivity (the pre/post-commit signals sound like the right
> place to couple in sensitivity commands with property commands).
>
> It's also important that sensitivity cannot be controlled alone
> in a single command... because commands usually should represent
> an action taken in the document (a document is dirty after executing
> any command).
What about enabling/disabling? Does that invoke "set property" commands
currently?
>> Other than that, I don't know of any other major showstoppers, but an
>> extensive code review by Juan and Tristan might very well reveal some. ;)
>>
>> The big question, however, it how to support transformation functions
>> for property bindings. This GBinding feature allows you to define a
>> function that processes the value of a binding's source property before
>> it is applied to the target, which allows you to create bindings between
>> properties of different types and generally make property bindings much
>> more useful and interesting. Adding this into the GTK+ and Glade
>> branches is not a big problem in principle, and in fact I did just that
>> during Summer of Code. However, I had to later remove the code again,
>> the reason being on the GTK+/GLib side.
>>
>> The problem is when and how to resolve the transformation function names
>> that would be stored in the GtkBuilder file to the actual function
>> implementations. In my code, I moved all property binding creation to
>> two new API functions, gtk_builder_create_bindings() and
>> gtk_builder_create_bindings_full(), which take the same arguments as
>> gtk_builder_connect_signals*() and locate transformation functions the
>> same way (GModule or a custom callback, called GtkBuilderBindingFunc).
>>
>> Unfortunately, this setup means more work for language binding authors:
>> because transformation functions are specified as an argument to
>> g_object_bind_property_full() rather than by connecting a signal, a
>> language-specific GtkBuilderConnectFunc for
>> gtk_builder_connect_signals*() cannot be reused and each language
>> binding would be required to provide a GtkBuilderBindingFunc to replace
>> the use of GModule with something appropriate for the language. Also,
>> the introduction of another function to be called for every loaded
>> GtkBuilder function is really not ideal.
>>
>> Talking with Juan and Tristan, we concluded that a proper solution
>> probably requires changes to GBinding itself. More specifically, if
>> GBinding implemented the transformation function as being the handler of
>> a "transform" signal instead of an anonymous callback, one could reuse
>> gtk_builder_connect_signals*() to locate transformation functions and
>> move property binding creation there. For backwards compatibility and
>> convenience, the g_object_bind_property() API could still stay as it is
>> now. There would be some issues to sort out - for instance, how to
>> behave if multiple signal handlers are connected to the "transform"
>> signal - but it might be doable without breaking anything. This still
>> needs to be talked about with both the GLib and GTK+ team, though.
>>
>> For reference, you can still find my code with transformation function
>> support in the "gtkbuilder-gbinding" GTK+ and "gbinding-transform" Glade
>> branch.
>
> I don't want to discuss transformation functions in depth until
> finished landing this code in GTK+/Glade.
>
> If transformation functions are implemented at a later date, they should
> use similar semantics as signals, actually reading the code at this
> moment I don't see any reason why the normal GtkBuilderConnectFunc
> couldnt be used by the GtkBuilder during gtk_builder_connect_signals()
> to resolve any gbinding transform function signals.
*If* we have a GBinding transformation function facility based on
function, naturally, GtkBuilderConnectFunc is just fine. My comment
about it unsuitability was based on the assumption that GBinding is like
it is now, with transformation functions having to be specified on
g_object_bind_property().
> So *when* we look into this, my vague proposal runs like this:
> a.) Introduce "transform" signals on GBinding object and
> probably prefer the signal (the default handler falls
> back on invoking any registered transform function for
> backwards compatibility and then defaults to usual GValue
> transforms and copies).
>
> b.) Extend GtkBuilder binding parsing slightly to allow specification
> of a transform function... collect the GBinding objects and
> transform function data during the parse and cache them for use
> in gtk_builder_connect_signals() (possibly even in the same cache
> as the actual<signals>).
>
> c.) Connect the transform signal callbacks later in the normal way
> from gtk_builder_connect_signals()
This assumes that GValue provides default conversions between all pairs
of types. Is this the case? If not, creating the property bindings first
and adding transformation functions later won't work. Assuming that
GValue does handle all of this, however, this seems like a good plan.
> This will imply an api extension in GtkBuilder, so when it gets
> introduced in GTK+ Glade will need to be careful not to register
> transform functions for projects targeting a too old version of GTK+.
>
> Which leads me to another observation, the binding feature itself
> should not be available of the project is targeting< GTK+ 3.2
> (or whatever GTK+ version we get the feature into).
True.
>> Well, this is all there is to say about the code now. (This mail is
>> already way too long as it is. ;) I hope you now have a better insight
>> wrt what I have done during GSoC and what is still to do.
>>
>> As I wrote in the beginning, I need your feedback! If you have any
>> questions, remarks, or suggestions regarding the issues I outlined -
>> especially regarding the transformation function situation - I would be
>> excited to hear them! Thanks. :)
>
> Thanks a lot for the great effort done this year.
Thanks for your guidance.
> Again, sorry for taking so long to give you some attention.
No problem. :)
Regards,
Denis
More information about the Glade-devel
mailing list