Implementing Gtk based Container-Widget: Part — 2

Working on a single row implementation

Ujjwal Kumar
4 min readJul 10, 2020

Some background

This write-up is in continuation of its previous part — setting up basic container functionality.

In the past couple of weeks, we moved on from just adding children to actually repositioning them (child widgets of the container, NewWidget) when enough space is not available for all widget to fit in the given width. Though the grid structure is yet to put in place, the widget could be seen taking shape already (look at below gif).

Work so far

In the last part of this blog, we set-up the basic container functionality that involved overriding some virtual functions in order to display some widgets on the screen. In this part, we cover how to handle a single row of widgets and adjusting/repositioning the widgets (determined by some properties/variables) when the container’s width is not enough to adjust all widgets in one horizontal line of widgets.

Note: For now, we are trying to work with only one row of widgets.

We introduced two new properties for children of NewWidget container namely — weight and position. Using these properties we try to develop a way which determines which widget needs to be repositioned and in what order the repositioning should happen.

In the above gif, you could see how the higher weighted widgets are positioned below the lower weighted widgets when we shrink the window’s width.

This repositioning is modified by changing the weights assigned to widgets, which changes the repositioning behaviour as below.

Weights: GtkComboBoxText = 0, GtkEntry = 1, GtkImage = 0
Weights: GtkComboBoxText = 0, GtkEntry = 1, GtkImage = 1

Implementation detail

In the previous blog, the basic methods were already introduced and the good thing is that no new functions were introduced during the work involved in these two weeks.

Past weeks our main focus was on implementing two functions namely — measure and size_allocate. The former being very crucial and the later is the heart of our widget. The entire allocation logic goes inside the size_allocate function, which is where we put our magical code to reposition the widgets whenever necessary.

The measure function

The measure happens in the size-requisition phase where a parent calls on child widgets asking for their preferred sizes.

  • The first point of interest is that the measure function performs size calculation for the following cases:
    1. preferred_height:
    this is pretty straight forward, we add up preferred heights for all the widgets using gtk_widget_get_preferred_height.
    2. preferred_width:
    this again is similar as above, the only difference is that here we use gtk_widget_get_preferred_width to get widths of the widgets.
    3. preferred_height_for_width:
    in this case, a widget is supposed to return its height requirement for the given width value.
    4. preferred_width_for_height:
    similar to the third case, a widget is supposed to return its width requirement for the given height value.
  • All four cases have their related virtual function in the GtkWidget class which we already overrode in our basic implementation.
  • Of all four, two cases are of our concern here. Case 2 and case 3

Measure — Preferred width

  • It is implemented as follows:
    - Group widgets by their weights
    - Sum preferred sizes of widgets belonging to the same group
    - Take the maximum sum value from the groups

Measure — Preferred height for width

  • Its implementation goes as follows:
    - Group the widgets by their weights
    - Starting from the group with the lowest weight, fit maximum possible groups into one line width (a row has multiple lines)
    - At the end of the above process, every group should be assigned a line to go into
    - For each line, bring widgets to their natural sizes using the following function gtk_distribute_natural_allocation()
    - For each line, line-height is equal to the height of the tallest widget in the line
    - Lastly, the preferred height is sum of heights of all the lines

The allocation function

Now let’s talk about the actual positioning of child widgets in the container space. The logic inside the size_allocate function is our special ingredient for the NewWidget.

Goals

  • If a row can’t fit its widgets in the container’s width, split the row into multiple lines
  • Widgets are grouped according to their weights
  • A line can have multiple groups
  • All widgets of a group should be in the same line at all times
  • The original order of children (when all placed in one row) should be maintained all the time.
  • Also, in a line, the relative order of the children should be maintained.

Approach

  • Sort the list of children based on their weights.
  • Get the preferred widths for the widgets, using gtk_widget_get_preferred_width().
  • Starting from the group with the lowest weight, fit maximum possible groups into one line width (a row has multiple lines)
  • At the end of the above step, every group should be assigned a line to go into
  • For each line, bring widgets to their natural sizes using the following function gtk_distribute_natural_allocation() and then distribute extra space among all widgets
  • Next, iterate over each line and sort the widgets of each line based on their position value.
  • Lastly, for each of the child do the following:
    Get the preferred height for the width of the widget
    Adjust allocation for this child widget
    Allocate space to the child widget

And this is how we achieved what is shown here :).

--

--