GNOME Bugzilla – Bug 612036
Allow the encoding of GtkBuilder files into widget classes
Last modified: 2013-08-14 21:42:51 UTC
Created attachment 155449 [details] header file defining GtkComposite abstract class... Hi, I've been turning this idea around in my head and talking to various people about it on irc for a while, obviously this construct is going to suit higher level language bindings better than writing objects in C, but since my added api would need bindings, I attached a prototype written in C for reference. I'm going to come up with a more formal proposal and target it at gtk-devel-list on monday, this is a pre-submit implementation of the enhancement for reference for now. I'm about to attach gtkcomposite.[ch] as separate files, all that needs doing to build is to add it to gtk/Makefile.am and to <gtk/gtk.h>. This work depends on an api introduced by Marco in bug 447972. For a simple outline of this construct, basically we have a GtkBin superclass taking care of interfacing with the lower levels of GTK+; GtkBuilder. An implementor of the superclass can simply define callbacks in the UI to invoke methods dealing with his "Composite" object (generally you swap the data and make Composite come first, they can be derivable...). Then the implementor declares some variables on its instance, if the implementor declares properties that are "connect", then the value is resolved to the internally built instance which the connector desired to connect to - currently the correlation is by property name <--> GtkBuilder instance name, but could be changed by extending the added GtkParamSpecComposite. GtkParamSpecComposite: This is introduced as a property type to be handled and installed by GtkComposite implementors, in C the raw ugly mechanics of GObject are exposed, you do this by installing and handling properties with g_object_class_install_property(). Ideally, in some of our language bindings we can do this by simply adding syntactic sugar to the definition of the instance variable. Again, obviously this is better targetted at language bindings but IMO still very useful in C. The main reason why I want to target GTK+ is because I think it would do wonders for our devtools front, if we can have tools that assume this paradigm with all languages, we reduce complexity all around I think.
Created attachment 155450 [details] C file implementing GtkComposite class
Created attachment 155451 [details] A demo written in C So heres a little test case of how it can initially be used. Attention much boiler plate code ahead ;-)
It sounds interesting, but I need more time to study this. From what I understand, this is some way to nicely create composite widgets in glade that behave just like builtin composite widgets ?
Ok so I sent the mail to the list this morning and tried pretty hard to explain this patch in not-so-technical terms here: http://mail.gnome.org/archives/gtk-devel-list/2010-March/msg00040.html Noting some technical things that turned up since saturday: a.) GtkComposite should be just api extending GtkContainer, allowing any container widget to be extended as a composite widget when providing a .ui for the class. b.) The composite building should traverse and build every builder file specified for every composite class of an instance starting from the superclass and then descending, currently with this implementation every concrete class can only have one builder definition - instead they should extend the superclass by either appending themselves to the UI (i.e. if the base composite class is a GtkHBox, every subclass adds portions to the ui horizontally) - OR - more tricky - subclasses extending composite implementations should be able to insert their UI fragments into specified container widgets from the parent (those could be exposed automatically using composite properties of the parent class by name/key in the child builder file). For example, a GtkDialog defined by a builder file can expose an action area and vbox as composite children (or composite properties, same thing), then a dialog subclass could extend the dialog UI by defining toplevels in its UI file which register to the said extension points; the action area and vbox (this ofcourse would need some added metadata for toplevels in GtkBuilder files to decide what child to add themselves to automagically at load time). Also, it would be nice if we could use this paradigm all over GTK+ internally and then deprecate the push/pop_composite_child() apis and provide a _get_composite_child() that generically works for everyone (this could also eventually be useful for Glade to expose internal composite children for configuration and/or adding children).
... then deprecate the push/pop_composite_child() apis ... Those are really easy to deprecate since they don't do anything. Here are some questions I didn't really see answered yet in your attempts to describe this: - GtkBuildable has this concept of child-types, ie you have named slots that define the places where the app can add further child widgets. Do your Composites allow me to define such slots ? - Relatedly, there is the somewhat thorny issue that if I add a widget in one of these slots, it is a 'logical' child of the composite widget, but actually, it is the child of some internal container widget inside the composite widget hierarchy. Thus, it is not possible to have child properties that relate the 'logical' child and the composite widget. I assume your work does not solve this ? :-(
Actually, I hadn't thought about how composite widgets were going to cascade in detail when I wrote up the patch, and thats right the patch doesnt address that in detail. I'm pretty sure this is going to take some minor extensions to the format, what I'm aiming for is, - An interface (.ui) can include a composite widget and add children to it. - A class who extends the composite widget can also add children to it from its own added .ui fragment Well, I just wrote 3 paragraphs about how we could go and generically support 'child-type's of added children, basically it starts with adding a 'is-child-slot' attribute to the composite param spec and... work from there, it works but Im going to replace the paragraphs with... Don't use the 'slots' when defining composite classes; its made for customized code that always knows how to position it manually, its better to just use internal children. If a composite parent exposes an entry point to add widgets, it does so by defining an internal container widget, then it gets treated like the 'vbox' or 'action-area' of a GtkDialog, its an exposed container you can add widgets to with packing property detail. On a side note, this means we should be able to begin a composite GtkBuilder fragment with <child> instead of <object>, the <child> can optionally then come with an internal="internal-name" property and start adding to the parent from there. And it also means we need gtk_builder_set_parent_context() or such, to give GtkBuilder knowlage of the composite parent instance while trying to build/add children (builder would behave the same way in every respect except it would be started with a parent in "context" and expect <child> instead of <object> as the first relevant tag). I'm going to let it boil in my head for a while and hope people have some interesting comments, then I might give her another run through this coming weekend. ----------------------- Another side note about 'child-type'; take GtkNotebook as an example; probably the trickiest of these, but it could have been implemented using a collection of "Page" widgets with tab-label attributes individually, and that would remove the need to color in individual child types - Im not sure that the 'child-types' feature is justified outside the use cases of already written GTK+ code (seeing as we already have <child internal="internal-name">, and that gives us more context when adding widgets).
In your blog post about this, you asked for feedback from a language binding's point of view. After a couple of minutes of looking at this with my Perl bindings glasses on, I can see only one problem: the automatic connection of signal handlers. Your current code simply calls gtk_builder_connect_signals. Language bindings would need to be able to completely control this. I think this can be done by using gtk_builder_connect_signals_full with a GtkBuilderConnectFunc that defaults to the gtk_builder_connect_signals behavior but which can be changed by language bindings.
Created attachment 156091 [details] [review] New composite widgets patch This patch implements the same functionality in a new way with a few corrections: - Instead of introducing a class it implements the functionality extending the GtkContainer api, allowing any already existing widget to be derived and extended using this automated composite code. - GtkBuilder takes 2 new apis: o gtk_builder_add_to_parent_from_string() o gtk_builder_add_to_parent_from_file() These apis parse builder format in the normal way except provide a contextual parent GObject, the builder fragment that is parsed by this api should start with <child> instead of <object> - GtkContainer now allows you to setup the template file or buffer on a per class level - GtkContainer now exposes a GtkBuilderConnect func on a per class level to be used to connect signals to the proper code in the proper language (as code is written by class, it makes sence to expose this by class). - GtkContainer exposes a new api: gtk_container_get_composite_child() This is implemented automatically by introspection of GtkParamSpecComposite properties. - GtkBuilder now uses gtk_container_get_composite_child() if gtk_buildable_get_internal_child() was unsuccessful (allowing automatic exposure of internal composite children). Note that this patch currently comes all in one including Marco's patch for gtk_builder_expose_object(). ... Here comes another example tarball.
Created attachment 156092 [details] Another demo in C This test includes the same test-composite.c except now it derives directly from GtkWindow without the needed extra bin class. It also extends TestComposite to expose a composite child 'content-area'. A new test is added: TextExtended, which simply derives TestComposite and adds widgets to its internal 'content-area'. Note that using gtk_buildable_add_child() child-type 'slots' is still possible using this method, it means that the implementor still needs to know how to hand position the child widget and it still needs to implement gtk_buildable_add_child() in order to place it. Although I think its all around easier to just expose internal container widgets for the purpose of packing more widgets into a composite widget...
It looks like you used and older version of my patch. Take a look at the newer one: http://bugzilla-attachments.gnome.org/attachment.cgi?id=155380
Updating bug title to reflect the newer patch, hopefully the new title is more meaningful...
*** Bug 618739 has been marked as a duplicate of this bug. ***
Review of attachment 156091 [details] [review]: ::: gtk/gtkcontainer.c @@ +1105,3 @@ + + if (container_class->template) + g_free (container_class->template); No need for the if here, g_free is NULL-safe @@ +1111,3 @@ + if (container_class->template_file) + container_class->template_file = + (g_free (container_class->template_file), NULL); This is just sneaky. I prefer straight assignments instead of such trickery. @@ +1137,3 @@ + if (container_class->template) + container_class->template = + (g_free (container_class->template), NULL); Same comments apply here. @@ +1266,3 @@ + + if ((composite_child = + gtk_builder_get_object (builder, (pspecs[i])->name)) != NULL) Please put the assignment before the if. @@ +3016,3 @@ + static const GParamSpecTypeInfo pspec_info = { + sizeof (GtkParamSpecComposite), /* instance_size */ + 16, /* n_preallocs */ no point: * @n_preallocs: Prior to GLib 2.10, it specified the number of pre-allocated (cached) instances to reserve memory for (0 indicates no caching). Since GLib 2.10, it is ignored, since instances are allocated with the slice allocator
Another comment: template is a C++ keyword and can't be used in headers, so the GtkContainer member needs a different name, e.g. template_.
I've created a branch "composite-widget-templates" for this and added some things: - Amended patch as per your comments, used shortened term 'tmpl' in some places. - Changed Marco's patch for gtk_builder_expose_object() a bit o Now 'external' is called 'external-object' o Now an 'external-object' can also be referred to as object property values (not directly relevant but interesting to have) o The new attribute for <signal>/<property> tags are documented - Transformed GtkDialog and GtkMessageDialog to use gtk_container_class_set_template(). So, the dialog portion is an example of how this stuff can be used but to be able to include the dialog stuff we need to convert the remaining dialogs to be composite containers too (or at least migrate some of their code from _init() --> _contstructor()) The issue here is simply that you cannot access the widgets of a GtkDialog from a subclass at subclass_init() time because the child widgets dont exist yet; they will be built only once the container class constructor runs. Another annoying thing is if you have construct properties that want to access composite child widgets directly, in this case care needs to be taken when setting up those properties the first time (see GtkMessageDialogPrivate->message_type/buttons_type in the patch for an example of this dance). I guess those are the limitations but all in all I dont think its a bad trade-off and there's no way around it I can see (the automatic assignment of properties <--> composite children needs to happen in the constructor).
this is done
(In reply to comment #16) > this is done When and how exactly?
(In reply to comment #17) > (In reply to comment #16) > > this is done > > When and how exactly? See: https://mail.gnome.org/archives/gtk-devel-list/2013-April/msg00001.html And: http://blogs.gnome.org/tvb/2013/04/09/announcing-composite-widget-templates/ And: http://blogs.gnome.org/tvb/2013/05/29/composite-templates-lands-in-vala/ The C API can be found here: https://developer.gnome.org/gtk3/unstable/GtkWidget.html#gtk-widget-class-set-template (see all the 'template' related GtkWidgetClass APIs). Cheers
Thanks a lot for the pointers, this looks exactly like I imagined. :-) Can't wait for this become usable in PyGtk/Gi.