Working with GtkBuildable Interface
GSoC progress
This post is in continuation of previous posts on a container widget implementation in libhandy i.e., an adaptive-grid widget implementation for libhandy.
Work so far
In the last few posts, we got to see how the backbone of the HdyGrid is taking shape. But, one thing that was yet to decide is how to take the weight for columns from the XML file. For that, we thought to accept it as the widget’s property (simply put, take input as string of comma-separated weights and then process it). Another option we thought is to have a custom tag to enable us to take the weight for a column.
We will be looking at how we can implement (the later option) in code so that in XML, we could do something as follows —
<object class=”HdyGrid”>
...
<columns>
<column>
<property name="position">1</property>
<property name="weight">1</property>
</column>
<column>
<property name="position">3</property>
<property name="weight">1</property>
</column>
<columns>
...
</object>
And to have this custom tag <columns>
we need to extend the GtkBuilder format with the help of GtkBuildable interface so that at the time of deserialization of UI descriptors, we get the weights for the columns.
Implementation detail
Now, let's see how we can proceed. So, to start, the interface provides a few virtual functions dedicated to parsing any data inside the tags. Some of which are —
custom_tag_start() - // Called for the start of custom tag to handle an elementcustom_tag_end() - // Called for the end tag of each custom element that is handledcustom_finished() - // Called for each custom tag handled by the buildable when the builder finishes parsing
Other vfuncs in the interface have not been mentioned.
Of the few functions mentioned, we need to provide a custom implementation for custom_tag_start and custom_tag_end to provide our custom logic for detecting and initialising the parsing of the custom tag. In some scenarios it custom_finished may also need to be replaced.
The replacement of vfuncs happens at the time of interface initialisation and hence we need to implement a _buildable_init (in our case its called hdy_grid_buildable_init) function which is called to initialise the interface.
custom_tag_start (defined here)
In custom_tag_start, we perform the following tasks —
- Check for the name of the tag and accordingly return TRUE if we want to parse the tag.
- Additionally, we need to do a check with our widget’s parent class if the tag is to be parsed there and chain up accordingly.
- Also, if we want to parse a tag, in addition to returning TRUE, we also need to provide a parsing logic (i.e. parser) and any custom data we might need later while parsing (which is generally the widget itself and the builder used to parse data; saved in a struct).
The parser (GMarkupParser)
This GType takes a list of functions in a specific order which is same as in the struct here.
In our case, we need to do something with each of the first three functions here, which is as follows —
- *_start_element —we need only at the start of
<property>
tag; get the property name (in our case, position or weight) to distinguish the value later (defined here) - *_end_element — we need only at the closing
</column>
tag; to store the weight for specified column position in a GList (defined here) - *_text — used to get the value for each property (defined here)
NOTE: The function names do not matter but their order matters.Though we can skip to provide a function, we cannot skip a function in between two desired functions (but we can set it as NULL).
custom_tag_end (defined here)
Lastly, in the call to this function, we need to free any memory allocation done in previous steps. Also, we need to chain up with our widget’s parent interface implementation (similar to what we did in tag_start).
The changes can be found in the latest commit here.