summaryrefslogtreecommitdiff
path: root/devdoc/dw-widget-sizes.doc
blob: ffc7fb5db448a8b2a8bcb9f6b02ff433e7338ea6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/** \page dw-widget-sizes Sizes of Dillo Widgets

<div style="border: 2px solid #ffff00; margin: 1em 0;
  padding: 0.5em 1em; background-color: #ffffe0">The complex "widget
  sizes" is currently divided into three documents: **Sizes of Dillo
  Widgets** (this document), \ref dw-grows, and \ref
  dw-size-request-pos. </div>

<div style="border: 2px solid #ff4040; margin: 1em 0;
  padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
  Not up to date, see other documents.</div>

Allocation
==========

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

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

The \em 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 \em ascent), and a
descender (which height is called \em descent). The total height is so
the sum of \em ascent and \em descent.

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

\image html dw-size-of-widget.png Allocation of a Widget

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

- \em x = 50
- \em y = 50
- \em width = 150
- \em ascent = 150
- \em 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
\em 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
==========

<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
Size hints have been removed, see \ref dw-grows.</div>


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.

<h3>Incremental Resizing</h3>

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.

<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
Not up to date: *queueResize* can now be called recursively (so to
speak). See code there.</div>

<table>
   <tr>
      <th>Within call of ... ↓
      <th>... is call allowed of ... ? →
      <th>queueResize
      <th>sizeAllocate
      <th>sizeRequest
      <th>getExtremes
   <tr>
      <th colspan=2>queueResize
      <td>No
      <td>No<sup>1</sup>
      <td>No<sup>1</sup>
      <td>No<sup>1</sup>
   <tr>
      <th colspan=2>sizeAllocate
      <td>Yes
      <td>Only for children<sup>2</sup>
      <td>Yes(?)
      <td>Yes(?)
   <tr>
      <th colspan=2>sizeRequest
      <td>Yes<sup>3</sup>
      <td>No
      <td>Limited<sup>4</sup>
      <td>Limited<sup>4</sup>
   <tr>
      <th colspan=2>getExtremes
      <td>Yes<sup>3</sup>
      <td>No
      <td>Limited<sup>4</sup>
      <td>Limited<sup>4</sup>
   <tr>
     <td colspan=6><sup>1</sup>) Otherwise, since these other methods
may be call *queueResize*, the limitation that *queueResize* must not
call *queueResize* can be violated.

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

<sup>3</sup>) Therefore the distinction between *RESIZE_QUEUED* and
*NEEDS_RESIZE*, and *EXTREMES_QUEUED* and *EXTREMES_CHANGED*,
respectively.

<sup>4</sup>) 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.
</table>

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.&nbsp;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?

*/