The Foo Canvas Widget

Overview

Coordinate Systems

The canvas has a coordinate system that goes from -inifinity to +inifinity for both x and y axes, how much of this is displayed is decided by the extent of the scroll_region. The scroll_region can be set to any size. The scale at which the scroll_region is projected on to the canvas window is decided by the pixels_per_unit of the canvas.

It is possible to set the scroll_region and pixels_per_unit so that the canvas window exceeds the X windows protocol limit of 65535, in actual fact the limit is actually 32767 as coordinates in the protocol are specified with short ints. The canvas does not deal with this problem, you the application programmer have to deal with it.

We deal with this by allowing the window to expand until it reaches 30000 pixels, after this we zoom in further by reducing the scroll_region in line with the increasing pixels_per_unit to maintain a window size of 30000.

A further problem seems to be that even we do this, if there are objects which are longer than the scroll_region, then they are not displayed properly. This is probably because the canvas is not clipping their coordinates at all so they exceed the 32767 limit. Thus you have to clip long objects as well as handle the overall canvas window size.

Event Handling

Event handling is not completely straight forward because event handling is split between events that happen within the root item of the canvas and events that happen within the canvas window but not within the root item. These two areas will often be disjoint because the width and/or height of one will be larger than the other. By default the root item is set to be the size of the bounding rectangle of all the items on the canvas. To get the root item and the canvas window to coincide you would have to create an item (not a group) that was as big as the window size.

Within the root item, event handling is consistent with a cascade of:

top item --> parent item --> ....... --> parent item --> root item

This cascade an only be triggered by events that happen on an item, events that happen on a group cannot be caught which is a shame. Even though the group containing an item seems to be the bounding box. Perhaps this is understandable for many applications so that its only if you click on an item within a group that anything happens.

If any item event handler registered by the application returns TRUE the event is not passed on to the items parent, otherwise it is until the root item is reached. At this point however the event is not passed on to any other obvious handler. If you register an event handler generally for the canvas window it gets called before the handlers registered for the canvas items. This is even if you use the g_signal_connect_after() call to register the handler. This is a pain for several reasons:

But perhaps is also good in that handling of canvas window events can be separated from handling of canvas item events.

Perhaps more thought required here.

Programming tips

Hidden side effects

foo_canvas_item_get_bounds() and unmapped foocanvas widgets

While it is perfectly legal to create items in a foocanvas widget that is unmapped, you cannot find the size of them. This is not ideal as you may wish to position stuff using the sizes of various groups/items before the widget is mapped.

foo_canvas_set_scroll_region() and the visible window

When you set a new scrolled region size, the foocanvas tries to optimise display by leaving unaltered the position of the visible window if that window still fits into the new scrolled region. This can be annoying if you are extending one of the margins of the scrolled region and hoping to see that extension reflected in the positioning of the window.

Very big windows and very big items

X Windows limits you to windows of 32k by 32k, you can create larger ones (65k by 65k) but you can't draw past 32k (and much else besides). The foocanvas would be the sensible place to encapsulate this information but it doesn't so you have to cope with it by monitoring the window size and restricting it via a combination of changing the zoom factor and the scrolled region size.

Even if you restrict the window size your problems are not over because you may have items that are longer than the window. The canvas will not clip these correctly, the visible effect of this is that these items will appear/disappear in a predictable but X server/Windows implementation dependant way. You need to clip them yourself. This is tedious because you cannot simply clip the group that contains the items, you must clip each item independently.

Item sizes and outline widths

If you specify a width for the outline line of an item, the line is drawn centred on the boundary of the item. That is to say the line is not entirely enclosed by the bounds of the item. For example if you specify an outline width of 1.0 then your item will have bounds that are 0.5 larger than perhaps you expect in every direction.

If you do not specify a width for the line it seems to be drawn using the X Windows idea of an "optimised" line of "zero" width so that the outline line ends up being entirely enclosed within the bounds of the item.

Gobject Properties for Foocanvas

foo-canvas.c:

/* FooCanvasGroup - a group of canvas items
 *
 * A group is a node in the hierarchical tree of groups/items inside a canvas.
 * Groups serve to give a logical structure to the items.
 *
 * Consider a circuit editor application that uses the canvas for its schematic
 * display.  Hierarchically, there would be canvas groups that contain all the
 * components needed for an "adder", for example -- this includes some logic
 * gates as well as wires.  You can move stuff around in a convenient way by
 * doing a foo_canvas_item_move() of the hierarchical groups -- to move an
 * adder, simply move the group that represents the adder.
 *
 * The following arguments are available:
 *
 * name		type		read/write	description
 * --------------------------------------------------------------------------------
 * x		double		RW		X coordinate of group's origin
 * y		double		RW		Y coordinate of group's origin
 */



foo-canvas-line.c:

/* Line item for the canvas.  This is a polyline with configurable width, cap/join styles, and arrowheads.
 * If arrowheads are enabled, then three values are used to specify their shape:
 *
 *	arrow_shape_a:  Distance from tip of arrowhead to the center point.
 *	arrow_shape_b:  Distance from tip of arrowhead to trailing point, measured along the shaft.
 *	arrow_shape_c:	Distance of trailing point from outside edge of shaft.
 *
 * The following object arguments are available:
 *
 * name			type			read/write	description
 * ------------------------------------------------------------------------------------------
 * points		FooCanvasPoints*	RW		Pointer to a FooCanvasPoints structure.
 *								This can be created by a call to
 *								foo_canvas_points_new() (in foo-canvas-util.h).
 *								X coordinates are in the even indices of the
 *								points->coords array, Y coordinates are in
 *								the odd indices.
 * fill_color		string			W		X color specification for line
 * fill_color_gdk	GdkColor*		RW		Pointer to an allocated GdkColor
 * fill_stipple		GdkBitmap*		RW		Stipple pattern for the line
 * width_pixels		uint			R		Width of the line in pixels.  The line width
 *								will not be scaled when the canvas zoom factor changes.
 * width_units		double			R		Width of the line in canvas units.  The line width
 *								will be scaled when the canvas zoom factor changes.
 * cap_style		GdkCapStyle		RW		Cap ("endpoint") style for the line.
 * join_style		GdkJoinStyle		RW		Join ("vertex") style for the line.
 * line_style		GdkLineStyle		RW		Line dash style
 * first_arrowhead	boolean			RW		Specifies whether to draw an arrowhead on the
 *								first point of the line.
 * last_arrowhead	boolean			RW		Specifies whether to draw an arrowhead on the
 *								last point of the line.
 * smooth		boolean			RW		Specifies whether to smooth the line using
 *								parabolic splines.
 * spline_steps		uint			RW		Specifies the number of steps to use when rendering curves.
 * arrow_shape_a	double			RW		First arrow shape specifier.
 * arrow_shape_b	double			RW		Second arrow shape specifier.
 * arrow_shape_c	double			RW		Third arrow shape specifier.
 */


foo-canvas-pixbuf.c

Nothing in the header about properties...

