Re: GTK+ canvas?



Someone posted a link to the minutes of the gtk+ meeting at GUADEC
which reminded me that I promised to write up some of the ideas I had
for a canvas-type widget.

The reason model/view is a useful model is that it allows applications
to keep a data structure (the model) containing all the business logic
(what the application *does*) in one place and then have separate
modules displaying views of this data structure. Whenever something
changes about the model the view is signalled to change itself as
appropriate. 

In an application based on GnomeCanvas or similar there will often be
a correspondence between objects in the data model and canvas items
and changes to the model will cause changes to the canvas
items. Essentially the application is forced to keep two distinct
copies of the model up to date. Not only does this use lots of memory,
it also requires lots of code to be written.

A much simpler approach is often to simply walk the model and draw on
a drawing area on every expose event. This gets hard for two reasons:

- you need to handle input on distinct areas corresponding to objects
  in the model.

- you need to handle scrolling

Both of these are solved by a canvas widget which I'd argue is its
main attraction. For the old GnomeCanvas you also had the attraction
of antialiasing/translucency and bezier curves, but not cairo provides
this.

So FooScrollArea is an attempt at implementing input and scrolling
without retaining a hierarchy of canvas items, ie, and "immeddiate
mode" canvas. Demo code here:

   http://www.daimi.au.dk/~sandmann/canvas-demo.tar.gz

The main thing to note is the small amount of code in testarea2
actually needed to implement the features demonstrated.

How applications would use it:

Applications keep a datastructure containing the application
model. Whenever something changes in this datastructure the
corresponding area is invalidated. The scroll area will then emit an
expose signal and the application will repaint the invalid area. Ie
basically like the gtk+ drawing model.

The difference is in how input is handled. During the expose handling,
the application is not only drawing stuff, it is also setting input
regions. These input regions are remembered by the canvas, but *get
invalidated along with the pixels*. Ie., you are required to "repaint"
all the input regions on every expose. If you paint two input regions
on top of each other, the top one will get the event first (and can
optionally pass it along to lower layers).

This way, all information about stacking and overlapping input regions
is *implicit* in the application model rather than explicit in the
canvas hierarchy. So even input handling is immediate mode rather than
retained mode.

Example of why this is useful:

Suppose you are writing an illustration program like Inkscape. Your
data model might be a tree of groups containing shapes and curves. One
of these shapes might be selected which means it has little handles
you can use to change the shape.

In the FooScrollArea model all you need to write is expose code for
the shape:

expose_shape (Shape *shape, ScrollArea *area, Region *region)
{
        draw_shape (area);

        draw_input_region (area, shape);

        if (shape->selected)
        {
                for (points in shape)
                {
                        draw_drag_handle();
        
                        set_drag_handle_input (handle_shape_input);
                }
        }
}

When a shape is clicked, the old selected shape gets its "selected"
bit cleared and the new one gets it set in the application model. Then
both areas are invalidad. When a drag handle is dragged, the
corresponding point needs to be moved and the item invalidated. So
there is only one copy of the state pertaining to the shape.


In the retained model, this will need to happen:

When a shape is clicked, the old selected shape needs to be unslected,
which means deleting a canvas item for each point on the shape. Then
new canvas items must be created for each of the handle points in the
new shape. When a drag handle is clicked, three different objects must
be updated: the application model object, the canvas item for the
shape, and the drag handle for the clicked point.

And this is just for editing a simple shape.

I believe this 'immediate' mode canvas is a simpler and more efficient
model than retained mode canvases, at least for most non-trivial
applications.



Soren



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]