aboutsummaryrefslogtreecommitdiff
path: root/devdoc/dw-layout-views.doc
diff options
context:
space:
mode:
Diffstat (limited to 'devdoc/dw-layout-views.doc')
-rw-r--r--devdoc/dw-layout-views.doc256
1 files changed, 256 insertions, 0 deletions
diff --git a/devdoc/dw-layout-views.doc b/devdoc/dw-layout-views.doc
new file mode 100644
index 00000000..d1118489
--- /dev/null
+++ b/devdoc/dw-layout-views.doc
@@ -0,0 +1,256 @@
+/** \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 archieve 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>
+
+*/