Last modified 3 years ago Last modified on 12/27/08 22:44:39

Anatomy of a Dialog

The following is a sample betawidget dialog. Although (hopefully) the finished GUI will not look anything like this the semantics should remain the same.

A sample dialog for a [[Betawidget]] GUI

With class names annotated.

A sample Betawidget GUI with the various class instances annotated.

Widget Drawing/Painting

Each widget instance has its own Cairo drawing context along with a flag specifying if the widget needs to be redrawn or not. When the status of a widget changes (e.g., in response to an event handler) the widget should set the redraw flag to true.

When a widget is asked to draw itself the widget first checks to see if it needs redrawing or not; if so the user-defined widgetDoDraw method is invoked to draw the widget to the Cairo context; otherwise, no action is taken. Next, the widget draws each of its children in-turn (by calling widgetDraw on them).

The widget system relies on just-in-time compositing to produce the final result which is displayed on-screen. Once all widgets have been drawn they must be composited onto a single image/texture before they can be useful to the user.

This compositing process is currently done entirely in software (using cairo). Should the performance prove unacceptable then it is likely that we will switch to OpenGL to hardware accelerate the process.

Event Handling

This is one of the more complex elements of betawidget. In betawidget events come in two forms: # core events which are events that have a 1:1 mapping with SDL events; # extended events which are those which have no corresponding SDL event.

All events begin life as a core event, with it being the job of a widgets event dispatcher to convert core events into extended events. The dispatcher also has the job of passing relevant events onto child widgets.

The current core events are:

  • mouse down;
  • mouse up;
  • mouse move;
  • key down;
  • key up;
  • text.

The default widget event dispatcher (see betawidget/src/widget.c: widgetHandleEventImpl) uses these core events to generate several extended events, which are more convenient. These include:

  • mouse click;
  • mouse enter;
  • mouse leave;
  • focus;
  • blur.

Furthermore it also incurs the responsibility of doing some bookkeeping for the widget. This includes thing such as if the widget has focus or not, if the widget has the mouse over it and if the mouse is pressed down.

Mouse Click Event Specifics

Although a seemingly simple and somewhat superfluous event, the mouse click event does serve a specific purpose. This purpose is to emulate the functionality and behaviour that users have come to expect.

In order for a click event to be generated the following events must occur in sequence:

  • The widget receives a mouse down event;
  • which is followed by one or more mouse move events;
  • with the corresponding mouse up event occurring on the same widget that received the mouse down event.

This behaviour is important as it allows a user to 'void' clicking on a widget by moving the mouse off of the widget and releasing the mouse button. This is similar to how, in a game of chess, so long as one does not take ones hand off of a piece, they are still able to change their mind.

Timers

Betawidget includes complete support for timers; which are handled in much the same way as any other event. The only main difference, so far as the user need be concerned is that timers are added using widgetAddTimerEventHandler as opposed to widgetAddEventHandler

Compatibility

Although the core events are based off of those in SDL Betawidget is not limited to being used with SDL. I am currently in the process of writing a Qt wrapper that will produce core events from Qt events. This will allow for betawidget to be used inside of Warzone Studio (which is also being written in Qt).

Widget Masking

It has become apparent that the assumption that all widgets are rectangular (and so have a rectangular bounding box for the purposes of event handling), is not a viable one. Therefore, I am in the process of adding support for masks to betawidget.

How masks work is probably best illustrated by use of a diagram. Consider the following widget

A Betawidget button plus bounding box.

the bounding box is represented by a rectangle (dashed line) and the button itself (i.e., what the user sees) by a green hexagon. It would be undesirable for mouse clicks/events outside of the hexagon to be handled by the buttons event handlers. Therefore, to hide, or mask unwanted events the following mask is used:

A betawidget mask for a button.

as is evident from the image, white regions of the mask allow events to pass unabridged, while black areas do not. Putting this all together gives us the following:

A betawidget button, showing how the mask affects mouse events..

The mask is a cairo context, much like the context the button itself is rendered too. The difference is that instead of being a 32-bit ARGB surface, it is an A1 or bit-mapped surface. (So each pixel is either 1 or 0, with there being 8 pixels packed into a single byte.)

There is no requirement for the mask to be representative of the widget, although masks which do not correspond to the outline of the widget may be counter-intuitive to users.

Why Masking is Important

The advantage of using masking, over say, just a plain bounding rectangle, is that it is possible to arrange widgets in an optically favourable layout. Without masking, it would not be possible for any two widget's bounding boxes to overlap (as multiple events would be generated). If a layout container can be sure that the widgets being added to it make use of masks then it will be able to safely allow for their bounding boxes to overlap.

{{Template:Betawidget}} Category:Betawidget ?