Status: implemented

For better or worse, we cannot get the layout we want with stock GTK widgets with gtk4. The main problem is that there’s no way to have the grid swindow expand vertically to a maximum size, but no more. As a result, we need a custom widget to handle the sizing of the grid on the left. See this diagram for an illustration of this:

PlayXwordColumn sizing
The expansion that requires a custom widget

This is what the PlayXwordColumn widget is for. Note, that despite the name, it’s more than just a column, but contains essentially the full widgetry for the Crossword playing area.

As we started adding more animations to handle multiple sizes better, this widget became a lot more complex, and increasingly specialized. We’ve made a conscious decision to lose the generality of gtk containers. Instead, we are focusing on precise control over the layout and behaviours (and animations).

Here’s a sketch as how it is laid out:

PlayXwordColumn overview
sketch of PlayXwordColumn


The only size that matters in the PlayXwordColumn is the width of the grid. That (plus any spacing) sets the minimum size of the game. The intro, notes, and clue labels are all height-for-width, and are anchored to this width.

Not surprisingly, the majority of the widget code is in the ::size_allocate method. Be careful when touching it.


We have two animations we run, primary and secondary. They are both triggered by the horizontal sizing. We don’t do animations based on the internal state of the crossword (yet — see below), and we don’t do animations based on any changes in vertical sizing. Events that can cause these are window resize events, or font / css changes (aka, zooming, but also system-wide ones).

Both animations are tracked by a float who’s value ranges between 0.0 and 1.0:

  • primary_value

  • secondary_value

The animation will just queue and allocation, which will update below as appropriate

Primary Animation

Affects: primary, clue, grid-swindow, notes

  • primary_value == 0.0: clue is visible; opacity is 1.0

  • primary_value == 0.0: primary is hidden; opacity is 0.0

  • primary_value == 0.0: grid-swindow and notes allocation ends below clue.

  • primary_value == 1.0: clue is hidden; opacity is 0.0

  • primary_value == 1.0: primary is visible; opacity is 1.0

  • primary_value == 1.0: grid-swindow and notes allocation extends into clue, if necessary

Secondary Animation

Affects: secondary

  • secondary_value == 0.0: secondary is hidden; opacity is 0.0

  • secondary_value == 1.0: secondary is visible; opacity is 1.0