Implementing Gtk based Container-Widget: Part — 1
Setting up a basic container functionality
Some background
The widget that I’ll be implementing will go into the GNOME’s new widget library, Handy. Handy is a library that packs widgets for developing adaptive applications.
The name of the widget is not yet final, so for now, I’ll call it NewWidget. This NewWidget is a container widget for managing layout; therefore, its implementation starts by sub-classing GtkContainer.
A container widget is something which can take in other widgets as its child and based on the properties the widget has to handle their sizes and positions of the children, among other things.
The need for a new container widget was felt when the Handy’s maintainer (Adrien Plazas) noticed a layout in Gnome Contacts application, where the fields (name, contact, email, etc.) are arranged in a grid kind of arrangement, but their design was not adaptive. So on smaller screens, the fields struggled to get enough width and didn’t look pretty.
One could redesign the application to make it prettier, but having a widget for this type of use-case will significantly reduce the line of code for application developers.
This whole Gtk development is entirely new for me, and that’s why, for the first time, I’ll be implementing a container widget. This blog might be helpful for others too, who start with Gtk development.
Work so far
The first few days went into getting to understand the expectation and requirements to the NewWidget.
I started by looking at the implementation of the other container widgets (yes, it was overwhelming). Without getting much help from their implementation code, I started to code my NewWidget learning about the measuring, allocation and draw functions.
For now, I managed to display the children centred in the container, one below the other (in the order they are added) in NewWidget.
Implementation detail
Like any other widget the bare minimum functions that are necessary to implement include 1) _new()
used for creating a new instance of the widget, 2) _init()
for initialising instance variables, 3) _class_init()
for initialising/replacing virtual functions.
Apart from those implementations, when creating a container, there are some functions which are mandatory to implement; otherwise, the child widgets might not even appear on the screen.
The _add
and _remove
function:
- These two functions are necessary to implement for container widgets that have non-internal child widgets.
Note that internal children are child widgets that are not added by the user of the widget, but as part of the implementation of the widget.
While non-internal children are added by the user of the widget.
- What they are supposed to do is simply add and remove the child widget to/from the list (usually GList) of children. It is also required that we do
gtk_widget_set_parent()
andgtk_widget_unparent()
in the respective functions. Apart from handling parent widget, it is our job to call for a resize if and when needed for our container widgetgtk_widget_queue_resize()
.
Note that you cannot call
gtk_widget_queue_resize()
on a widget from inside its implementation of the GtkWidgetClass::size_allocate virtual method. Calls togtk_widget_queue_resize()
from inside GtkWidgetClass::size_allocate will be silently ignored.
The _forall
function:
- Called implicitly, this again is necessary in case we have non-internal children. This function accepts a callback function which is supposed to be called with every child of our container as an argument.
- As a direct consequence of the absence of this function, the child widgets of our container will not make it to the widget-tree hierarchy and therefore will not be drawn to the screen.
The _measure
function:
- This function does not affect directly in the existence of the widgets and you may happily skip its implementation (the new container widget will work fine for now).
- But talking about its importance, here we are supposed to measure the widget’s size (minimum, natural, etc.) taking into consideration the children’s respective sizes. The caller of this function then receives the value for how much space the widget is demanding.
- The entire size negotiation takes place in two phases: Requisition and Allocation. The measure function is called in the requisition phase and size_allocate (discussed below) function is called in the allocation phase.
The size requisition of a widget is it’s preferred width and height. Widgets are required to honor the size allocation they receive; a size request is only a request, and widgets must be able to cope with any size.
The _size_allocate
function:
- This function is called by parents on their children with allocation parameters set accordingly. It is the allocation phase of size negotiation.
- It is expected that we allocate the provided allocation parameters to our container widget (after adjusting if necessary). To do this, we can either use
gtk_widget_set_allocation()
or GtkWidgetClass’s virtual functionGTK_WIDGET_CLASS (hdy_grid_parent_class)->size_allocate (widget, allocation)
in this way. The interesting thing is that the later makes a call to the former under the hood (and does something more). - Also, it is our responsibility to (calculate and) allocate the sizes to each child inside our container using
gtk_widget_size_allocate()
.
These set of functions are necessary to successfully display the container children.
That’s it for now. Will share more info in my next blog.