EW Event/Window Abstraction Library

Notice

Except where stated explicitly, the term "Windows" does not refer to a Microsoft product.

Purpose

EW provides an abstraction above the windowing and event systems, as well as graphics languages. Support is provided for:

The selection of a the windowing system and graphics language are done with "-D" compile flags which direct the EW header files. Libraries and applications written using the EW system are above this differentiation and their source code need not have any notion of what kind of system they are using. Note also that Xt and Motif are never used and are not needed.

The WDS WiDget Set is built from EW. EW has no dependence or need for WDS. An alternate widget set could, theoretically, be built over EW. However, it is convenient to use WDS widgets and systems as examples. To fully understand the capabilities of EW, you may also need to examine WDS as well.

Previous support for MEX and IrisGL is no longer current. There appears to be no need.

Design

EW is built on the libraries OSD, GPL, and WBA. OSD provides some basic OS abstractions like timers, console IO, memory allocation, and sleeping. GPL provides some fundamental storage classes, primarily the invaluable doubly-linked list. WBA provides standardized application interface including argument and environment handling.

EW takes OSD's abstraction a step further and covers window operations, the event loop, and fundamental graphics. It also provides a base widget class. As software written above OSD need not concern itself with which OS it is on, software over EW also need not worry about the variations in native GUI systems.

Features

Simple Window Handling

EW provides a simple interface to creating, opening, resizing, and moving windows. If a window is attached to the event context, it will be visible and receives events. A simple example shown below, hello.cc, places a WDS_Button in an EW_Window attached to the EW_EventContext.

Hierarchial Widget Organization

The widgets are organized in a basic tree where each root window and every widget has a linked-list containing any number of child widgets of any type, recognized or not. For example, in WDS, hierarchy nodes and hierarchy forms have a special relationship and can identify instances of each other. However, they both have to expect and handle children of any other type. Also, for instance, a button can contain, say, a scroll region, even though it may seem senseless.

This is implemented with base classes containing generic functionality and a set of virtual functions with default implementations.

This base functionality includes:

and virtuals for:

Automatic Rebuilding of Dynamic Structure, and Partial Redraw

For every non-trivial event, the affect widget hierarchy(ies) are given the chance to resize. Resizes are usually very fast and little effort is spent. During a resize, widgets can mark themselves or others as dirty. During the redraw pass, all widgets marked dirty will have their Draw() virtual member called, as well as all of its descendents. This partial redraw not only increases efficiency of the interface, it significantly reduces redraw-flicker when double-buffering is not used.

A major benfit of this approach is that during an operation or resize, entire widget sub-hierarchies, for example, can be moved to a new parent and the geometry of the entire structure will be automatically rebuilt and seen on the next redraw pass.

Uniform Event Set with Hierarchial Descension and Return

EW abstracts the native events from the platform and provide a uniform set of events though the class EW_Event. The specification of events is done so that an instance of EW_Event can specify groups of events to be used as a mask or filter.  This class contains:

The any/all values aren't generally generated from the event loop, but are used as masks. WDS uses grouped specifications in that way to register bypasses with scopes.

The Descend() virtual for widgets is rarely replaced and directs the event as follows:

This allows each widget to examine and/or use the event before and/or after the children do. The children may or may not affect the event. This approach is critcal to the full flexibility of many widget operations such as separable background drag.

Separable Event Usage

The event usage flags are constructed so that consumption is not restricted to an all or none basis. Currently, a widget can optionally specify usage of just the x or y component and grab and/or drag functionality. For example, if a widget has only one-dimensional operations, the other component can be left for another widget.

To show the benefits this provides, suppose you have use a vertical and horizontal partition from WDS and that the vertical partition is one of the children of the horizontal. The partitions know nothing special about each other. If there is diagonal motion with the left mouse held inside the vertical partition and no descendent of the vertical partition uses any of it, the vertical partition reacts, moving its children as appropriate. But since it only consumes the vertical component, the ancestral horizontal partition can simutaneously move children as well, including that entire vertical partition. The resultant effect is that the pointed-at child of the vertical partition can be moved diagonal, intact, even though no single widget is handling that motion and the pointed-at child does not even have to participate in its own motion.

To extend this even further, suppose you put a scroll region inside the vertical partition. Dragging the background of the scroll region normally just scrolls the children around, consuming motion events in both directions. But if the scrollable children are dragged all the way to their bounds, then the event is no longer consumed in either or both directions and can be used by ancestors. So, essentially the region can be dragged until it catches and then the partition(s) move the entire scroll region widget.

This effect is demonstrated in the WDS test example, although functionality may be difficult to see from just the screen shots.

Timers, Work Functions, and IdleMouse Events

The EW event context can be configured to send any number of periodic timer events. Each timer has a unique identifier, target window, and interval in milliseconds. The interval is only approximate. When set, timer events are passed to the appropriate window and seen by all its widgets. Widgets may choose to act on the timer, potentially using the identifier to recognize its meaning.

When activated and when no other events are pending, the event loop will send a system work function event to all windows to be seen by all widgets. This is a chance for any widgets to do any background labor that doesn't require constant user input. This labor should be done in small bits between work events so that the interface and other widgets have a fair share of time. Longer jobs should be handled using multi-threading, which is supported in the OSD layer below.

When the event queue just empties out, the event loop will send a idle-mouse event if the mouse has moved since the last idle-mouse event sent. WDS uses this event to redetermine sub-focus through its scopes.

Fundamental Graphics Operations

EW supplies the fundamental graphics primitives by mapping into the selected graphics language. These primitives include:

EW also provides a clipping stack to trim drawings to a specified rectangular bound. This clipping is organized in a stack so that each level of widget can use the intersection of its bounds with the currently clipped space. This is especially useful in the WDS scroll region widget whose children's geometry can far exceed their parents bounds.

Independently Double-Buffered Windows with Double Partial Redraw

EW supports double-buffered windows. Considerable redraws during the opaque resizing of many widgets such as partitions and scroll regions can cause interfaces to flash and flicker due to continuous clearing and redrawing. Animation software avoids this by using double-buffering. With this technique, active drawing is done in a hidden buffer and switched to the front when complete (true double-buffering swaps between buffers, it doesn't copy pixel maps). By going back and forth between buffers, the user only sees complete images. By applying these techniques to windows and widgets, interfaces appear solid and crisp, even during complex operations. To maximize efficiency, the partial redraw algorithm is expanded to account for redrawing widgets on two successive frames, when necessary. An always-redraw-everything algorithm would be simpler, but much less effiecient.

Double-buffering operates under OpenGL and pure X compilations (essentially all compilations except Win32/GDI; note that the Win32/OpenGL compilation does support double-buffering). Under pure X, colormap masks are used to simulate double-buffering. The colormap method is not truly double-buffered, is far better than the conventional single-buffered methods. Single-buffered windows are, of course, still fully supported.

Win32/GDI may be capable of using a method similar to pure X as above, but this is not currently being pursued. We do anticipate providing a smarter redraw mechanism that can utilize moved/scrolled regions with minimal redraw.

Simple Font Matching

Although graphics can be done with exactly the same rendering on the various platforms, fonts are not as straight-forward. Different types of machines and even different installations on the same type of machines can have a different choice of fonts available. To allow applications to choose reasonable fonts easily, a request is made specifying the pixel height and style (fixed/proportional, bold, italic). EW then matches the request against a preselected subset of the available fonts on the system and chooses a best weighted match.

The application can match and load several fonts in this manner, and then switch between them as it wishes. A text drawing window member function is provided to render character data, as well as various information queries.