OpenTTD Source: Hierarchical widgets

OpenTTD Source 13.1

Data Structures
Hierarchical widgets

Hierarchical widgets, also known as nested widgets, are widgets stored in a tree. At the leafs of the tree are (mostly) the 'real' widgets visible to the user. At higher levels, widgets get organized in container widgets, until all widgets of the window are merged. More...

## Data Structures
class | NWidgetBase
| Baseclass for nested widgets. More...
class | NWidgetResizeBase
| Base class for a resizable nested widget. More...
class | NWidgetCore
| Base class for a 'real' widget. More...
class | NWidgetContainer
| Baseclass for container widgets. More...
class | NWidgetHorizontal
| Horizontal container. More...
class | NWidgetHorizontalLTR
| Horizontal container that doesn't change the direction of the widgets for RTL languages. More...
class | NWidgetVertical
| Vertical container. More...
class | NWidgetMatrix
| Matrix container with implicitly equal sized (virtual) sub-widgets. More...
class | NWidgetSpacer
| Spacer widget. More...
class | NWidgetBackground
| Nested widget with a child. More...
class | NWidgetViewport
| Nested widget to display a viewport in a window. More...
class | NWidgetScrollbar
| Nested widget to display and control a scrollbar in a window. More...
class | NWidgetLeaf
| Leaf widget. More...

## Detailed Description

Hierarchical widgets, also known as nested widgets, are widgets stored in a tree. At the leafs of the tree are (mostly) the 'real' widgets visible to the user. At higher levels, widgets get organized in container widgets, until all widgets of the window are merged.

# Hierarchical widget kinds

A leaf widget is one of

- NWidgetLeaf for widgets visible for the user, or
- NWidgetSpacer for creating (flexible) empty space between widgets.

The purpose of a leaf widget is to provide interaction with the user by displaying settings, and/or allowing changing the settings.

A container widget is one of

- NWidgetHorizontal for organizing child widgets in a (horizontal) row. The row switches order depending on the language setting (thus supporting right-to-left languages),
- NWidgetHorizontalLTR for organizing child widgets in a (horizontal) row, always in the same order. All children below this container will also never swap order.
- NWidgetVertical for organizing child widgets underneath each other.
- NWidgetMatrix for organizing child widgets in a matrix form.
- NWidgetBackground for adding a background behind its child widget.
- NWidgetStacked for stacking child widgets on top of each other.

The purpose of a container widget is to structure its leafs and sub-containers to allow proper resizing.

# Hierarchical widget computations

The first 'computation' is the creation of the nested widgets tree by calling the constructors of the widgets listed above and calling `Add()` for every child, or by means of specifying the tree as a collection of nested widgets parts and instantiating the tree from the array.

After the creation step,

- The leafs have their own minimal size (min_x, min_y), filling (fill_x, fill_y), and resize steps (resize_x, resize_y).
- Containers only know what their children are, fill_x, fill_y, resize_x, and resize_y are not initialized.

Computations in the nested widgets take place as follows:

1. A bottom-up sweep by recursively calling NWidgetBase::SetupSmallestSize() to initialize the smallest size (smallest_x, smallest_y) and to propagate filling and resize steps upwards to the root of the tree.
2. A top-down sweep by recursively calling NWidgetBase::AssignSizePosition() with ST_SMALLEST to make the smallest sizes consistent over the entire tree, and to assign the top-left (pos_x, pos_y) position of each widget in the tree. This step uses fill_x and fill_y at each node in the tree to decide how to fill each widget towards consistent sizes. Also the current size (current_x and current_y) is set.
3. After initializing the smallest size in the widget tree with ST_SMALLEST, the tree can be resized (the current size modified) by calling NWidgetBase::AssignSizePosition() at the root with ST_RESIZE and the new size of the window. For proper functioning, the new size should be the smallest size + a whole number of resize steps in both directions (ie you can only resize in steps of length resize_{x,y} from smallest_{x,y}).

After the second step, the current size of the widgets are set to the smallest size.

To resize, perform the last step with the new window size. This can be done as often as desired. When the smallest size of at least one widget changes, the whole procedure has to be redone from the start.

See also
  Hierarchical widget parts