Creating New Items

Creating New Items — how to create new canvas items.

How to Create New Canvas Items

There are 3 ways to create new canvas items, listed here in increasing order of complexity:

These will be discussed in turn below. (It is also possible to create new container items by subclassing GooCanvasGroup, but that is not covered here.)

The final part of this section covers creating item models.

Creating a Simple Subclass of GooCanvasItemSimple

For items that consist of a simple graphic element such as a line, rectangle or circle, it is possible to create a subclass of GooCanvasItemSimple and override just one method, simple_create_path(). (This method is used for the GooCanvasEllipse and GooCanvasPath items.)

The simple_create_path() method should create a path using the given cairo context. The path will be drawn using the stroke, fill and other painting properties from GooCanvasItemSimple.

This example shows the simple_create_path() method for a simple rectangular item, MyItem:

1
2
3
4
5
6
7
8
static void
my_item_simple_create_path (GooCanvasItemSimple *simple,
                            cairo_t             *cr)
{
  MyItem *item = (MyItem*) simple;

  cairo_rectangle (cr, item->x, item->y, item->width, item->height);
}

Whenever the item is changed in some way it should call goo_canvas_item_simple_changed(), passing a boolean value indicating whether the item's bounds need to be recalculated or if it only needs to be repainted. The GooCanvasItemSimple code will take care of updating the item and repainting the appropriate parts of the canvas.


Creating a Regular Subclass of GooCanvasItemSimple

Most items will need more than a simple line or rectangle, so they will need to create a subclass of GooCanvasItemSimple and override three methods, simple_update(), simple_paint() and simple_is_item_at().

The simple_update() method should compute the bounds of the item, in the item's coordinate space, and place them in the bounds member of GooCanvasItemSimple. Note that the cairo context passed to this function may have transformations applied to it, so cairo_identity_matrix() should be called before using it.

The simple_paint() method should paint the item using the given cairo context. To use the stroke and fill properties from GooCanvasItemSimple to paint parts of the item call goo_canvas_style_set_stroke_options() and goo_canvas_style_set_fill_options() before calling cairo_stroke() and cairo_fill(). (The item's style can be found in GOO_CANVAS_ITEM_SIMPLE (item)->simple_data->style).

The simple_is_item_at() method should return TRUE if the given coordinate (in the item's coordinate space) is inside the item. (The is_pointer_event argument can be ignored for most purposes since the GooCanvasItemSimple code will take care of it.)

This example code shows the simple_update(), simple_paint() and simple_is_item_at() methods for a rectangular item (the complete item's source code can be found in the GooCanvas demo directory, in demo-item.h and demo-item.c):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
static void
goo_demo_item_update  (GooCanvasItemSimple *simple,
                       cairo_t             *cr)
{
  GooDemoItem *demo_item = (GooDemoItem*) simple;

  /* Compute the new bounds. */
  simple->bounds.x1 = demo_item->x;
  simple->bounds.y1 = demo_item->y;
  simple->bounds.x2 = demo_item->x + demo_item->width;
  simple->bounds.y2 = demo_item->y + demo_item->height;
}


static void
goo_demo_item_paint (GooCanvasItemSimple   *simple,
                     cairo_t               *cr,
                     const GooCanvasBounds *bounds)
{
  GooDemoItem *demo_item = (GooDemoItem*) simple;

  cairo_move_to (cr, demo_item->x, demo_item->y);
  cairo_line_to (cr, demo_item->x, demo_item->y + demo_item->height);
  cairo_line_to (cr, demo_item->x + demo_item->width,
                 demo_item->y + demo_item->height);
  cairo_line_to (cr, demo_item->x + demo_item->width, demo_item->y);
  cairo_close_path (cr);
  goo_canvas_style_set_fill_options (simple->simple_data->style, cr);
  cairo_fill (cr);
}


static gboolean
goo_demo_item_is_item_at (GooCanvasItemSimple *simple,
                          gdouble              x,
                          gdouble              y,
                          cairo_t             *cr,
                          gboolean             is_pointer_event)
{
  GooDemoItem *demo_item = (GooDemoItem*) simple;

  if (x < demo_item->x || (x > demo_item->x + demo_item->width)
      || y < demo_item->y || (y > demo_item->y + demo_item->height))
    return FALSE;

  return TRUE;
}

As with the simple GooCanvasItemSimple subclass, the item should call goo_canvas_item_simple_changed() whenever it is changed, to ensure that the item's bounds are recomputed and it is repainted if necessary.


Implementing the GooCanvasItem Interface

The most complicated way to create new canvas items is to implement the GooCanvasItem interface directly. This should not be needed in most cases, but may be desired if the developer wants to avoid the memory and processor overheads associated with the GooCanvasItemSimple class, or if the developer wants to turn an existing application object into a canvas item.

At a minimum the canvas item must implement these 6 methods:

  • get_parent() - the item's parent should be returned.

  • set_parent() - the item's parent should be stored (though it should not add a reference to the parent).

  • get_bounds() - returns the bounds of the item, in canvas space. The item should ensure that the bounds are up-to-date before returning them, calling goo_canvas_item_ensure_updated() if necessary.

  • update() - if the item has been changed since the last update, or if the entire_tree flag is TRUE, the item's bounds should be recomputed (in canvas space). It should also request a redraw of the old bounds and the new bounds, so the display is updated appropriately. The new bounds should be returned in the bounds argument.

  • paint() - if the item's bounds intersect the given bounds then the item should be painted on the given cairo context. The scale parameter is only used to check if the item should be visible, according to the item's “visibility” and “visibility-threshold” property settings.

  • get_items_at() - if the given point is inside the item then a pointer to the item should be added to the start of the list of found items. The list is then returned.

The canvas item must also implement the “parent”, “title”, “description”, “visibility”, “visibility-threshold”, “transform” and “pointer-events” properties. (The last 4 properties can simply be ignored if the application doesn't intend to use them.)

If the canvas item will be used within a container that does item layout, such as GooCanvasTable, it must implement the first two methods here at least:

  • get_requested_area() - returns the requested area of the item, in the parent's coordinate space.

  • allocate_area() - allocates the item's area, in the parent's coordinate space.

  • get_requested_height() - returns the requested height of the item, given a particular allocated width, in the parent's coordinate space. (This only needed for items that change height as their width is changed, such as text items.)

If the canvas item supports a transformation matrix it must implement:

  • get_transform() - returns the item's transformation matrix.

  • set_transform() - sets the item's transformation matrix.

If the canvas item supports a GooCanvasStyle setting, it must implement:

  • get_style() - returns the item's style.

  • set_style() - sets the item's style.

Since GooCanvasItemSimple implements most of the above methods and properties its source code is a good place to look for help.


Creating Item Models

As with creating canvas items, to create item models it is possible to subclass GooCanvasItemModelSimple or to implement the GooCanvasItemModel interface directly.

Subclassing GooCanvasItemModelSimple is very easy, since only one method from the GooCanvasItemModel interface must be implemented - create_item(). This should return a new canvas item for viewing the item model in a canvas. (It may be called multiple times if multiple canvases are viewing the same canvas model.)

The GooCanvasItemModelSimple subclass should emit the "changed" signal whenever it has changed, with a boolean flag indicating if the bounds need to be recomputed. The canvas items will connect to this signal and request an update or a redraw as appropriate.

To implement the GooCanvasItemModel interface directly, the class must implement the get_parent(), set_parent() and create_item() methods. It may also implement get_transform(), set_transform(), get_style() and set_style() methods if desired.

The class must also implement the “parent”, “title”, “description”, “can-focus”, “visibility”, “visibility-threshold”, “transform” and “pointer-events” properties. (The last 4 properties can simply be ignored if the application doesn't intend to use them.)