summaryrefslogtreecommitdiff
path: root/old/dw/html/dw-layout-views.html
blob: 3c86612af7343c77ccd23db640c3d96081c77c30 (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
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.8"/>
<title>Dillo: Layout and Views</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="https://www.dillo.org/dw/html/jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
 <tbody>
 <tr style="height: 56px;">
  <td style="padding-left: 0.5em;">
   <div id="projectname">Dillo
   </div>
  </td>
 </tr>
 </tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.8 -->
  <div id="navrow1" class="tabs">
    <ul class="tablist">
      <li><a href="index.html"><span>Main&#160;Page</span></a></li>
      <li class="current"><a href="pages.html"><span>Related&#160;Pages</span></a></li>
      <li><a href="namespaces.html"><span>Namespaces</span></a></li>
      <li><a href="annotated.html"><span>Classes</span></a></li>
      <li><a href="files.html"><span>Files</span></a></li>
    </ul>
  </div>
</div><!-- top -->
<div class="header">
  <div class="headertitle">
<div class="title">Layout and Views </div>  </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><p>Rendering of Dw is done in a way resembling the model-view pattern, at least formally. Actually, the counterpart of the model, the layout (<a class="el" href="classdw_1_1core_1_1Layout.html" title="The central class for managing and drawing a widget tree. ">dw::core::Layout</a>), does a bit more than a typical model, namely the layouting (delegated to the widget tree, see <a class="el" href="dw-layout-widgets.html">Layout and Widgets</a>), and the view does a bit less than a typical view, i.e. only the actual drawing.</p>
<p>Additionally, there is a structure representing common properties of the platform. A platform is typically related to the underlying UI toolkit, but other uses may be thought of.</p>
<p>This design helps to archieve two important goals:</p>
<ul>
<li>
<p class="startli">Abstraction of the actual drawing, by different implementations of <a class="el" href="classdw_1_1core_1_1View.html" title="An interface to encapsulate platform dependent drawing. ">dw::core::View</a>.</p>
<p class="endli"></p>
</li>
<li>
It makes portability simple. </li>
</ul>
<h2>Viewports</h2>
<p>Although the design implies that the usage of viewports should be fully transparent to the layout module, this cannot be fully achieved, for the following reasons:</p>
<ul>
<li>
<p class="startli">Some features, which are used on the level of <a class="el" href="classdw_1_1core_1_1Widget.html" title="The base class of all dillo widgets. ">dw::core::Widget</a>, e.g. anchors, refer to scrolling positions.</p>
<p class="endli"></p>
</li>
<li>
Size hints (see <a class="el" href="dw-layout-widgets.html">Layout and Widgets</a>) depend on the viewport sizes, e.g. when the user changes the window size, and so also the size of a viewport, the text within should be rewrapped. </li>
</ul>
<p>Therefore, <a class="el" href="classdw_1_1core_1_1Layout.html" title="The central class for managing and drawing a widget tree. ">dw::core::Layout</a> keeps track of the viewport size, the viewport position, and even the thickness of the scrollbars, they are relevant, see below for more details. If a viewport is not used, however, the size is not defined.</p>
<p>Whether a given <a class="el" href="classdw_1_1core_1_1View.html" title="An interface to encapsulate platform dependent drawing. ">dw::core::View</a> implementation is a viewport or not, is defined by the return value of <a class="el" href="classdw_1_1core_1_1View.html#a7e2c1f80643405545a07f00ccedb39db" title="Return, whether this view uses a viewport. ">dw::core::View::usesViewport</a>. If this method returns false, the following methods need not to be implemented at all:</p>
<ul>
<li>
<a class="el" href="classdw_1_1core_1_1View.html#a4ef7529de4e770675e48c83ad400bf88" title="Get the thickness of the horizontal scrollbar, when it is visible. ">dw::core::View::getHScrollbarThickness</a>, </li>
<li>
<a class="el" href="classdw_1_1core_1_1View.html#ab2bea7535dd5c4b70fb49432adffce5c" title="Get the thickness of the vertical scrollbar, when it is visible. ">dw::core::View::getVScrollbarThickness</a>, </li>
<li>
<a class="el" href="classdw_1_1core_1_1View.html#ae98d05f691f202dc1c292e68fb0170e4" title="Scroll the vieport to the given position. ">dw::core::View::scrollTo</a>, and </li>
<li>
<a class="el" href="classdw_1_1core_1_1View.html#a01fe6ec7ebbc0073c218175149b8306d" title="Set the viewport size. ">dw::core::View::setViewportSize</a>. </li>
</ul>
<h3>Scrolling Positions</h3>
<p>The scrolling position is the canvas position at the upper left corner of the viewport. Views using viewports must</p>
<ol>
<li>
change this value on request (<a class="el" href="classdw_1_1core_1_1View.html#ae98d05f691f202dc1c292e68fb0170e4" title="Scroll the vieport to the given position. ">dw::core::View::scrollTo</a>), and </li>
<li>
tell other changes to the layout, e.g. caused by user events (<a class="el" href="classdw_1_1core_1_1Layout.html#ace39379897c324b20ddc8c550a49c48b">dw::core::Layout::scrollPosChanged</a>). </li>
</ol>
<p>Applications of scrolling positions (anchors, test search etc.) are handled by the layout, in a way fully transparent to the view.</p>
<h3>Scrollbars</h3>
<p>A feature of the viewport size model are scrollbars. There may be a vertical scrollbar and a horizontal scrollbar, displaying the relationship between canvas and viewport height or width, respectively. If they are not needed, they are hidden, to save screen space.</p>
<p>Since scrollbars decrease the usable space of a view, <a class="el" href="classdw_1_1core_1_1Layout.html" title="The central class for managing and drawing a widget tree. ">dw::core::Layout</a> must know how much space they take. The view returns, via <a class="el" href="classdw_1_1core_1_1View.html#a4ef7529de4e770675e48c83ad400bf88" title="Get the thickness of the horizontal scrollbar, when it is visible. ">dw::core::View::getHScrollbarThickness</a> and <a class="el" href="classdw_1_1core_1_1View.html#ab2bea7535dd5c4b70fb49432adffce5c" title="Get the thickness of the vertical scrollbar, when it is visible. ">dw::core::View::getVScrollbarThickness</a>, how thick they will be, when visible.</p>
<p>Viewport sizes, which denote the size of the viewport widgets, include scrollbar thicknesses. When referring to the viewport <em>excluding</em> the scrollbars space, we will call it "usable viewport size", this is the area, which is used to display the canvas.</p>
<h2>Drawing</h2>
<p>A view must implement several drawing methods, which work on the whole canvas. If it is necessary to convert them (e.g. into <a class="el" href="classdw_1_1fltk_1_1FltkViewport.html">dw::fltk::FltkViewport</a>), this is done in a way fully transparent to <a class="el" href="classdw_1_1core_1_1Widget.html" title="The base class of all dillo widgets. ">dw::core::Widget</a> and <a class="el" href="classdw_1_1core_1_1Layout.html" title="The central class for managing and drawing a widget tree. ">dw::core::Layout</a>, instead, this is done by the view implementation.</p>
<p>There exist following situations:</p>
<ul>
<li>
<p class="startli">A view gets an expose event: It will delegate this to the layout (<a class="el" href="classdw_1_1core_1_1Layout.html#a7a9b129376723762f4fcdc14e1d73e44">dw::core::Layout::draw</a>), which will then pass it to the widgets (<a class="el" href="classdw_1_1core_1_1Widget.html#a2e7d05212aabad32824fd577fb7e3dd7">dw::core::Widget::draw</a>), with the view as a parameter. Eventually, the widgets will call drawing methods of the view.</p>
<p class="endli"></p>
</li>
<li>
<p class="startli">A widget requests a redraw: In this case, the widget will delegate this to the layout (<a class="el" href="classdw_1_1core_1_1Layout.html#a1e65014b2460fb2f66df84eb8b0064cc">dw::core::Layout::queueDraw</a>), which delegates it to the view (<a class="el" href="classdw_1_1core_1_1View.html#a0da55aba14e5b1582e2e2c6016c2d8c0" title="Queue a region, which is given in canvas coordinates, for drawing. ">dw::core::View::queueDraw</a>). Typically, the view will queue these requests for efficiency.</p>
<p class="endli"></p>
</li>
<li>
A widget requests a resize: This case is described below, in short, <a class="el" href="classdw_1_1core_1_1View.html#a70f66c62b0fa4153207019ea7818fedd" title="Queue the total viewport for drawing. ">dw::core::View::queueDrawTotal</a> is called for the view. </li>
</ul>
<p>If the draw method of a widget is implemented in a way that it may draw outside of the widget's allocation, it should draw into a <em>clipping view.</em> A clipping view is a view related to the actual view, which guarantees that the parts drawn outside are discarded. At the end, the clipping view is merged into the actual view. Sample code:</p>
<div class="fragment"><div class="line"><span class="keywordtype">void</span> Foo::draw (<a class="code" href="classdw_1_1core_1_1View.html">dw::core::View</a> *view, <a class="code" href="classdw_1_1core_1_1Rectangle.html">dw::core::Rectangle</a> *area)</div>
<div class="line">{</div>
<div class="line">   <span class="comment">// 1. Create a clipping view.</span></div>
<div class="line">   <a class="code" href="classdw_1_1core_1_1View.html">dw::core::View</a> clipView =</div>
<div class="line">      view-&gt;<a class="code" href="classdw_1_1core_1_1View.html#ac65430bcd8b9c6c18d5d184dc22d1615">getClippingView</a> (allocation.x, allocation.y,</div>
<div class="line">                             allocation.width, getHeight ());</div>
<div class="line"></div>
<div class="line">   <span class="comment">// 2. Draw into clip_view</span></div>
<div class="line">   clipView-&gt;doSomeDrawing (...);</div>
<div class="line"></div>
<div class="line">   <span class="comment">// 3. Draw the children, they receive the clipping view as argument.</span></div>
<div class="line">   <a class="code" href="classdw_1_1core_1_1Rectangle.html">dw::core::Rectangle</a> *childArea</div>
<div class="line">   <span class="keywordflow">for</span> (&lt;all relevant children&gt;) {</div>
<div class="line">      <span class="keywordflow">if</span> (child-&gt;intersects (area, &amp;childArea))</div>
<div class="line">         child-&gt;<a class="code" href="classdw_1_1core_1_1Rectangle.html#a499c5b7e4acaf3afa2cee3d71b2152df">draw</a> (clipView, childArea);</div>
<div class="line">   }</div>
<div class="line"></div>
<div class="line">   <span class="comment">// 4. Merge</span></div>
<div class="line">   view-&gt;<a class="code" href="classdw_1_1core_1_1View.html#a040f3722c187b7f73464f7d0dc38c80a">mergeClippingView</a> (clipView);</div>
<div class="line">}</div>
</div><!-- fragment --><p>A drawing process is always embedded into calls of <a class="el" href="classdw_1_1core_1_1View.html#a2325fd7198acc544b9547a4345d41f56" title="Called before drawing. ">dw::core::View::startDrawing</a> and <a class="el" href="classdw_1_1core_1_1View.html#a6bd3b85b5d155cd0b5fcaa44225380d1" title="Called after drawing. ">dw::core::View::finishDrawing</a>. An implementation of this may e.g. use backing pixmaps, to prevent flickering.</p>
<h2>Sizes</h2>
<p>In the simplest case, the view does not have any influence on the canvas size, so it is told about changes of the canvas size by a call to <a class="el" href="classdw_1_1core_1_1View.html#a5f07017e4e77649cd5280a66be6de669" title="Set the canvas size. ">dw::core::View::setCanvasSize</a>. This happens in the following situations:</p>
<ul>
<li>
<a class="el" href="classdw_1_1core_1_1Layout.html#a5adef4d301d59d70c551153bf3d78230">dw::core::Layout::addWidget</a>, </li>
<li>
<a class="el" href="classdw_1_1core_1_1Layout.html#ac080e495161e3bdbc1166de961d74f6c">dw::core::Layout::removeWidget</a> (called by <a class="el" href="classdw_1_1core_1_1Widget.html#afa654cec6369417221663a2583836496">dw::core::Widget::~Widget</a>), and </li>
<li>
<a class="el" href="classdw_1_1core_1_1Layout.html#afdc8081b83653fd1aa5bf5b41218045d">dw::core::Layout::queueResize</a> (called by <a class="el" href="classdw_1_1core_1_1Widget.html#ac00e44ccde79daf2b90247c352de67ef" title="This method should be called, when a widget changes its size. ">dw::core::Widget::queueResize</a>, when a widget itself requests a size change). </li>
</ul>
<h3>Viewports</h3>
<p>There are two cases where the viewport size changes:</p>
<ul>
<li>
<p class="startli">As an reaction on a user event, e.g. when the user changes the window size. In this case, the view delegates this change to the layout, by calling <a class="el" href="classdw_1_1core_1_1Layout.html#a51f1aecef5f041cdb398802f2349d6f6">dw::core::Layout::viewportSizeChanged</a>.</p>
<p class="endli"></p>
</li>
<li>
The viewport size may also depend on the visibility of UI widgets, which depend on the world size, e.g scrollbars, generally called "viewport markers". This is described in a separate section. </li>
</ul>
<p>After the creation of the layout, the viewport size is undefined. When a view is attached to a layout, and this view can already specify its viewport size, it may call <a class="el" href="classdw_1_1core_1_1Layout.html#a51f1aecef5f041cdb398802f2349d6f6">dw::core::Layout::viewportSizeChanged</a> within the implementation of dw::core::Layout::setLayout. If not, it may do this as soon as the viewport size is known.</p>
<p>Generally, the scrollbars have to be considered. If e.g. an HTML page is rather small, it looks like this:</p>
<div class="image">
<img src="dw-viewport-without-scrollbar.png" alt="dw-viewport-without-scrollbar.png"/>
</div>
<p>If some more data is retrieved, so that the height exceeds the viewport size, the text has to be rewrapped, since the available width gets smaller, due to the vertical scrollbar:</p>
<div class="image">
<img src="dw-viewport-with-scrollbar.png" alt="dw-viewport-with-scrollbar.png"/>
</div>
<p>Notice the different line breaks.</p>
<p>This means circular dependencies between these different sizes:</p>
<ol>
<li>
<p class="startli">Whether the scrollbars are visible or not, determines the usable space of the viewport.</p>
<p class="endli"></p>
</li>
<li>
<p class="startli">From the usable space of the viewport, the size hints for the toplevel are calculated.</p>
<p class="endli"></p>
</li>
<li>
<p class="startli">The size hints for the toplevel widgets may have an effect on its size, which is actually the canvas size.</p>
<p class="endli"></p>
</li>
<li>
The canvas size determines the visibility of the scrollbarss. </li>
</ol>
<p>To make an implementation simpler, we simplify the model:</p>
<ol>
<li>
<p class="startli">For the calls to dw::core::Widget::setAscent and dw::core::Widget::setDescent, we will always exclude the horizontal scrollbar thickness (i.e. assume the horizontal scrollbar is used, although the visibility is determined correctly).</p>
<p class="endli"></p>
</li>
<li>
For the calls to dw::core::Widget::setWidth, we will calculate the usable viewport width, but with the general assumption, that the widget generally gets higher. </li>
</ol>
<p>This results in the following rules:</p>
<ol>
<li>
<p class="startli">Send always (when it changes) <a class="el" href="classdw_1_1core_1_1Layout.html#ad39ba6567e9a2c3d83b019dadb7036b0">dw::core::Layout::viewportHeight</a> minus the maximal value of <a class="el" href="classdw_1_1core_1_1View.html#a4ef7529de4e770675e48c83ad400bf88" title="Get the thickness of the horizontal scrollbar, when it is visible. ">dw::core::View::getHScrollbarThickness</a> as argument to dw::core::Widget::setAscent, and 0 as argument to dw::core::Widget::setDescent.</p>
<p class="endli"></p>
</li>
<li>
<p class="startli">There is a flag, <a class="el" href="classdw_1_1core_1_1Layout.html#afb88886e91f0b74aab5f581c060275cd">dw::core::Layout::canvasHeightGreater</a>, which is set to false in the following cases:</p>
<ul>
<li>
<a class="el" href="classdw_1_1core_1_1Layout.html#a5adef4d301d59d70c551153bf3d78230">dw::core::Layout::addWidget</a>, </li>
<li>
<a class="el" href="classdw_1_1core_1_1Layout.html#ac080e495161e3bdbc1166de961d74f6c">dw::core::Layout::removeWidget</a> (called by <a class="el" href="classdw_1_1core_1_1Widget.html#afa654cec6369417221663a2583836496">dw::core::Widget::~Widget</a>), and </li>
<li>
<a class="el" href="classdw_1_1core_1_1Layout.html#a51f1aecef5f041cdb398802f2349d6f6">dw::core::Layout::viewportSizeChanged</a>. </li>
</ul>
<p class="endli">Whenever the canvas size is calculated (<a class="el" href="classdw_1_1core_1_1Layout.html#a7792ba666460057e47208128719e4659">dw::core::Layout::resizeIdle</a>), and <a class="el" href="classdw_1_1core_1_1Layout.html#afb88886e91f0b74aab5f581c060275cd">dw::core::Layout::canvasHeightGreater</a> is false, a test is made, whether the widget has in the meantime grown that high, that the second argument should be set to true (i.e. the vertical scrollbar gets visible). As soon as and <a class="el" href="classdw_1_1core_1_1Layout.html#afb88886e91f0b74aab5f581c060275cd">dw::core::Layout::canvasHeightGreater</a> is true, no such test is done anymore. </p>
</li>
</ol>
</div></div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated on Sat May 28 2016 11:47:43 for Dillo by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.8
</small></address>
</body>
</html>