Dillo v3.1.1-46-g8a360e32
Loading...
Searching...
No Matches
Layout and Widgets

Both, the layouting and the drawing is delegated to a tree of widgets.

A widget represents a given part of the document, e.g. a text block, a table, or an image. Widgets may be nested, so layouting and drawing may be delegated by one widget to its child widgets.

Where to define the borders of a widget, whether to combine different widgets to one, or to split one widget into multiple ones, should be considered based on different concerns:

  • First, there are some restrictions of Dw:

    • The allocation (this is the space a widget allocates at a time) of a dillo widget is always rectangular, and
    • the allocation of a child widget must be a within the allocation of the parent widget.

  • Since some widgets are already rather complex, an important goal is to keep the implementation of the widget simple.

  • Furthermore, the granularity should not be too fine, because of the overhead each single widget adds.

For CSS, there will be a document tree on top of Dw, this will be flexible enough when mapping the document structure on the widget structure, so you should not have the document structure in mind.

Sizes

Sizes of Dillo Widgets

Styles

Each widget is assigned a style, see dw::core::style for more informations.

Iterators

Widgets must implement dw::core::Widget::iterator. There are several common iterators:

Both hide the constructor, use the create method.

These simple iterators only iterate through one widget, it does not have to iterate recursively through child widgets. Instead, the type dw::core::Content::WIDGET is returned, and the next call of dw::core::Iterator::next will return the piece of contents after (not within) this child widget.

This makes implementation much simpler, for recursive iteration, there is dw::core::DeepIterator.

Anchors and Scrolling

Todo:
This section is not implemented yet, after the implementation, the documentation should be reviewed.

Here is a description, what is to be done for a widget implementation. How to jump to anchors, set scrolling positions etc. is described in Dillo Widget Usage.

Anchors

Anchors are position markers, which are identified by a name, which is unique in the widget tree. The widget must care about anchors in three different situations:

  1. Adding an anchor is inititiated by a specific widget method, e.g. dw::Textblock::addAnchor. Here, dw::core::Widget::addAnchor must be called,

  2. Whenever the position of an anchor is changed, dw::core::Widget::changeAnchor is called (typically, this is done in the implementation of dw::core::Widget::sizeAllocateImpl).

  3. When a widget is destroyed, the anchor must be removed, by calling dw::core::Widget::removeAnchor.

All these methods are delegated to dw::core::Layout, which manages anchors centrally. If the anchor in question has been set to jump to, the viewport position is automatically adjusted, see Dillo Widget Usage.

Drawing

In two cases, a widget has to be drawn:

  1. as a reaction on an expose event,
  2. if the widget content has changed and it needs to be redrawn.

In both cases, drawing is done by the implementation of dw::core::Widget::draw, which draws into the view.

Each view provides some primitive methods for drawing, most should be obvious. Note that the views do not know anything about dillo widgets, and so coordinates have to be passed as canvas coordinates.

A widget may only draw in its own allocation. If this cannot be achieved, a clipping view can be used, this is described in Layout and Views. Generally, drawing should then look like:

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.
for (<all relevant children>) {
if (child->intersects (area, &childArea))
child->draw (clipView, childArea);
}
// 4. Merge
view->mergeClippingView (clipView);
}
dw::core::Shape implemtation for simple rectangles.
Definition types.hh:70
void draw(core::View *view, core::style::Style *style, int x, int y)
Definition types.cc:41
An interface to encapsulate platform dependent drawing.
Definition view.hh:17
virtual void mergeClippingView(View *clippingView)=0
virtual View * getClippingView(int x, int y, int width, int height)=0

Clipping views are expensive, so they should be avoided when possible.

The second argument to dw::core::Widget::draw is the region, which has to be drawn. This may (but needs not) be used for optimization.

If a widget contains child widgets, it must explicitly draw these children (see also code example above). For this, there is the useful method dw::core::Widget::intersects, which returns, which area of the child must be drawn.

Explicit Redrawing

If a widget changes its contents, so that it must be redrawn, it must call dw::core::Widget::queueDrawArea or dw::core::Widget::queueDraw. The first variant expects a region within the widget, the second will cause the whole widget to be redrawn. This will cause an asynchronous call of dw::core::Widget::draw.

If only the size changes, a call to dw::core::Widget::queueResize is sufficient, this will also queue a complete redraw (see Sizes of Dillo Widgets.)

Mouse Events

A widget may process mouse events. The view (Layout and Views) passes mouse events to the layout, which then passes them to the widgets. There are two kinds of mouse events:

  • events returning bool, and
  • events returning nothing (void).

The first group consists of:

For these events, a widget returns a boolean value, which denotes, whether the widget has processed this event (true) or not (false). In the latter case, the event is delegated according to the following rules:

  1. First, this event is passed to the bottom-most widget, in which allocation the mouse position is in.
  2. If the widget does not process this event (returning false), it is passed to the parent, and so on.
  3. The final result (whether any widget has processed this event) is returned to the view.

The view may return this to the UI toolkit, which then interprets this in a similar way (whether the viewport, a UI widget, has processed this event).

These events return nothing:

since they are bound to a widget.

When processing mouse events, the layout always deals with two widgets: the widget, the mouse pointer was in, when the previous mouse event was processed, (below called the "old widget") and the widget, in which the mouse pointer is now ("new widget").

The following paths are calculated:

  1. the path from the old widget to the nearest common ancestor of the old and the new widget, and
  2. the path from this ancestor to the new widget.

For the widgets along these paths, dw::core::Widget::enterNotifyImpl and dw::core::Widget::leaveNotifyImpl are called.

Signals

If a caller outside of the widget is interested in these events, he can connect a dw::core::Layout::LinkReceiver. For those events with a boolean return value, the results of the signal emission is regarded, i.e. the delegation of an event to the parent of the widget can be stopped by a signal receiver returning true, even if the widget method returns false.

First, the widget method is called, then (in any case) the signal is emitted.

Selection

If your widget has selectable contents, it should delegate the events to dw::core::SelectionState (dw::core::Layout::selectionState).

Miscellaneous

Cursors

Each widget has a cursor, which is set by dw::core::Widget::setCursor. If a cursor is assigned to a part of a widget, this widget must process mouse events, and call dw::core::Widget::setCursor explicitly.

(This will change, cursors should become part of dw::core::style::Style.)

Background

Backgrounds are part of styles (dw::core::style::Style::backgroundColor). If a widget assigns background colors to parts of a widget (as dw::Table does for rows), it must call dw::core::Widget::setBgColor for the children inside this part.