/** \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 views do a bit less than a typical view, i.e. only the actual drawing. Additionally, there is a structure representing common properties of the platform, views generally work only together with one specific platform. A platform is typically related to the underlying UI toolkit, but other uses may be thought of. This design helps to archieve three important goals:

Viewports

Although the design implies that the usage of viewports should be fully transparent to the layout module, this cannot be fully archived, for the following reasons: Therefor, 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. These sizes are always equal in all views. However, a given view may not use viewports at all, and there may be the case, that no view related to a layout uses viewports, in this case, the viewport size is not defined at all. (The case, that there is no viewport at all, is currently not well defined, but this case does not occur currently within dillo.) 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:

Scrolling Positions

The scrolling position is the canvas position at the upper left corner of the viewport. Views using viewports must
  1. change this value on request (dw::core::View::scrollTo), and
  2. tell other changes to the layout, e.g. caused by user events (dw::core::Layout::scrollPosChanged).
Applications of scrolling positions (anchors, test search etc.) are handled by the layout, in a way fully transparent to the views.

Scrollbars

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. Each view returns, via dw::core::View::getHScrollbarThickness and dw::core::View::getVScrollbarThickness, how thick they will be, when visible. The total space difference is then the maximum of all values, which the views return. 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.

Drawing

A view must implement several drawing methods, which work on the whole canvas. If it is neccesary 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: 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 clipping view. 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 () { 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.

Sizes

Generally, all views show the same layout, which has a given size (canvas size). In the simplest case, views do not have an influence on the canvas size, so that they are just told about changes of the canvas size, by a call to dw::core::View::setCanvasSize. This happens in the following situations:

Viewports

Furthermore, viewport sizes and scrollbar thicknesses are always the same. There are two cases, in which the viewport size changes: After the creation of the layout, the viewport size is undefined. When a view is attached to a layout, and this view is already to be able to define its viewport size, it may already 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 gets 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:
  1. Whether the scrollbars are visible or not, determines the usable space of the viewport.
  2. From the usable space of the viewport, the size hints for the toplevel are calculated.
  3. The size hints for the toplevel widgets may have an effect on its size, which is actually the canvas size.
  4. The canvas size determines the visibility of the scrollbarss.
To make an implementation simpler, we simplify the model:
  1. 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).
  2. 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.
This results in the following rules:
  1. 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.
  2. There is a flag, dw::core::Layout::canvasHeightGreater, which is set to false in the following cases:
    • dw::core::Layout::addWidget,
    • dw::core::Layout::removeWidget (called by dw::core::Widget::~Widget), and
    • dw::core::Layout::viewportSizeChanged.
    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.
*/