Hello everyone! in the past few weeks I been working on a survey for Glade. The idea is that it will help us to better know our user base and thus allow us to take inform decisions to improve Glade users experience.
So please take a few minutes to complete it!
First of all I want to thanks everyone involved in GUADEC organization specially to the GNOME foundation for sponsoring me once again.
It is been great meeting with old friends and making new ones!
So after Tristan’s talk UI developer experience with Glade/GtkBuilder where he talked about the new template API some good friend of us, lets see if you can guess who, came and ask:
friend: How do I port my widgets to the new templates stuff?
friend: I do not want to redo all of them in glade
me: What kind of widgets?
friend: “A grid with some s#it in it!”
me: hmmm…
So we talked about it and told him if that he was that lazy not to redo all widgets in glade manually he could do some function that iterate over containers and spit some xml to get at least the hierarchy right.
As we all know the lazier a programmer is the better, since it will end up writing a program to do its chores!
Anyways I did!
I made a function you can paste in your program and use it together with libgladeui api to dump a runtime GtkWidget to an xml definition.
#include <gladeui/glade.h>
/* Create a Glade project */
GladeProject *project = glade_project_new ();
/* add as many widgets as you want in the project */
glade_project_add_widget_from_object (project, widget, NULL);
/* And then save it to a file */
glade_project_save (project, "myclass.ui", NULL);
And that is all you have to do if the widget is simple enough.
You will have to mark internal children manually since there is no easy way to introspect them, say for example you want to dump a GtkDialog derived widget…
#include <gladeui/glade.h>
/* Create a Glade project */
GladeProject *project = glade_project_new ();
/* We need to mark every internal children manually */
INTERNAL_CHILD (gtk_dialog_get_action_area (GTK_DIALOG (widget)),
"action_area");
INTERNAL_CHILD (gtk_dialog_get_content_area (GTK_DIALOG (widget)),
"vbox");
/* add as many widgets as you want in the project */
glade_project_add_widget_from_object (project, widget, NULL);
/* And then save it to a file */
glade_project_save (project, "myclass.ui", NULL);
This is obviously hacky code, it was not heavily tested and will probably make gladeui and gtk API complain a lot but it works pretty well for what is intended.
BTW you have to link with gladeui-2.0 library for this code to work!
New Drag & Drop support in 3.15 development series:
First of all I would like to thanks once again to the foundation for sponsoring my trip to Brussels to attend the Developer Experience Hackhest and Alberto for inviting me in the first place!
After the hackfest we all agreed that Glade needs some love to make it more newbie friendly, nothing we did not already knew, but please do not move along Glade needs your help!
On of the thing that new people find the most difficult to understand about creating GUI with Gtk+/Glade is the container packing paradigm which is very powerful once you get used to it but not as intuitive as one would like.
A way to improve this situation would be to create a new free form layout container similar to Java’s GroupLayout as suggested by Alex in this mail which of course is a lot of work specially in Glade so… help and/or sponsorship is welcome!!
But for now being able to drag & drop widgets around should make thinks easier, right?
The next thing we want to address is the property editor. Not only does not look good (In its defense the whole thing is autogenerated) but thanks to wide screen displays being ubiquitous nowadays it is wasting some precious vertical space :S
This is how it currently looks like
So… to save some vertical space and make it look more modern this are the changes I made
New ATK icon
Replaced toggle buttons with switches
Fields do not expand by default
Replaced text field edit button with an entry edit icon
Removed class field title
Moved clear and help buttons to the top of the notebook
The last item is definitely not definitive since it might make more sense to move it inside the scrolled window or simply to the toolbar.
As you can see there is lot of room for improvement so if you come up with a good idea help us!
What if you want to reference an object you created from a GtkBuilder script? or even more add children to a container created outside the scope of GtkBuilder?
You can not!
But do not worry, a simple API like the following will allow us to reference any external object from builder.
All you have to do is tell builder which objects are external setting the “external-object” parameter then is as simple as calling gtk_builder_expose_object() to actually expose it.
Please note that the external-object parameter is needed to avoid naming space conflicts.
Ok but what about adding children to an existing container ?¿
The first idea was to use a fragment of builder xml format (basically the <child> tag) and a new API gtk_builder_add_to_parent_*() but then I realize that would prevent us from setting properties on the external object and also it will not allow us to define anarchist objects that is objects outside the container hierarchy like for example a GtkAction.
So instead, I decided to add a new element: <template>
This new element is similar to <object> with the only difference it will be used as an entry point for the external object referenced by the template id.
Now with a template defined by this xml you can add children and set properties on an external object called “mybox”
I am really pleased to say that GNOME foundation is sponsoring me again to travel to Boston for my first hackfest after a very personally successful GUADEC.
Not only GUADEC was a success for me professionally but most importantly on a personal level since I got the opportunity to meet the persons behind those IRC nicks and email addresses I knew for so long.
Back in the day Glade used to generate code which was messy if was not handled properly, eventually everyone agreed it was better to use libglade instead and load the UI interface from a xml file, something that got consolidated with the advent of GtkBuilder in GTK+
So if it’s better for applications, shouldn’t be the same for classes?
I believe so, this is why I am continuing the work that Tristan started in composite-widget-templates GTK+ branch in my own branch named composite-template.
The idea is pretty simple, instead of hand coding composite children in _init(), like most widget classes do or in _constructor() where they should all we need to do is set a template in _class_init() as follow
and voilà! GtkContainer class will build your children from that template using GtkBuilder at construction time. If you need to expose an internal child then all you have to do is declare it as follow
And if you do not like the idea of depending on a file, like I do, for your widget class to work you can use a string or even better a GResource.
but Juan, what about performance?
Obviously there is some extra overhead in the parsing but is not going to be significant in a medium/big size project that already uses GtkBuilder for it’s main interface. That being said if we want to optimize things the easy way would be to use EXI (Efficient XML Interchange) format which is basically a XML binary format, it would save use some memory space and the need to actually parse the UI definition. The hard way would be to rethink GtkBuilder internal data model and transform it into a binary format that can be saved persistently. Implementing either of these approaches at the GMarkup level would not only be helpful for composite classes but more importantly for applications with big UI files.
Really wait, Say that again!
Ok, I should probably had started with an example, lets say you have a login dialog like this
This code show the differences implementing a composite object the regular way and using templates. Note that USE_TEMPLATE macro is used to choose either implementation at compile time.
Since this example sets the template from a relative path the file foologindialog.ui has to be in the current working directory when you run the program.
And last but not least you need composite-templates Gtk+ branch for this to work
It was a long time since I did not give cross compiling a try and it turns out to be easier than expected if you use precompiled binaries from windows:mingw[1] project on the OpenSUSE Build Service. (OBS)
First of all we need to install the cross compiler, in Debian and Debian based distros do
$ sudo apt-get install mingw-w64
Then we need to download a python script [2] that makes downloading mingw packages from OBS easy.
CC: the cross compiler
MINGW_ROOT: is an absolute path we are going to use as a prefix for our build
PKG_CONFIG_LIBDIR: this way we tell pkg-config to use mingw libs
PKG_CONFIG_PATH: just to make sure we are not pointing some where else
PKG_CONFIG: ask pkg-config to replace .pc files prefix variable with the real path
And this is pretty much it, now we can proceed to run autogen.sh with –host and –target flags and glade should compile
git clone git://git.gnome.org/glade
cd glade
./autogen.sh --prefix=$MINGW_ROOT --host i686-w64-mingw32 \
--target i686-w64-mingw32 --enable-shared=yes --enable-static=no
make
make install
Before you can run it you need to update glib schemas bin file
The important function here is gimp_cairo_wilber() which uses a path created from a SVG path description string. This allows them to grab that string and paste it inside a SVG file and edit it with Inkscape for example. Which could be annoying if you have to do it frequently or if it has to be done by an artist that does not feel comfortable messing around with source code.
This is the SVG path for wilber (WordPress does not allow me to include a svg image)
<svgwidth="225"height="165"><gtransform="translate(-287,-438)"><pathid="path2987"d="m 509.72445,438.68864 c -8.24739,31.09081 -44.77407,52.85702 -77.8653,59.0601 6.66245,5.26814 11.01867,13.47826 11.01867,22.62501 1e-5,15.87371 -12.92175,28.64855 -28.79547,28.64855 -15.87372,0 -28.79547,-12.77484 -28.79547,-28.64855 0,-8.84972 3.98978,-16.76089 10.2841,-22.03735 -36.20146,-2.43256 -51.86122,-34.37828 -51.86123,-34.37826 l -1.02841,45.69077 c 0,4.7013 -0.59743,10.31207 -2.49756,18.65829 -0.33714,-0.50356 -0.66979,-0.97205 -1.02841,-1.46916 -8.90026,-12.33694 -21.90268,-19.02373 -32.7622,-18.07063 -3.61983,0.3177 -6.923,1.56607 -9.84335,3.67289 -11.68135,8.42727 -11.57317,28.50691 0.29384,44.9562 10.11908,14.02637 25.47866,20.85962 37.02274,17.33604 58.07995,40.4437 198.30291,67.68661 175.85805,-136.0439 z M 363.24953,501.1278 c 10.58249,-2e-5 19.24596,8.66347 19.24596,19.24595 0,10.58249 -8.66348,19.09904 -19.24596,19.09904 -10.58247,0 -19.09903,-8.51655 -19.09903,-19.09904 -1e-5,-10.58246 8.51656,-19.24595 19.09903,-19.24595 z m -57.44402,14.9854 c 5.87915,-2e-5 10.57793,5.72665 10.57793,12.78166 10e-6,7.05496 -4.69877,12.78166 -10.57793,12.78166 -5.87915,0 -10.72484,-5.72665 -10.72484,-12.78166 -2e-5,-7.05501 4.84569,-12.78166 10.72484,-12.78166 z M 440.821,552.54828 c 0,0 7.9294,1.4756 13.0755,6.90504 3.52231,3.71619 3.85558,9.70174 3.08522,17.92371 -0.77029,-3.49373 -2.08601,-5.61044 -3.08522,-8.08037 -10.88262,13.17996 -40.46669,13.79263 -77.8653,0.58767 40.60128,8.1206 61.35686,0.67581 73.45783,-8.66803 -3.1952,-4.12713 -8.66803,-8.66802 -8.66803,-8.66802 z m -6.17377,-27.95144 c 0,7.6429 -6.20294,13.84584 -13.84584,13.84584 -7.6429,0 -13.84584,-6.20294 -13.84584,-13.84584 0,-7.6429 6.20294,-13.84584 13.84584,-13.84584 7.6429,0 13.84584,6.20294 13.84584,13.84584 z m -56.6468,-1.59753 c 0,4.70333 -3.81719,8.52053 -8.52052,8.52053 -4.70333,0 -8.52052,-3.8172 -8.52052,-8.52053 0,-4.70332 3.81719,-8.52052 8.52052,-8.52052 4.70333,0 8.52052,3.8172 8.52052,8.52052 z"/></g></svg
<svg width="225" height="165">
<g transform="translate(-287,-438)">
<path id="path2987" d="m 509.72445,438.68864 c -8.24739,31.09081 -44.77407,52.85702 -77.8653,59.0601 6.66245,5.26814 11.01867,13.47826 11.01867,22.62501 1e-5,15.87371 -12.92175,28.64855 -28.79547,28.64855 -15.87372,0 -28.79547,-12.77484 -28.79547,-28.64855 0,-8.84972 3.98978,-16.76089 10.2841,-22.03735 -36.20146,-2.43256 -51.86122,-34.37828 -51.86123,-34.37826 l -1.02841,45.69077 c 0,4.7013 -0.59743,10.31207 -2.49756,18.65829 -0.33714,-0.50356 -0.66979,-0.97205 -1.02841,-1.46916 -8.90026,-12.33694 -21.90268,-19.02373 -32.7622,-18.07063 -3.61983,0.3177 -6.923,1.56607 -9.84335,3.67289 -11.68135,8.42727 -11.57317,28.50691 0.29384,44.9562 10.11908,14.02637 25.47866,20.85962 37.02274,17.33604 58.07995,40.4437 198.30291,67.68661 175.85805,-136.0439 z M 363.24953,501.1278 c 10.58249,-2e-5 19.24596,8.66347 19.24596,19.24595 0,10.58249 -8.66348,19.09904 -19.24596,19.09904 -10.58247,0 -19.09903,-8.51655 -19.09903,-19.09904 -1e-5,-10.58246 8.51656,-19.24595 19.09903,-19.24595 z m -57.44402,14.9854 c 5.87915,-2e-5 10.57793,5.72665 10.57793,12.78166 10e-6,7.05496 -4.69877,12.78166 -10.57793,12.78166 -5.87915,0 -10.72484,-5.72665 -10.72484,-12.78166 -2e-5,-7.05501 4.84569,-12.78166 10.72484,-12.78166 z M 440.821,552.54828 c 0,0 7.9294,1.4756 13.0755,6.90504 3.52231,3.71619 3.85558,9.70174 3.08522,17.92371 -0.77029,-3.49373 -2.08601,-5.61044 -3.08522,-8.08037 -10.88262,13.17996 -40.46669,13.79263 -77.8653,0.58767 40.60128,8.1206 61.35686,0.67581 73.45783,-8.66803 -3.1952,-4.12713 -8.66803,-8.66802 -8.66803,-8.66802 z m -6.17377,-27.95144 c 0,7.6429 -6.20294,13.84584 -13.84584,13.84584 -7.6429,0 -13.84584,-6.20294 -13.84584,-13.84584 0,-7.6429 6.20294,-13.84584 13.84584,-13.84584 7.6429,0 13.84584,6.20294 13.84584,13.84584 z m -56.6468,-1.59753 c 0,4.70333 -3.81719,8.52053 -8.52052,8.52053 -4.70333,0 -8.52052,-3.8172 -8.52052,-8.52053 0,-4.70332 3.81719,-8.52052 8.52052,-8.52052 4.70333,0 8.52052,3.8172 8.52052,8.52052 z"/>
</g>
</svg
So after doing the same thing a few times for Glade
I decided to automate the process by creating a simple application that takes a SVG file and outputs C code for a cairo_path_t struct.
Generated header and source file:
Last week, inspired by Tristan’s GtkComposite branch idea of embedding builder xml into widget classes together with the old eagerness of implementing Glade UI with Glade itself I started hacking Glade in glade!
The first step was to recreate GladeWindow widget hierarchy in Glade, that was quick and easy. Then I started replacing the hard coded widgets with it but since I wanted to use the very same executable I was hacking on to edit its own UI definition file I had to make sure I got the palette, design view, inspector and property editor properly working before removing the old code.
Later on I proceeded to remove the old menu that was implemented using GtkUIManager… big mistake! How was I supposed to edit the file if I was not able to save the file, so I revert it and moved it to the end of the window and keep it until the new menu had a working save menu item!
After this, Glade was functional and only had one regression. For some reason GtkAccelLabel does not show up the accelerator when used with a related action. But that seems to be a Gtk+ bug (see bugzilla report)
The only problem left to solve was the dependency GladeWindow had on the UI file. I really did not like the idea of a class depending on a file so I decided it was a good time to give GResource a try! GResource allows you to easily embed resources into your code. All you have to do is define all your resources in GResource xml format, in this case only one file glade.glade.
And compile it into source using glib-compile-resourses tool, which can be done automatically with a couple of Makefile rules.
# Rules to compile resources%.h:%.gresource.xml
$(GLIB_COMPILE_RESOURCES)--generate $<--target=$@%.c:%.gresource.xml
$(GLIB_COMPILE_RESOURCES)--generate $<--target=$@# Dependencies rule, and do not forget to do a make after# editing the UI with glade!
glade-resources.c glade-resources.h: \
glade-resources.gresource.xml \
$(shell$(GLIB_COMPILE_RESOURCES)--generate-dependencies \
glade-resources.gresource.xml)
# Rules to compile resources
%.h: %.gresource.xml
$(GLIB_COMPILE_RESOURCES) --generate $< --target=$@
%.c: %.gresource.xml
$(GLIB_COMPILE_RESOURCES) --generate $< --target=$@
# Dependencies rule, and do not forget to do a make after
# editing the UI with glade!
glade-resources.c glade-resources.h: \
glade-resources.gresource.xml \
$(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies \
glade-resources.gresource.xml)
Once you have the build system setup it is as simply as using the resource uri instead of a file path.