Dillo
|
Each widget has an allocation at a given time, this includes
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 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.
In the example in the image, the widget has the following allocation:
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.
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:
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.)
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:
Especially the latter is vaguely defined, here are some examples:
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.
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.
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:
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.
Which method can be called, when the call of another method is not finished? These rules are important in two circumstances:
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:
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.
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: