diff options
Diffstat (limited to 'doc/dw-layout-views.doc')
-rw-r--r-- | doc/dw-layout-views.doc | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/doc/dw-layout-views.doc b/doc/dw-layout-views.doc new file mode 100644 index 00000000..f064c671 --- /dev/null +++ b/doc/dw-layout-views.doc @@ -0,0 +1,270 @@ +/** \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: + +<ul> +<li> It makes different views of the same document simple, e.g. the + normal viewport and the preview window. + +<li> Abstraction of the actual drawing, by different implementations + of dw::core::View. Most important, there must be a viewport, but + some other views are possible, e.g. a preview window. + +<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 archived, +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> + +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: + +<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 views. + +<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. 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. + +<h2>Drawing</h2> + +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: + +<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 all views (dw::core::View::queueDraw). + Typically, the views 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> + +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: + +<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> + +Furthermore, viewport sizes and scrollbar thicknesses are always the +same. There are two cases, in which 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 affected view delegates this + change to the layout, by calling + dw::core::Layout::viewportSizeChanged. All other views are + told about this, by calling dw::core::Layout::setViewportSize. + +<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 an own + section. +</ul> + +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: + +<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> + +*/
\ No newline at end of file |