Dillo v3.1.1-14-g8f67d6e0
Loading...
Searching...
No Matches
Sizes of Dillo Widgets
The complex "widget sizes" is currently divided into three documents: Sizes of Dillo Widgets (this document), GROWS - Grand Redesign Of Widget Sizes, and Size requisitions depending on positions.
Furthermore, there are some notes in Miscellaneous Notes on Dw.
Info: Not up to date, see other documents.

Allocation

Each widget has an allocation at a given time, this includes

  • the position (x, y) relative to the upper left corner of the canvas, and
  • the size (width, ascent, descent).

The canvas is the whole area available for the widgets, in most cases, only a part is seen in a viewport. The allocation of the toplevel widget is exactly the allocation of the canvas, i.e.

  • the position of the toplevel widget is always (0, 0), and
  • the canvas size is defined by the size of the toplevel widget.

The size of a widget is not simply defined by the width and the height, instead, widgets may have a base line, and so are vertically divided into an ascender (which height is called ascent), and a descender (which height is called descent). The total height is so the sum of ascent and descent.

Sizes of zero are allowed. The upper limit for the size of a widget is defined by the limits of the C++ type int.

Allocation of a Widget

In the example in the image, the widget has the following allocation:

  • x = 50
  • y = 50
  • width = 150
  • ascent = 150
  • descent = 100

The current allocation of a widget is hold in dw::core::Widget::allocation. It can be set from outside by calling dw::core::Widget::sizeAllocate. This is a concrete method, which will call dw::core::Widget::sizeAllocateImpl (see code of dw::core::Widget::sizeAllocate for details).

For trivial widgets (like dw::Bullet), dw::core::Widget::sizeAllocateImpl does not need to be implemented. For more complex widgets, the implementation should call dw::core::Widget::sizeAllocate (not dw::core::Widget::sizeAllocateImpl) on all child widgets, with appropriate child allocations. dw::core::Widget::allocation should not be changed here, this is already done in dw::core::Widget::sizeAllocate.

Requisitions

A widget may prefer a given size for the allocation. This size, the requisition, should be returned by the method dw::core::Widget::sizeRequestImpl. In the simplest case, this is independent of the context, e.g. for an image. dw::Image::sizeRequestImpl returns the following size:

  • If no buffer has yet been assigned (see dw::Image for more details), the size necessary for the alternative text is returned. If no alternative text has been set, zero is returned.
  • If a buffer has been assigned (by dw::Image::setBuffer), the root size is returned (i.e. the original size of the image to display).

This is a bit simplified, dw::Image::sizeRequestImpl should also deal with margins, borders and paddings, see dw::core::style.

From the outside, dw::Image::sizeRequest should be called, which does a bit of optimization. Notice that in dw::Image::sizeRequestImpl, no optimization like lazy evaluation is necessary, this is already done in dw::Image::sizeRequest.

A widget, which has children, will likely call dw::Image::sizeRequest on its children, to calculate the total requisition.

The caller (this is either the dw::core::Layout, or the parent widget), may, but also may not consider the requisition. Instead, a widget must deal with any allocation. (For example, dw::Image scales the image buffer when allocated at another size.)

Size Hints

Info: Size hints have been removed, see GROWS - Grand Redesign Of Widget Sizes.

Width Extremes

dw::Table uses width extremes for fast calculation of column widths. The structure dw::core::Extremes represents the minimal and maximal width of a widget, as defined by:

  • the minimal width is the smallest width, at which a widget can still display contents, and
  • the maximal width is the largest width, above which increasing the width- does not make any sense.

Especially the latter is vaguely defined, here are some examples:

  • For those widgets, which do not depend on size hints, the minimal and the maximal width is the inherent width (the one returned by dw::core::Widget::sizeRequest).
  • For a textblock, the minimal width is the width of the widest (unbreakable) word, the maximal width is the width of the total paragraph (stretching a paragraph further would only waste space). Actually, the implementation of dw::Textblock::getExtremesImpl is a bit more complex.
  • dw::Table is an example, where the width extremes are calculated from the width extremes of the children.

Handling width extremes is similar to handling requisitions, a widget must implement dw::core::Widget::getExtremesImpl, but a caller will use dw::core::Widget::getExtremes.

Resizing

When the widget changes its size (requisition), it should call dw::core::Widget::queueResize. The next call of dw::core::Widget::sizeRequestImpl should then return the new size. See dw::Image::setBuffer as an example.

Interna are described in the code of dw::core::Widget::queueResize.

Incremental Resizing

A widget may calculate its size based on size calculations already done before. In this case, a widget must exactly know the reasons, why a call of dw::core::Widget::sizeRequestImpl is necessary. To make use of this, a widget must implement the following:

  1. There is a member dw::core::Widget::parentRef, which is totally under control of the parent widget (and so sometimes not used at all). It is necessary to define how parentRef is used by a specific parent widget, and it has to be set to the correct value whenever necessary.
  2. The widget must implement dw::core::Widget::markSizeChange and dw::core::Widget::markExtremesChange, these methods are called in two cases:
    1. directly after dw::core::Widget::queueResize, with the argument ref was passed to dw::core::Widget::queueResize, and
    2. if a child widget has called dw::core::Widget::queueResize, with the value of the parent_ref member of this child.

This way, a widget can exactly keep track on size changes, and so implement resizing in a faster way. A good example on how to use this is dw::Textblock.

Rules for Methods Related to Resizing

Which method can be called, when the call of another method is not finished? These rules are important in two circumstances:

  1. To know which method can be called, and, especially, which methods must not* be called, within the implementation of sizeRequestImpl* (called by sizeRequest), markSizeChange, and markExtremesChange* (the latter two are called by queueResize).
  2. On the other hand, to make sure that the calls, which are allowed, are handled correctly, especially in implementations of sizeRequestImpl*, markSizeChange, markExtremesChange

Generally, the rules defined below are, in case of doubt, rather strict; when changing the rules, loosening is simpler than to tighten them, since this will make it neccessary to review old code for calls previously allowed but now forbidden.

Short recap:

  • QueueResize directly calls markSizeChange and markExtremesChanges*, and queues an idle function for the actual resizing (dw::core::Layout::resizeIdle). (The idle function is called some time after queueResize is finished.)
  • The resize idle function first calls sizeRequest, then sizeAllocate*, for the toplevel widget.

In the following table, the rules are defined in detail. "Within call of ..." includes all methods called from the original method: the first row (queueResize) defines also the rules for markExtremesChanges* and markExtremesChanges, and in the second row (sizeAllocate), even sizeRequest has to be considered.

Info: Not up to date: queueResize can now be called recursively (so to speak). See code there.
Within call of ... ↓ ... is call allowed of ... ? → queueResize sizeAllocate sizeRequest getExtremes
queueResize No No1 No1 No1
sizeAllocate Yes Only for children2 Yes(?) Yes(?)
sizeRequest Yes3 No Limited4 Limited4
getExtremes Yes3 No Limited4 Limited4

1) Otherwise, since these other methods may be call queueResize, the limitation that queueResize must not call queueResize can be violated.

2) Could perhaps be loosened as for sizeRequest and getExtremes*, but there is probably no need.

3) Therefore the distinction between RESIZE_QUEUED and NEEDS_RESIZE*, and EXTREMES_QUEUED and EXTREMES_CHANGED, respectively.

4) Calls only for children are safe. In other cases, you take a large responsibility to prevent endless recursions by (typically indirectly) calling sizeRequest / getExtremes for direct ancestors.

Furthermore, sizeAllocate can only be called within a call of dw::core::Layout::resizeIdleId, so (if you do not touch dw::core) do not call it outside of sizeAllocateImpl. The other methods can be called outsize; e. g. sizeRequest is called in dw::Textblock::addWidget.

To avoid painful debugging, there are some tests for the cases that one method call is strictly forbidden while another method is called.

This could be done furthermore:

  • The tests could be refined.
  • Is it possible to define exacter rules, along with a proof that no problems (like endless recursion) can occur?