[GUI] Another RFC on setting and getting.

Axel Simon A.Simon@ukc.ac.uk
Tue, 8 Apr 2003 10:49:46 +0100


On Sun, Apr 06, 2003 at 09:37:41PM -0700, David Sankel wrote:
> --- Glynn Clements <glynn.clements@virgin.net> wrote:
> > Axel Simon wrote:
> > 
> > > - Callbacks get separate functions which called
> > on<WidgetName><ActionName>
> > 
> > Just on<ActionName> would be better. The "activate"
> > callback is the
> > same callback whether it's a push-button,
> > toggle-button, etc.
> 
> I agree with this.

Ok, no problem. 

Related, but originally later in this email:
> I agree that things like this should use classes:
> 
> class HasLabelAttribute a where -- ...
> 
> > "Clicked" is likely to be an inappropriate name.
> > E.g. several Motif
> > widget classes have an "activate" callback. For a

Daan implemented a rather ad-hoc approach of partitioning
attributes and callbacks into type classes (message from
Mon, 10 Feb 2003 23:59:16 +0100). He agreed that a more orthogonal 
approach seems to be to either:
a) give each attribute/callback its own class
b) let each widget class containt all attributes
   and callbacks

I don't like a) because we would define a lot of classes
with rather special functionality (imagine 
"class ColumnChangeCallback"?!) which will only have a
single instance in practice. This is particularly bad
since the module system does not allow us to control
what classes are exported, leading to a polution
of the global class namespace and longer compile times.

On the other hand b) doesn't seem to work out logically.
Suppose we have push buttons, menu entries, table
entries, etc. They can all be activated, thus the
classes "Button", "MenuEntry", "TableEntry" need to
have a member "onActivate". So we need to put this
member in the parent of all these classes which is
probably "Widget". But descendants of "Widget" include
also "DialogBox" or something similar which cannot be
activated.

BTW Gtk takes the latter approach. Widget contains a
callback called onActivate which is dead if the
derived widget has no such capability.


> > Read-only or write-only attributes could be
> > implemented by throwing an
> > error for prohibited operations. Read-only
> > attributes could be
> > implemented by simply ignoring write operations.
I agree with Alastair here that catching errors at compile
is preferable.

> Although I haven't discovered a use for write-only
> attributes for a gui toolkit, it seems that it would
> fit clearly in the constructor.
Ok, some inspiration: Gtk allows labels to contain
either a simple string or markup like
"<red>warning</red>: about to erase file". If the
markup is passed as an algebraic data type we would
need to parse the label's text in order to get the
algebraic data type back. So it's easier to consider
the text of a Gtk lable as write only. But the label
can be changed after the creation.
Note that I do not claim that this is useful. We might
get away with passing all write-only attributes at
construction time.

> newSomething requiredArgument [attribute := list]
> 
> For read-only attributes, you would most always want
> to read the value and nothing else.
> 
> h <- getHeight myButton
I think it would be more systematic (and thus easier to
learn) if we could use "get" here as well.

> > > data WidgetClass w => Attr a w = <abstract>
> > 
> > Using "WidgetClass" will confuse anyone who is used
> > to Xt. With Xt, a
> > "WidgetClass" is a (pointer to a) record which
> > describes the class
> > itself; the individual widgets have type "Widget".
That is the same in Gtk. But it is not possible to
change the common class description without going
through an instance. In gtk2hs I borrowed form gtk+hs
which has
class WidgetClass w where...
and
instance WidgetClass Widget where...
which basically contains nothing but casting functions.
The class names talk about types so I think it is
consistent to use WidgetClass here.
But I'll be flexible if this is fundamentally wrong.

> > > -- An example for a Button widget: The constructor
> > has one
> > > -- mandatory argument.
> > > newButton :: Container -> [Setter Button] -> IO
> > Button
> > 
> > 1. This omits widget name, which should be
> > mandatory.
> 
> This has been discussed thoroughly earlier.  There
> didn't seem to be a consensus to do this.  I suggest
> that this be an CGA implementation specific extension.
Maybe we can discuss that together with the layout
issue. Specifying layout across GUI toolkits is probably
one of the biggest challenges.


> > More importantly, most callbacks will need to take
> > one or more
> > parameters which provide further information about
> > the event.
[..]
> > 1. Records (with named fields) would be preferable
> > to tuples; Some
> > callbacks get passed a significant amount of data.
>
> Can you give an example where a standard widget would
> pass a significant amount of data?
In Gtk you can connect to "onActivate" which takes "IO ()" as
callback handler. If you go for the bare button click, it looks
like this:
  | Button {
    sent        :: Bool,
    click       :: Click,
    time        :: Integer,
    x,y         :: Double,
    modif       :: Modifier,
    button      :: Button,
    xRoot,
    yRoot       :: Double }
which is one constructor for an "Event". I don't see
an advantage in requireing to pass exactly one parameter to
each callback. A lot of other events have two or three
parameters, I think you can write that many underscores.
Usually you are interested in at least one bit of information
and then I'd rather write
handler _ xPos _ =
than
handler (_,xPos,_) =

> > 2. This approach requires a different function for each type of
> > callback. I would prefer a single polymorphic addCallback function.
>
> This is again making runtime errors into what could
> have been compile time errors.  I think this is reason
> enough not to do it this way.
Although I don't see the latter point, I object the single
addCallback approach. It makes the implementation more
difficult and programs harder to understand. I don't see
any advantage.

Axel.