From code:

        g_object_class_install_property
                (gobject_class,
                 PROP_PIXBUF,
                 g_param_spec_object ("pixbuf", NULL, NULL,
                                      GDK_TYPE_PIXBUF,
                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_WIDTH,
                 g_param_spec_double ("width", NULL, NULL,
				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_WIDTH_SET,
                 g_param_spec_boolean ("width_set", NULL, NULL,
				       FALSE,
				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_WIDTH_IN_PIXELS,
                 g_param_spec_boolean ("width_in_pixels", NULL, NULL,
				       FALSE,
				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_HEIGHT,
                 g_param_spec_double ("height", NULL, NULL,
				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_HEIGHT_SET,
                 g_param_spec_boolean ("height_set", NULL, NULL,
				       FALSE,
				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_HEIGHT_IN_PIXELS,
                 g_param_spec_boolean ("height_in_pixels", NULL, NULL,
				       FALSE,
				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_X,
                 g_param_spec_double ("x", NULL, NULL,
				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_X_IN_PIXELS,
                 g_param_spec_boolean ("x_in_pixels", NULL, NULL,
				       FALSE,
				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_Y,
                 g_param_spec_double ("y", NULL, NULL,
				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_Y_IN_PIXELS,
                 g_param_spec_boolean ("y_in_pixels", NULL, NULL,
				       FALSE,
				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_ANCHOR,
                 g_param_spec_enum ("anchor", NULL, NULL,
                                    GTK_TYPE_ANCHOR_TYPE,
                                    GTK_ANCHOR_NW,
                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
                 PROP_INTERP_TYPE,
                 g_param_spec_enum ("interp_type", NULL, NULL,
                                    GDK_TYPE_INTERP_TYPE,
                                    GDK_INTERP_BILINEAR,
                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
        g_object_class_install_property
                (gobject_class,
		 PROP_POINT_IGNORES_ALPHA,
                 g_param_spec_boolean ("point_ignores_alpha", NULL, NULL,
				       FALSE,
				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));



foo-canvas-polygon.c:

/* Polygon item for the canvas.  A polygon is a bit different from rectangles and ellipses in that
 * points inside it will always be considered "inside", even if the fill color is not set.  If you
 * want to have a hollow polygon, use a line item instead.
 *
 * The following object arguments are available:
 *
 * name			type			read/write	description
 * ------------------------------------------------------------------------------------------
 * points		FooCanvasPoints*	RW		Pointer to a FooCanvasPoints structure.
 *								This can be created by a call to
 *								foo_canvas_points_new() (in foo-canvas-util.h).
 *								X coordinates are in the even indices of the
 *								points->coords array, Y coordinates are in
 *								the odd indices.
 * fill_color		string			W		X color specification for fill color,
 *								or NULL pointer for no color (transparent).
 * fill_color_gdk	GdkColor*		RW		Allocated GdkColor for fill.
 * outline_color	string			W		X color specification for outline color,
 *								or NULL pointer for no color (transparent).
 * outline_color_gdk	GdkColor*		RW		Allocated GdkColor for outline.
 * fill_stipple		GdkBitmap*		RW		Stipple pattern for fill
 * outline_stipple	GdkBitmap*		RW		Stipple pattern for outline
 * width_pixels		uint			RW		Width of the outline in pixels.  The outline will
 *								not be scaled when the canvas zoom factor is changed.
 * width_units		double			RW		Width of the outline in canvas units.  The outline
 *								will be scaled when the canvas zoom factor is changed.
 */



foo-canvas-rect-ellipse.c:

/* Base class for rectangle and ellipse item types.  These are defined by their top-left and
 * bottom-right corners.  Rectangles and ellipses share the following arguments:
 *
 * name			type		read/write	description
 * ------------------------------------------------------------------------------------------
 * x1			double		RW		Leftmost coordinate of rectangle or ellipse
 * y1			double		RW		Topmost coordinate of rectangle or ellipse
 * x2			double		RW		Rightmost coordinate of rectangle or ellipse
 * y2			double		RW		Bottommost coordinate of rectangle or ellipse
 * fill_color		string		W		X color specification for fill color,
 *							or NULL pointer for no color (transparent)
 * fill_color_gdk	GdkColor*	RW		Allocated GdkColor for fill
 * outline_color	string		W		X color specification for outline color,
 *							or NULL pointer for no color (transparent)
 * outline_color_gdk	GdkColor*	RW		Allocated GdkColor for outline
 * fill_stipple		GdkBitmap*	RW		Stipple pattern for fill
 * outline_stipple	GdkBitmap*	RW		Stipple pattern for outline
 * width_pixels		uint		RW		Width of the outline in pixels.  The outline will
 *							not be scaled when the canvas zoom factor is changed.
 * width_units		double		RW		Width of the outline in canvas units.  The outline
 *							will be scaled when the canvas zoom factor is changed.
 */



foo-canvas-text.c:

/* Text item for the canvas.  Text items are positioned by an anchor point and an anchor direction.
 *
 * A clipping rectangle may be specified for the text.  The rectangle is anchored at the text's anchor
 * point, and is specified by clipping width and height parameters.  If the clipping rectangle is
 * enabled, it will clip the text.
 *
 * In addition, x and y offset values may be specified.  These specify an offset from the anchor
 * position.  If used in conjunction with the clipping rectangle, these could be used to implement
 * simple scrolling of the text within the clipping rectangle.
 *
 * Properties marked with [*] also have _set properties associated
 * with them, that determine if the specified value should be used
 * instead of the default (style-defined) values
 *
 * The following object arguments are available:
 *
 * name			type			read/write	description
 * ------------------------------------------------------------------------------------------
 * text			string			RW		The string of the text label
 * markup		string			 W		A Pango markup string for the text label
 *
 * x			double			RW		X coordinate of anchor point
 * y			double			RW		Y coordinate of anchor point
 *
 * font			string			 W		A string describing the font
 * font_desc	        PangoFontDescription*	RW		Pointer to a PangoFontDescriptor
 * attributes           PangoAttrList*          RW		Pointer to a Pango attribute list
 * style		PangoStyle		RW		Pango style of font to use	[*]
 * variant		PangoVariant		RW		Pango variant of font to use	[*]
 * weight		int			RW		Pango weight of font to use	[*]
 * stretch		PangoStretch		RW		Pango stretch of font to use	[*]
 * size			int			RW		Size (in pixels) of font	[*]
 * size_points		double			RW		Size (in points) of font
 * scale                double                  RW              Ratio to scale font		[*]
 *
 * anchor		GtkAnchorType		RW		Anchor side for the text
 * justification	GtkJustification	RW		Justification for multiline text
 * clip_width		double			RW		Width of clip rectangle
 * clip_height		double			RW		Height of clip rectangle
 * clip			boolean			RW		Use clipping rectangle?
 * x_offset		double			RW		Horizontal offset distance from anchor position
 * y_offset		double			RW		Vertical offset distance from anchor position
 *
 * text_width		double			R		Used to query the width of the rendered text
 * text_height		double			R		Used to query the rendered height of the text
 *
 * fill_color		string			 W		X color specification for text
 * fill_color_gdk	GdkColor*		RW		Pointer to an allocated GdkColor
 * fill_color_rgba	guint   		RW		RGBA value used for AA color.
 * fill_stipple		GdkBitmap*		RW		Stipple pattern for filling the text
 */


foo-canvas-widget.c:

/* Widget item for canvas.  The widget is positioned with respect to an anchor point.
 * The following object arguments are available:
 *
 * name			type			read/write	description
 * ------------------------------------------------------------------------------------------
 * widget		GtkWidget*		RW		Pointer to the widget
 * x			double			RW		X coordinate of anchor point
 * y			double			RW		Y coordinate of anchor point
 * width		double			RW		Width of widget (see below)
 * height		double			RW		Height of widget (see below)
 * anchor		GtkAnchorType		RW		Anchor side for widget
 * size_pixels		boolean			RW		Specifies whether the widget size
 *								is specified in pixels or canvas units.
 *								If it is in pixels, then the widget will not
 *								be scaled when the canvas zoom factor changes.
 *								Otherwise, it will be scaled.
 */


Ed Griffiths <edgrif@sanger.ac.uk>
Last modified: Wed Mar 23 08:10:14 GMT 2005