[GUI] Common GUI API : What would the code look like?

Glynn Clements glynn.clements@virgin.net
Thu, 6 Mar 2003 02:48:39 +0000


David Sankel wrote:

> > > Questions ::
> > > 1) How does the interface look in
> > Test_midlevel.hs? 
> > > Is there any critiques in
> > >   regard to using this style in CGA (Common GUI
> > API)?
> > 
> > 1. Widgets aren't being given names. This makes it
> > impossible to refer
> > to them from outside of the code. It also eliminates
> > the possibility
> > of implementing a useable Xt (e.g. Motif) backend.
> > Toolkits which
> > don't name widgets can just ignore the name.
> 
> I'm sorry, I don't understand what you mean by
> "impossible to refer to them from outside of the
> code".  Can you give me an example of what you are
> talking about.

X resources. In an Xt-based program, every widget has a "pathname", so
widget properties can be specified in external files, command-line
arguments etc. Also, the Editres protocol allows one program to
query/set properties of a named widget in another program.

> > 2. Widget properties (label, position, dimension)
> > are being embedded
> > in the code, which is a bad idea. This point is
> > closely tied in with
> > point 1.
> 
> Where should this be in your opinion?

For Xt-based programs, these settings are obtained via X resources.

The X resource mechanism operates by merging several files to create a
resource database. Properties which are specific to the application
(e.g. widget labels) normally go into a file which is provided with
the application (look in /usr/lib/X11/app-defaults/ or
/etc/X11/app-defaults/ for examples). Other properties (typically
stylistic properties such as colours and fonts) are normally defined
by the user (e.g. in ~/.Xdefaults or via the RESOURCE_MANAGER property
on the root window).

The mechanism by which resource files are located allows for
per-language resource files, as well as for different resource files
for mono/colour displays, different resource files for different hosts
(a single installation may be used on multiple systems via NFS), etc.

On Windows, natural language strings normally live in a string table
resource. This means that the translators only need to translate a
simple text file, not source code.

Also, specifying literal dimensions is normally wrong. A widget's
dimensions vary depending upon the label (which is language-specific)
and the font (which is a user preference).

Xt-based toolkits don't normally use absolute positions, but specify
positions relative to an edge of another widget or the parent. This
deals with the fact that you don't know the sizes of the widgets at
compile time, and allows the layout to remain sensible if the parent
is resized (or if the parent's initial size wasn't what you were
expecting; under X, you can't *force* a top-level window to have a
specific size).

> > 3. Personally, I would prefer a generic widget
> > creation function,
> > rather than a different function for each type of
> > widget. E.g. Xt has
> > XtCreateWidget(), with the widget class being passed
> > as an argument.
> > A more Haskell-ish approach would be to use
> > typeclasses, e.g.:
> > 
> > 	class Widget a where
> > 		create :: String -> a -> [Property] -> IO a
> > 	--	create name parent properties = ...

That should be:

		create :: (Widget b) => String -> b -> [Property] -> IO a

A widget's parent must be a widget, but not necessarily the same type
of widget.

> Why would you prefer this over the other method?

Partly for simplicity and uniformity in general, and partly for
simplifying code which constructs UIs dynamically.

If you have the generic interface, you can easily implement
type-specific versions, e.g.:

	createLabel :: (Widget a) => String -> a -> [Property] -> IO LabelWidget
	createLabel = create

	createButton :: (Widget a) => String -> a -> [Property] -> IO ButtonWidget
	createButton = create

OTOH, if you only have the type-specific versions, implementing a
generic function comes down to a case statement which has to be
extended as new types are added.

> One
> problem with this method seems to be runtime errors
> for nonexistant classes.

No; with either approach, the class has to exist at compile time. If
you have widget-class values which are passed as an argument (as for
XtCreateWidget), you have to get the value from somewhere. With type
classes, the compiler will infer a specific type, which has to exist.

-- 
Glynn Clements <glynn.clements@virgin.net>