257 lines
8.9 KiB
Plaintext
257 lines
8.9 KiB
Plaintext
/** \page dw-layout-views Layout and Views
|
|
|
|
Rendering of Dw is done in a way resembling the model-view pattern, at
|
|
least formally. Actually, the counterpart of the model, the layout
|
|
(dw::core::Layout), does a bit more than a typical model, namely the
|
|
layouting (delegated to the widget tree, see \ref dw-layout-widgets),
|
|
and the view does a bit less than a typical view, i.e. only the actual
|
|
drawing.
|
|
|
|
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.
|
|
|
|
This design helps to achieve two important goals:
|
|
|
|
<ul>
|
|
<li> Abstraction of the actual drawing, by different implementations
|
|
of dw::core::View.
|
|
|
|
<li> It makes portability simple.
|
|
</ul>
|
|
|
|
|
|
<h2>Viewports</h2>
|
|
|
|
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:
|
|
|
|
<ul>
|
|
<li> Some features, which are used on the level of dw::core::Widget,
|
|
e.g. anchors, refer to scrolling positions.
|
|
|
|
<li> Size hints (see \ref dw-layout-widgets) 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.
|
|
</ul>
|
|
|
|
Therefore, dw::core::Layout 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.
|
|
|
|
Whether a given dw::core::View implementation is a viewport or not, is
|
|
defined by the return value of dw::core::View::usesViewport. If this
|
|
method returns false, the following methods need not to be implemented
|
|
at all:
|
|
|
|
<ul>
|
|
<li> dw::core::View::getHScrollbarThickness,
|
|
<li> dw::core::View::getVScrollbarThickness,
|
|
<li> dw::core::View::scrollTo, and
|
|
<li> dw::core::View::setViewportSize.
|
|
</ul>
|
|
|
|
<h3>Scrolling Positions</h3>
|
|
|
|
The scrolling position is the canvas position at the upper left corner
|
|
of the viewport. Views using viewports must
|
|
|
|
<ol>
|
|
<li> change this value on request (dw::core::View::scrollTo), and
|
|
<li> tell other changes to the layout, e.g. caused by user events
|
|
(dw::core::Layout::scrollPosChanged).
|
|
</ol>
|
|
|
|
Applications of scrolling positions (anchors, test search etc.) are
|
|
handled by the layout, in a way fully transparent to the view.
|
|
|
|
<h3>Scrollbars</h3>
|
|
|
|
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.
|
|
|
|
Since scrollbars decrease the usable space of a view, dw::core::Layout
|
|
must know how much space they take. The view returns, via
|
|
dw::core::View::getHScrollbarThickness and
|
|
dw::core::View::getVScrollbarThickness, how thick they will be, when
|
|
visible.
|
|
|
|
Viewport sizes, which denote the size of the viewport widgets, include
|
|
scrollbar thicknesses. When referring to the viewport \em excluding
|
|
the scrollbars space, we will call it "usable viewport size", this is
|
|
the area, which is used to display the canvas.
|
|
|
|
<h2>Drawing</h2>
|
|
|
|
A view must implement several drawing methods, which work on the whole
|
|
canvas. If it is necessary to convert them (e.g. into
|
|
dw::fltk::FltkViewport), this is done in a way fully transparent to
|
|
dw::core::Widget and dw::core::Layout, instead, this is done by the
|
|
view implementation.
|
|
|
|
There exist following situations:
|
|
|
|
<ul>
|
|
<li> A view gets an expose event: It will delegate this to the
|
|
layout (dw::core::Layout::draw), which will then pass it to the
|
|
widgets (dw::core::Widget::draw), with the view as a parameter.
|
|
Eventually, the widgets will call drawing methods of the view.
|
|
|
|
<li> A widget requests a redraw: In this case, the widget will
|
|
delegate this to the layout (dw::core::Layout::queueDraw), which
|
|
delegates it to the view (dw::core::View::queueDraw).
|
|
Typically, the view will queue these requests for efficiency.
|
|
|
|
<li> A widget requests a resize: This case is described below, in short,
|
|
dw::core::View::queueDrawTotal is called for the view.
|
|
</ul>
|
|
|
|
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
|
|
<i>clipping view.</i> 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:
|
|
|
|
\code
|
|
void Foo::draw (dw::core::View *view, dw::core::Rectangle *area)
|
|
{
|
|
// 1. Create a clipping view.
|
|
dw::core::View clipView =
|
|
view->getClippingView (allocation.x, allocation.y,
|
|
allocation.width, getHeight ());
|
|
|
|
// 2. Draw into clip_view
|
|
clipView->doSomeDrawing (...);
|
|
|
|
// 3. Draw the children, they receive the clipping view as argument.
|
|
dw::core::Rectangle *childArea
|
|
for (<all relevant children>) {
|
|
if (child->intersects (area, &childArea))
|
|
child->draw (clipView, childArea);
|
|
}
|
|
|
|
// 4. Merge
|
|
view->mergeClippingView (clipView);
|
|
}
|
|
\endcode
|
|
|
|
A drawing process is always embedded into calls of
|
|
dw::core::View::startDrawing and dw::core::View::finishDrawing. An
|
|
implementation of this may e.g. use backing pixmaps, to prevent
|
|
flickering.
|
|
|
|
|
|
<h2>Sizes</h2>
|
|
|
|
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 dw::core::View::setCanvasSize. This happens
|
|
in the following situations:
|
|
|
|
<ul>
|
|
<li> dw::core::Layout::addWidget,
|
|
<li> dw::core::Layout::removeWidget (called by dw::core::Widget::~Widget),
|
|
and
|
|
<li> dw::core::Layout::queueResize (called by
|
|
dw::core::Widget::queueResize, when a widget itself requests a size
|
|
change).
|
|
</ul>
|
|
|
|
<h3>Viewports</h3>
|
|
|
|
There are two cases where the viewport size changes:
|
|
|
|
<ul>
|
|
<li> 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
|
|
dw::core::Layout::viewportSizeChanged.
|
|
|
|
<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.
|
|
</ul>
|
|
|
|
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
|
|
dw::core::Layout::viewportSizeChanged within the implementation of
|
|
dw::core::Layout::setLayout. If not, it may do this as soon as the
|
|
viewport size is known.
|
|
|
|
Generally, the scrollbars have to be considered. If e.g. an HTML page
|
|
is rather small, it looks like this:
|
|
|
|
\image html dw-viewport-without-scrollbar.png
|
|
|
|
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:
|
|
|
|
\image html dw-viewport-with-scrollbar.png
|
|
|
|
Notice the different line breaks.
|
|
|
|
This means circular dependencies between these different sizes:
|
|
|
|
<ol>
|
|
<li> Whether the scrollbars are visible or not, determines the
|
|
usable space of the viewport.
|
|
|
|
<li> From the usable space of the viewport, the size hints for the
|
|
toplevel are calculated.
|
|
|
|
<li> The size hints for the toplevel widgets may have an effect on its
|
|
size, which is actually the canvas size.
|
|
|
|
<li> The canvas size determines the visibility of the scrollbarss.
|
|
</ol>
|
|
|
|
To make an implementation simpler, we simplify the model:
|
|
|
|
<ol>
|
|
<li> 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).
|
|
|
|
<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.
|
|
</ol>
|
|
|
|
This results in the following rules:
|
|
|
|
<ol>
|
|
<li> Send always (when it changes) dw::core::Layout::viewportHeight
|
|
minus the maximal value of dw::core::View::getHScrollbarThickness as
|
|
argument to dw::core::Widget::setAscent, and 0 as argument to
|
|
dw::core::Widget::setDescent.
|
|
|
|
<li> There is a flag, dw::core::Layout::canvasHeightGreater, which is set
|
|
to false in the following cases:
|
|
|
|
<ul>
|
|
<li> dw::core::Layout::addWidget,
|
|
<li> dw::core::Layout::removeWidget (called by dw::core::Widget::~Widget),
|
|
and
|
|
<li> dw::core::Layout::viewportSizeChanged.
|
|
</ul>
|
|
|
|
Whenever the canvas size is calculated (dw::core::Layout::resizeIdle),
|
|
and dw::core::Layout::canvasHeightGreater 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 dw::core::Layout::canvasHeightGreater is true, no such test
|
|
is done anymore.
|
|
</ol>
|
|
|
|
*/
|