aboutsummaryrefslogtreecommitdiff
path: root/doc/DwRender.txt
diff options
context:
space:
mode:
Diffstat (limited to 'doc/DwRender.txt')
-rw-r--r--doc/DwRender.txt620
1 files changed, 620 insertions, 0 deletions
diff --git a/doc/DwRender.txt b/doc/DwRender.txt
new file mode 100644
index 00000000..73fd0ded
--- /dev/null
+++ b/doc/DwRender.txt
@@ -0,0 +1,620 @@
+Aug 2004, S.Geerken@ping.de
+
+===========================
+ Dw Render Abstraction
+===========================
+
+This document describes the rendering abstraction used in Dw. At the
+time this document was written, some other documents about Dw were out
+of date, and have to be rewritten.
+
+Sometimes, you will find remarks about the old design (with one
+concrete Gtk+ widget instead of the layout/multiple rendering views);
+those are useful for those, who are already familiar with the old
+design. Furthermore, it makes the explanation often simpler, because
+the new design is a bit more complex.
+
+Naming
+------
+As stated in "Dw.txt", the prefix "p_Dw_" is used for functions used
+within the whole Dw module, but not outside. Typically, these are
+protected functions, which are called by inherited classes. E.g., a
+specific widget will have to call p_Dw_widget_queue_resize(), but this
+function should not called outside from Dw.
+
+The "Dw_" prefix is not only used for functions, which use is
+restricted to a sub-module, but also for functions used only within
+the core of Dw. The distinction between both can simply made, whether
+the function is declared as static or not. The core of Dw consists of
+the modules Dw_widget and Dw_render, anything else is inherited from
+these two modules (widgets, platforms, views). As an example,
+Dw_gtk_viewport_queue_resize() is only called in the Dw_widget module,
+it should not be called by a widget implementation.
+
+
+Overview
+========
+
+Rendering of Dw is done in a way resembling the model-view pattern, at
+least formally. Actually, the counterpart of the model, the layout
+(DwRenderLayout), does a bit more than a typical model, namely
+calculating the layout, 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.
+
+The general structure looks like this:
+
+ 1 +------------------+
+ .---->| DwRenderPlatform |
+ +----------------+ 1 / +------------------+
+ | DwRenderLayout |--< ^ 1
+ +----------------+ \ | *
+ 1 ^ | 1 \ +--------------+
+ | | `---->| DwRenderView |
+ | | * +--------------+
+ | |
+ layout | | toplevel_dw
+ | |
+ * | V 0..1
+ +----------+ 0..1
+ | DwWidget |--. parent
+ +----------+ |
+ * | |
+ `-------'
+ (children)
+
+(This is a bit simplified, actually, only DwContainer has child
+widgets, and the relation is only defined in an abstract way).
+
+DwRenderLayout and DwWidget (including sub classes) are platform and
+view independant, i.e., it should be possible to adopt them into
+another platform, after suitable implementations of DwRenderPlatform
+and DwRenderView have been developed.
+
+(The only exception is DwEmbedGtk, a widget which, in the old design,
+embeds Gtk+ widgets into the DwWidget tree. This will still remain a
+platform specific widget, see notes in the section "UI Widgets".
+
+Furthermore, this design does not yet cover DwStyle, which is still
+bound to the Gtk+ platform. It may be simply replaced for other
+platforms, but this will lose the possibility to have multiple
+implementations of DwRenderPlatform running within the same program.
+The same aplies to DwTooltip and Imgbuf.)
+
+This new design helps to archieve two important goals:
+
+ 1. Abstraction of the actual rendering. Currently, we only have the
+ normal viewport, but a list of planned implementations can be
+ found below in this text.
+
+ 2. It makes different views of the same document simple, e.g. the
+ normal viewport and the preview window.
+
+ 3. It makes portability simpler.
+
+Vieports are handles by this design, but only in a rather abstract
+way, and may be removed completely, i.e., they will be only part of
+specific view implementations. This also means, that the distinction
+between world coordinates and viewport coordinates vanishes from most
+parts of Dw.
+
+
+World
+=====
+
+The world is the whole area, in which content is rendered. For a view
+without a viewport, this is the whole itself. A view with a viewport
+provides a way, so that the user may see some part of the world within
+a (in most cases) smaller window, the viewport.
+
+The world may have a base line, so that the worls size is described by
+three numbers, width, ascent and descent.
+
+Any view must implement the method set_world_size(), which is called,
+whenever the world size changes. For viewports, this will change
+scroll bars etc., for views without viewport, this will normally
+change the size of the view itself. (Although on this level, "view
+size" is not defined. This should not be confused with the viewport
+size!)
+
+
+Drawing
+=======
+
+A view must implement several drawing methods, which work on the whole
+world. If it is neccesary to convert them (e.g. in DwGtkViewport),
+this is done in a way fully transparent to DwWidget and
+DwRenderingLayout, instead, this is done by the view implementation.
+
+There exist following situations:
+
+ - A view gets an expose event: It will delegate this to the
+ rendering layout, which will then pass it to the widgets, with
+ the view as a parameter. Eventually, the widgets will call
+ drawing methods of the view.
+
+ - A widget requests a redraw: In this case, the widget will
+ delegate this to the layout. The drawing requests are queued, and
+ compressed. in an this idle function, DwWidget::draw is called
+ for the toplevel widget, for each view.
+
+ (This is still something to consider, the queueing may be moved
+ again into the view implementations.)
+
+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 rendering view related to the
+actual rendering view, which guarantees that the parts drawn outside
+are discarded. At the end, the clipping view is merged into the actual
+view. Sample code for widget DwFoo:
+
+ void Dw_foo_draw (DwWidget *widget,
+ DwRenderView *view,
+ DwRectangle *area)
+ {
+ DwRenderView *clip_view;
+
+ /* 1. Create a clipping view. */
+ clip_view =
+ p_Dw_render_view_get_clipping_view (view,
+ widget->allocation.x,
+ widget->allocation.y,
+ widget->allocation.width
+ DW_WIDGET_HEIGHT (widget));
+
+ /* 2. Draw into clip_view. */
+ Dw_render_view_do_some_drawing (clip_view, ...);
+
+ /* 3. Draw the children, they receive the clipping view as argument. */
+ for (<all relevant children>) {
+ if (p_Dw_widget_intersect (button->child, area, &child_area))
+ p_Dw_widget_draw (child, clip_view, child_area);
+ }
+
+ p_Dw_render_view_merge_clipping_view (view, clip_view);
+ }
+
+A drawing process is always embedded into calls of
+DwRenderView::start_drawing() and DwRenderView::finish_drawing(). An
+implementation of this are backing pixmaps, to prevent flickering.
+
+
+Viewports and Scrolling Positions
+=================================
+
+Although the design implies that the usage of viewports should be
+fully transparent to the Dw_render module, this cannot be fully
+archived, for the following reasons:
+
+ 1. Some features, which are used on the level of DwWidget,
+ e.g. anchors, refer to scrolling positions.
+
+ 2. Some code in DwWidget is currently tightly bound to viewports.
+ This may be change, by delegating some of this functionality to
+ viewports, and defining scrolling positions in a more abstract
+ way.
+
+Therefor, DwRenderLayout keeps track of the viewport size, the
+viewport position, and even the thickness of the scrollbars (or,
+generally, "viewport marker"), they are relevant, see below. 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.
+
+Unlike world sized, viewports are not considered to have a base line,
+i.e., they are described by only two numbers, width and height.
+
+Viewport Size
+-------------
+All viewport sizes and positions are the same in all views, which uses
+viewports. There are two cases, in which the viewport size changes:
+
+ 1. 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
+ p_Dw_render_layout_vieport_size_changed(). All other views are
+ told about this, by calling DwRenderView::set_viewport_size().
+
+ 2. 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.
+
+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
+p_Dw_render_layout_vieport_size_changed() within the implementation of
+set_layout. If not, it may do this, as soon as the viewport size gets
+known.
+
+Each call of p_Dw_render_layout_vieport_size_changed() will change the
+viewport size of all other views, which use viewports, so this
+function has to be called with care.
+
+If there is no view attached, which used viewports, the viewport size
+remains undefined.
+
+Viewport Markers
+----------------
+Viewport markers are UI widgets, which display to the user the
+relation of the world size and the widget size. Most commonly,
+scrollbars will be used.
+
+When they are not needed, they are hidden, as in the following figure:
+
+
+ +--------------------+
+ | This is only a |
+ | very short text. |
+ | |
+ | |
+ | |
+ +--------------------+
+
+but shown, as soon as the world size exceeds the viewport size:
+
+ +------------------+-+
+ | In this example, |^|
+ | there is some |#|
+ | more text, so |#|
+ | that the | |
+ | vertical |v|
+ +------------------+-+
+
+A view using viewports must provide, how large the differences
+are. Generally, there are four cases, from the combinations of whether
+the world width is smaller (-) or greater (+) than the viewport width,
+and whether the world height is smaller (-) or greater (+) than the
+viewport height. So there are eight numbers, the horizontal difference
+dh, and the vertical difference dv, for the cases --, -+, +-, and ++.
+
+For scrollbars, the values are rather simple:
+
+ - dh is 0 for the cases -- and -+, and the thickness of the
+ vertical scrollbar in the cases +- and ++.
+
+ - dv is 0 for the cases -- and +-, and the thickness of the
+ horizontal scrollbar in the cases -+ and ++.
+
+For any view implementation, the following rules must be fullfeeded
+(dx means either dh or dv):
+
+ - dx(-+) >= d(--)
+ - dx(++) >= d(+-)
+ - dx(+-) >= d(--)
+ - dx(++) >= d(-+)
+
+In short, when smaller world dimensions (-) grow (switch to +), the
+differences may not become less.
+
+The sizes of viewport markers are part of the viewport size, the
+method DwRenderView::set_viewport_size() has four parameters:
+
+ - two for the size of the viewport, *including* the markers
+ (i.e. the markers never change the size), and
+
+ - two, which denote the differences between the above and the
+ actual viewport size, caused by the markers. If a value of these
+ is 0, the respective marker is hidden, if it is greater than 0,
+ it is shown. In the latter case, the maximun is calculated, and
+ passed to all views.
+
+(Actually, the decision, whether the markers are visible or not, is a
+bit more complicated, since the vieport size also defines the size
+hints for the topmost widget, which may affect the widget size, and so
+the world size. Handling this problem is done within DwRenderLayout,
+look at the comments there.)
+
+Scrolling Positions
+-------------------
+The scrolling position is the world position at the upper left corner
+of the viewport. Views using viewports must
+
+ 1. change this value on request, and
+ 2. tell other changes to the layout, e.g. caused by user events.
+
+Applications of scrolling positions (anchors, test search etc.) are
+handled by the layout, in a way fully transparent to the views.
+
+
+An Example with Nested Views
+============================
+The following example refers to graphical plugins, which are not yet
+realized (I have some working proof-of-concept code), but which will
+most likely follow the design, which is here shortly described, as
+needed to understand the example (since there is no specification of
+graphical plugins yet). I included this, to demonstrate, how nested
+layouts can be used.
+
+Graphical plugins will communicate with dillo via two protocols, dpi1
+(for anything not directly related to rendering), and a new protocol,
+which is an extension of the XEmbed protocol (see somewhere at
+http://freedesktop.org, this is the protocol, which GtkPlug and
+GtkSocket use). Within dillo, a socket window will be created, which
+window id will be (via dpi1?) passed to the plugin. The plugin will
+then create a plugin window, in which the contents of a web recource
+is shown, which dillo cannot show natively.
+
+XEmbed will be extended, so that the plugins may make use of the
+extensions, which the Dw size model adds to the XEmbed size
+model. Such a plugin behaves (on an abstract level) like a DwWidget,
+so following extensions are necessary:
+
+ - a plugin may not simply have a size, but instead distinguish
+ between ascent and descent,
+ - a plugin may return width extremes, and
+ - it is possible to send size hints to the plugin.
+
+Within Dw, the socket will be realized by a special plugin,
+DwSocketGtk (or a respective widget for other platforms), which is a
+sub class of DwEmbedGtk, and embeds another UI widget, which will
+provide the socket window (for Gtk+, either GtkSocket, or a sub class
+of this).
+
+The plugins may be implemented independently of Dw, they either do not
+support the extensions to XEmbed (then, the respective features behave
+in a default way), or they may contain there own implementation.
+However, Dw may be extracted from dillo, to become an own library. A
+plugin using this library may then use a special type of view,
+GtkDwFlatView (for plugins based on Gtk+, actually, the UI toolkit for
+dillo and the plugin must not be the same, since the protocol will be
+UI toolkit independant.)
+
+This will lead to a structure like this:
+
+ top_layout:DwRenderLayout ----- top_page:DwPage
+ / \ |
+ :GtkDwPlatform top_view:GtkDwView `- table:DwTable
+ |
+ `- cell:DwTableCell
+ |
+ `- socket:DwSocketGtk
+ |
+ DILLO gtk_socket:GtkSocket
+ .
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - . (extension of
+ . XEmbed)
+ PLUGIN
+ plugin_layout:DwRenderLayout ----- dw_flat_view:GtkDwFlatView
+ \
+ `------- foo:DwFoo
+
+GtkDwFlatView is both an extension of GtkSocket, as well as an
+implementation of DwRenderView.
+
+This case must be equivalent to the case, that the widget "foo" is a
+direct child of the widget "cell", since all objects between them only
+delegate all functionality. Size hints must so sent as in the
+following scenario:
+
+ 1. The view "top_view" receieves an event, which will change the
+ viewport size, and delegates this to the layout.
+
+ 2. The layout will make size hints of this viewport size, i.e., it
+ calls the set_* methods of the widget "top_page".
+
+ 3. DwPage::set_* will queue a resize request, which is delegated
+ to the layout.
+
+ 4. Handling the resize request is done within a idle loop. Here,
+ size_request is called for the widget "top_page", to determine
+ the preferred size.
+
+ 5. DwPage::size_request will depend on the size hints, especially,
+ it will use the hinted size to determine the word wrap width.
+ Within DwPage::size_request, size hints will be sent to the
+ child widgets. The size hints sent to the children are those
+ sent to the page, minus the respective border widths (margins,
+ borders, paddings). (How the ascent and descent hints should
+ generally be delegated to the children, is still a matter of
+ consideration, and may change.)
+
+ 6. DwTable::size_request, which is called within this context, has
+ a different way to delegate the size hints to the children:
+ ascent and descent are handled the same way, but the widths are
+ calculated from the column widths, which are calculated within
+ DwTable::size_request.
+
+ 7. Via the widget "cell", finally the widget "socket" receives
+ appropriate size hints. These must be (equally) delegated to
+ the widget "foo", this is done in a way described in the
+ following steps.
+
+ 8. The size hints are transmitted via the protocol extending
+ XEmbed. The Gtk+ widget "dw_flat_view" receives them (via the
+ widget "gtk_socket"), and delegates them to the respective
+ layout "plugin_layout". (TODO: How is this done? What has to be
+ done is to send the size hints to the toplevel widget, but a
+ view has no access to it.)
+
+ 9. The layout "plugin_layout" will then delegate this (what ever
+ "this" means), as size hints, to the widget "foo".
+
+ 10. The widget "foo" will queue a size_request, which is delegated
+ to the layout. In the idle function handling this request, the
+ recalculation of the widget size will change the world size,
+ which is delegated to the view "dw_flat_view".
+
+ 11. For GtkDwFlatView, the world size is the view size. For this
+ reason, it changes its size, to fit the world size. This size
+ is send to the DwSocket, which will then request a size
+ request.
+
+
+UI Widgets
+==========
+A note before: This chapter refers often to Gtk+ as base UI toolkit,
+just to make naming simpler, For other UI toolkits, the same, mostly
+only with other names, applies.
+
+In some cases, it is necessary to embed widgets from the UI toolkit
+into the view, e.g. to realize HTML forms in dillo.
+
+The Dw_render module provides no interface at all, instead, this is
+completely left to the platform implementations. Below, two design
+approaches are discussed, which have following in common:
+
+ 1. There is a special Dw widget, which embeds something related to
+ the UI widgets (below called DwEmbedGtk).
+
+ 2. This Dw widget, the platform, and the views are tightly bound,
+ beyond the methods, which the respective interfaces provide. The
+ Dw widget can simply refer to the platform by
+ (DwWidget::render_layout->platform, and the platform may refer
+ to the views, when DwPlatform::attach_view and
+ DwPlatform::detach_view are implemented.
+
+General Problems
+----------------
+For most UI toolkits, there have to be multiple instances of widgets
+in different views. Furthermore, there may be, on the same platform,
+different classes for the same widgets. Consider this simple example:
+
+ :DwRenderLayout ------------------- :DwPage
+ / \ |
+ ,----' `----. dw:DwEmbedGtk
+ / \
+ view1:GtkDwViewport view2:GtkDwViewport
+ | |
+ gtk1:GtkButton gtk2:GtkButton
+
+This structure represents a page with one button in it, which is, in
+the DwWidget tree, represented by the DwEmbedGtk "dw", and, in the
+views "view1" and "view2", by the Gtk+ widgets "gtk1" and "gtk2",
+respectively. The old approach, where the UI widget (in this case
+GtkButton) is not directly applicable, since there must be multiple
+instances of this UI widget.
+
+Here is a more complex example:
+
+ :DwRenderLayout ------------------- :DwPage
+ / \ |
+ ,----' `----. dw:DwEmbedGtk
+ / \
+ view1:GtkDwFooView view2:GtkDwBarView
+ | |
+ gtk1:GtkFooButton gtk2:GtkBarButton
+
+In this case, the different views GtkDwFooView and GtkDwBarView deal
+with different classes for buttons, GtkFooButton and GtkBarButton.
+
+Simple Approach
+---------------
+Within dillo, the following limitations are reasonable:
+
+ 1. There is only one view instance, which actually needs UI widgets
+ (GtkDwViewport for Gtk+). There may be multiple views, but these
+ additional views do not need instances of widgets, e.g. in the
+ preview, a UI widget is simply shown as a grey box.
+
+ 2. There is only one type of UI widget, i.e. normal Gtk+ widgets.
+
+Because of these limitations, the implementation of UI widgets is
+currently quite simple in dillo. As before, the widget DwEmbedGtk
+refers to an instance of a concrete Gtk+ widget. This Gtk+ widget is
+told to the platform, of which DwEmbedGtk knows, that it is an
+instance of GtkDwPlatform. GtkDwPlatform will add this Gtk+ widget to
+the single view, which needs it, and DwEmbedGtk will be responsible of
+allocating etc. of the Gtk+ widget.
+
+Advanced Approach
+-----------------
+This is a short overview of an approach, which will overcome the
+limitations of the simple approach, but with the costs of a greater
+complexity. It will be detailed, in the case, that it is implemented.
+
+ +------------+ factory +---------------------+
+ | DwEmbedGtk | ---------> | DwGtkWidgetFactory |
+ +------------+ 1 1 +---------------------+
+ | create_foo_widget() |
+ | create_bar_widget() |
+ +---------------------+
+ . . .
+ /_\ /_\ /_\
+ | | |
+ - - - - - - - - - - - - - - - - - .
+ | |
+ +---------------------+ +---------------------+ |
+ | DwGtkButtonFactory | | DwGtkListFactoty | (other ...)
+ +---------------------+ +---------------------+
+ | create_foo_widget() | | create_foo_widget() |
+ | create_bar_widget() | | create_bar_widget() |
+ +---------------------+ | add_list_item(...) |
+ +---------------------+
+
+DwEmbedGtk refers to a factory, which creates the UI widgets for each
+view. For each general widget type (e.g. button, list, ...), there is
+an implementation of this interface.
+
+This interface DwGtkWidgetFactory contains different method for
+different views. E.g., when a button is shown, and so DwEmbedGtk
+refers to a DwGtkButtonFactoty, GtkDwFooView may need a GtkFooButton,
+which is created by create_foo_widget(), while GtkDwBarView may call
+create_bar_widget(), to get a GtkBarButton. (This design still makes
+it hard to add new views, which then need, say, GtkBazButtons.)
+
+The concrete factories may contain additional methods, in the diagram
+above, DwGtkListFactory must provide a function to add items to the
+lists. This method will add a item to all list widget, which were
+created by this factory.
+
+
+More Ideas for View Implementations
+===================================
+
+Preview Window
+--------------
+The status bar gets a new button, on which the user may click to get a
+small image of the small page, in which he can select the viewport
+position by dragging a rubber-band rectangle. The actual scaling
+factor will probably depend on the aspect ratio of the page.
+
+(This has already been implemented for Gtk+, as GtkDwPreview and
+GtkDwPreviewButton.)
+
+Graphical Plugin
+----------------
+See section "An Example with Nested Views" for explanations.
+
+======================================================================
+
+Alternative design for handling UI widgets:
+
+- There is a platform *independant* widget, the platform dependencies
+ are within the factories.
+
+ DwEmbedUI::draw
+ -> DwRenderView::draw_ui_widget(..., factory)
+
+Implementation of "draw_ui_widget" in a preview view:
+ -> draw some gray boxes etc.
+
+Implementation of "draw_ui_widget" in a normal "bar" view (for platform "foo"):
+ -> determine that is is a factory for a "baz" widget?
+ -> (BarFactory*)factory->get_foo_bar_widget(this)
+
+This method either returns an already created widget, or creates one
+with the current state of the factory. "This" is taken as a key.
+
+----
+
+ | general | widget specific
+----------------------+------------------+------------------------
+ platform independant | nothing? | e.g. adding list items
+----------------------+------------------+------------------------
+ platform dependant | e.g. creating a | ???
+ | widget, or |
+ | informations |
+ | about drawing in |
+ | a preview |
+
+======================================================================
+
+Abstraction of DwStyle
+----------------------
+structures needed: