Hello fellow hackers, today we bring you a new feature which I believe can very much improve the GTK+/GNOME developer story.
This is a feature I’ve been planning for a long time (I originally blogged about it 3 years ago) so I’m very excited about it having finally landed in GTK+, it’s my hope and ambition that this feature will help shape the future of user interface programming with GTK+.
Before I continue, I have to thank Juan Pablo Ugarte for keeping the dream alive and talking about this at the last GUADEC. Also recognition must be given to Openismus GmbH for sponsoring my full time work on this for the last few weeks, the time to completely focus on this task would not have been afforded me without them.
Unfortunately this post will be a little terse, the one I had planned which is a bit more relaxed and has some comic relief will not be ready on time. So, at the risk of being taken seriously, let’s continue with a brief overview of the APIs introduced to GTK+ and the actions taken.
What are Composite Widget Templates ?
Composite Widget Templates are an association of GtkWidget class data with GtkBuilder xml, which is to say that the xml which defines a composite widget is now a part of the definition of a widget class or type.
This feature automates the creation of composite widgets without the need for directly accessing the GtkBuilder APIs and comes with a few features that help to bind a GtkWidget with it’s GtkBuilder xml.
As of yesterday, 23 composite widget classes in GTK+, from simple classes such as GtkFontButton or GtkVolumeButton to more complex widget classes such as GtkFileChooserDefault and GtkPrintUnixDialog have all been ported to remove all manual user interface creation code, in favour of GtkBuilder xml.
So, how can I use it ?
There are three or four new APIs added to GtkWidget which play on the class data, currently they will only be available in C, but if you have a little imagination, you can see how this can be very useful in higher level languages, by extending the syntax and adding some keywords (hint: I have vala in mind as a top candidate).
Before I go into the API details here, I would like to point out a complete working example which I created today while writing this post. To give it a try, you need of course GTK+ master from today (or yesterday). For those who are interested I suggest you download that small tarball, and build it with one simple ‘make’ command.
First, lets start with an example of how to bind your template to your widget class:
static void my_widget_class_init (MyWidgetClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); /* Setup the template GtkBuilder xml for this class */ gtk_widget_class_set_template_from_resource (widget_class, "/org/foo/my/mywidget.ui"); } static void my_widget_init (MyWidget *widget) { /* Initialize the template for this instance */ gtk_widget_init_template (GTK_WIDGET (widget)); }
So, to bind some GtkBuilder XML to a widget class, we need to call two functions:
- gtk_widget_class_set_template_from_resource() binds some GtkBuilder XML to the class data
- gtk_widget_init_template() initializes the template for a given instance, this is currently needed for the base C apis, but both could certainly be automated in a highlevel language.
Next, we have a function which creates an implicit relationship between some instance variables and some objects defined in the GtkBuilder XML:
struct _MyWidgetPrivate { /* This is the entry defined in the GtkBuilder xml */ GtkWidget *entry; }; static void my_widget_class_init (MyWidgetClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); /* After having called gtk_widget_class_set_template_from_resource(), we can * define the relationship of the private entry and the entry defined in the xml. */ gtk_widget_class_bind_child (widget_class, MyWidgetPrivate, entry); g_type_class_add_private (gobject_class, sizeof (MyWidgetPrivate)); }
In the above code, we’ve defined a relationship between the MyWidgetPrivate pointer named ‘entry’ and the object in the GtkBuilder XML of the same name ‘entry’. The entry will be available for access on the subclassed GtkWidget instance private data at any time after gtk_widget_init_template() was called, until the given widget is disposed (at which time the pointer will become NULL). GTK+ takes care of memory managing such automated pointers, so it is ensured to exist for the lifetime of your instances.
Again, with highlevel bindings in mind, this could be implemented as some syntactic sugar in the actual declaration of the instance variable.
Finally there is one more point of interest in the API which is Callbacks. Functions in your widget class code can be specified as Callbacks which serve as endpoints for any signal connections defined in the GtkBuilder XML.
/* A callback handling a "clicked" event from a button defined in the GtkBuilder XML */ static void my_widget_button_clicked (MyWidget *widget, GtkButton *button) { g_print ("The button was clicked with entry text: %s\n", gtk_entry_get_text (GTK_ENTRY (widget->priv->entry))); } static void my_widget_class_init (MyWidgetClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); /* After having called gtk_widget_class_set_template_from_resource(), we can * declare callback ports that this widget class exposes, to bind with <signal> * connections defined in the GtkBuilder xml */ gtk_widget_class_bind_callback (widget_class, my_widget_button_clicked); }
Note that all signal connections defined in composite templates have the composite widget instance as user data by default.
In the above example code, my_widget_button_clicked() callback was declared with the assumption that the <signal> connection defined in the template was declared as ‘swapped’. Swapped signal connections are connections where the user data of the callback is returned first instead of the emitter. I think that this should be the default for composite widget callbacks as it blends in more naturally with normal class methods (where the class instance is always the first parameter).
This detail might not apply directly to higher level languages, which could achieve the above by adding some syntactic sugar in the declaration of a Callback method. Perhaps the instance will be implied as the ‘self’ variable.
I have also included some additional API to allow bindings to specify the GtkBuilderConnectFunc which should be used to make signal connections for a given widget class. I hope that bindings authors will contact me if they need any additional support in the GTK+ api to implement this.
Can I now use Glade to define my Composite Widget Templates ?
Of course silly ! That’s the whole point right ?
You’ll need Glade master from today as well, however I should be rolling a development snapshot with full support for this later this week as well.
All of GTK+’s composite widget classes have been recreated using Glade. Here is a screenshot of a GTK+ composite widget being edited in Glade:
Glade is still in it’s early stages supporting this, so there is hardly any features added here. I hope to work towards a brighter future where Glade can understand a multitude of composite widget templates as components of a single project, which will open the doors for some really nice and useful features.
Conclusion
All in all I have a lot to say about this work, but I’ll cut this blog post short for now, however I may be posting followups in the near future.
I’m very satisfied with this work, and I hope you will enjoy creating user interfaces as composite widget classes.
Love it. 🙂 I was waiting for something like this for a very long time.
This will change my life.
Yes, easy and fewer lines of code makes me feel better.