Boost logo

Boost :

From: Reece Dunn (msclrhd_at_[hidden])
Date: 2004-12-14 06:31:41


Hi All,

I have been working on revising my GUI library that is currently in the
sandbox. The motivation for doing this was Andy Little's comment that most
GUI libraries start from a particular operating system and then abstract its
interface, providing work-arounds for other GUI systems.

There are several considerations to make with a GUI library. For insatance,
it should be possible to support GUI interfaces on portable devices (e.g.
Palm Pilot/PalmOS) and terminal devices (like is done with the VIM editor).
With these devices, code needs to be written to provide common components
such as buttons.

Also, the target operating system places design problems on creating a
generic interface. For example, Windows supports char * and wchar_t *
versions of its API, but the version that should be used depends on whether
you are targetting Windows 9x or NT based systems. Windows also requires
each component type to be registered before you can create an instance of
it.

Each operating system has different arguments that it passes when an event
is generated, containing data relating to that event. Consideration must be
made to allow events to be written generically, but also allow you to hook
into OS-specific events and get OS-specific data.

With all this in mind, how do you design a GUI library for C++? Although a
GUI component exists on the screen and is drawn on that screen, the
components and graphics elements should be kept seperate, since it is
primarily the OSs role to render a component. There are shared elements
(mostly the position, size and area types) and the graphics layer should
allow for graphics-based events to be handled by the components library.

Taking a look at how C++ is designed, it is predominantly data orientated.
That is, C++ allows you to create new data types that fit into the language
via operator overloading. The STL provides collections of data and
algorithms for manipulating those collections. Terminal-based applications
are supported by I/O streams that allow you to send data out and read data
in.

I have taken this as the primary design feature when implementing the GUI
components. Thus, each component has an event to signal user interaction
(ondatachanged, onselectionchanged, onpressed) and data that is associated
with the component.

The first thing that comes to mind when reading "data that is associated
with the component" is data binding. I think it is a mistake to build data
binding directly into the component architecture. The reason for this is
twofold:

You would need to use templates to bind the different data objects to the
control which makes it harder to implement the controls in a
platform-independant way and can lead to code bloat. Also, consider having a
textbox widget that you use to enter the age. Having:

   gui::textbox< long > age;

How does the textbox perform the conversion? By storing the data as a
string, the user can extract the information on data collection like this:

   long age = boost::lexical_cast< long >( age_input.get_text());

or

   long age = ::atol( age_input.get_text());

Another thing to take into account is when an input field has a validity
based on other component values. For example, the valitity of a dates day
field is dependant on month and year. In this instance, it is best to put
all the validation logic in the base component and extract the data to a
date class such as Boost.DateTime's date.

A component has a size and position, as well as visibility (visible or
hidden) and enabled/disabled states. The component should make it easy to
interoperate with native API.

The majority of the current proposals (mine included) have taken the
approach of providing native interoperability with the OS-specific data
structures. This is very difficult to maintain properly and support generic
code because of the different names of the data elements, types used (long
or float) and whether an area is (position + size) or (top-left +
bottom-right). Also, most OSs have (0,0) being the top-left corner, but
MacOS has (0,0) being bottom-left.

With this in mind, I have taken a drastic approach: do not provide native
interoperability for size, position and area. I have designed them as:

   struct position{ float x; float y; };
   struct size{ float dx; float dy; };
   struct area
   {
      float top; float left;
      float width; float height;
   };

This means that the interface must convert these to the native
representations and vise versa.

The current implementation (Windows specific at this time) can be found at:
   http://uk.geocities.com/msclrhd/gui/gui.zip

Thoughts? Comments?

Regards,
Reece


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk