Another RFC on setting and getting.

Hi all, it seems that there are a lot of issues left open about the following basic concepts: - The usage of (:=) and existential types instead of (=:) for turning attributes into something which can go into an argument list. Pro for := : It reflects the semantics of assigning something to an attribute. Contra for := : it might be a keyword in future Haskell, it requires non-Haskell 98 features. - The necessity of read only attributes. - The necessity of write only attributes. Or getting rid of them in favour of mandatory arguments (to the constructor). - Callbacks get separate functions which called on<WidgetName><ActionName> I would like propose the following basic setup, together with the questions how read only attribute can fit in. Write only attributes could be easily added by just supplying "pushButtonNow :: Setter Button", albeit breaking the := syntax - you'd have to say set b ["Hello" =: label, pushButtonNow] Here it is: -- An attribute is a property of a widget. It knows how to set the -- and how to read the value of this property. data WidgetClass w => Attr a w = <abstract> -- The assignment operator joins an attribute with its value. (=:) :: a -> Attr a w -> Setter w -- Set and get can be used on every widget. set :: WidgetClass w => w -> [Setter w] -> IO () get :: WidgetClass w => w -> Attr a w -> IO a -- An example for a Button widget: The constructor has one -- mandatory argument. newButton :: Container -> [Setter Button] -> IO Button -- The Button has at least this attribute. label :: Attr String Button -- This is one callback the Button provides. Note that you cannot -- attach any function at construction time or with the set -- function. The returned action is the unregister function. onClicked :: ButtonClass b => b -> IO () -> IO (IO ()) Axel.

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 would like propose the following basic setup, together with the questions how read only attribute can fit in. Write only attributes could be easily added by just supplying "pushButtonNow :: Setter Button", albeit breaking the := syntax - you'd have to say set b ["Hello" =: label, pushButtonNow]
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.
Here it is:
-- An attribute is a property of a widget. It knows how to set the -- and how to read the value of this property. 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". In traditional OO terminology, "WidgetClass" is the metaclass of the "Widget" class. Also, the above implies that attributes are tied to a specific type, rather than to a class (including subclasses). I don't know about GTK, but Xt and Qt both use an OO-style class hierarchy. OTOH, different toolkits (and, in the case of Xt, widget sets) will have differing hierarchies (e.g. for both Athena and Motif, push buttons are a subclass of labels, but this isn't so for Qt).
-- 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. 2. Container should be a class rather than a type.
-- The Button has at least this attribute. label :: Attr String Button
So do labels, toggle buttons. Is a label's label really different from a button's label?
-- This is one callback the Button provides. Note that you cannot -- attach any function at construction time or with the set -- function. The returned action is the unregister function. onClicked :: ButtonClass b => b -> IO () -> IO (IO ())
"Clicked" is likely to be an inappropriate name. E.g. several Motif
widget classes have an "activate" callback. For a push-button, this
may occur either as a result of clicking on a button, or using the
keyboard accelerator (Alt + underlined character), or moving the
keyboard focus to the button then pressing space. Either way, it's the
same callback.
Ditto for Windows, and probably for most other toolkits (except
Athena).
More importantly, most callbacks will need to take one or more
parameters which provide further information about the event. E.g. for
Motif's XmPushButton, the information passed to the activate callback
allows the callback to distinguish between single/double clicks. For
more complex widgets (e.g. text fields), substantially more
information may be passed.
We would need a CallbackData class in order to provide a generic
function for adding callback handling, e.g.
addCallback :: (Widget w, CallbackData d) => w -> CallbackIdentifier -> (w -> d -> IO ()) -> IO ()
Or even use dependent types, so that the data has to match the
specific callback.
--
Glynn Clements

On Sat, 5 Apr 2003 06:55:28 +0100
Glynn Clements
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.
From now on, I propose to tag messages about state handling with the [State] keyword, and invite people who are interested in other aspects of the CGA to start their own discussions.
We could distinguish between two kind of read-only attributes: those which are fixed at creation-time and those which are written somewhere and read somewhere else. Apart from this detail, wich is perhaps unuseful, I think that we should avoid run-time errors when we can model them in the type system, as it leads to a more detailed machine-checked specification. For the specific case, I like an approach where a state variable has two components: a read-only one and a write-only one, as in Variable a = Variable { writeable :: Writeable a, readable :: Readable a } A read-only attribute is exposed with just the "Readable a" type, not with the "Variable a" one. I have to read the other e-mails, and to fit this approach in the getter/setter one, comments are welcome. Vincenzo

--- Glynn Clements
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.
I would like propose the following basic setup, together with the questions how read only attribute can fit in. Write only attributes could be easily added by just supplying "pushButtonNow :: Setter Button", albeit breaking the := syntax - you'd have to say set b ["Hello" =: label, pushButtonNow]
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.
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. newSomething requiredArgument [attribute := list] For read-only attributes, you would most always want to read the value and nothing else. h <- getHeight myButton And for read and write attributes, the attribute mechanism works fine.
Here it is:
-- An attribute is a property of a widget. It knows how to set the -- and how to read the value of this property. 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". In traditional OO terminology, "WidgetClass" is the metaclass of the "Widget" class.
I don't know anyone who has used Xt; I've only been in the programming business for 8 years. Consequently, I don't think we should make sure the names don't conflict with the naming scheme of some old libraries.
-- 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. myButton <- newButton [ title := "Okay", i_name := "RightButton" ]
-- The Button has at least this attribute. label :: Attr String Button
So do labels, toggle buttons. Is a label's label really different from a button's label?
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 push-button, this may occur either as a result of clicking on a button, or using the keyboard accelerator (Alt + underlined character), or moving the keyboard focus to the button then pressing space. Either way, it's the same callback.
I agree, activated is a much better name than Clicked for the button example.
More importantly, most callbacks will need to take one or more parameters which provide further information about the event. E.g. for Motif's XmPushButton, the information passed to the activate callback allows the callback to distinguish between single/double clicks. For more complex widgets (e.g. text fields), substantially more information may be passed.
We would need a CallbackData class in order to provide a generic function for adding callback handling, e.g.
I do not think a class is necessary. But for signals that do have arguments, I suggest we put the arguments in a tuple. There will be several times where the library user would wish to ignore arguments and this would make it simpler. onCurserPositionChange (paragraph, position) = -- ... onCurserPositionChange _ = -- ... David J. Sankel

David Sankel wrote:
I don't know anyone who has used Xt; I've only been in the programming business for 8 years. Consequently, I don't think we should make sure the names don't conflict with the naming scheme of some old libraries.
While it may not be used much in the free software ghetto (Athena is primitive, and Motif wasn't commonly available on free Unices until recently), Motif is still widely used in commercial Unix/X11 software, and is likely to remain so. BTW, I'm curious as to what you were using 8 years ago; neither GTK nor Qt were around then.
-- 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.
myButton <- newButton [ title := "Okay", i_name := "RightButton" ]
This would mean that a missing name would be a run-time error, when it should be caught at compile time.
More importantly, most callbacks will need to take one or more parameters which provide further information about the event. E.g. for Motif's XmPushButton, the information passed to the activate callback allows the callback to distinguish between single/double clicks. For more complex widgets (e.g. text fields), substantially more information may be passed.
We would need a CallbackData class in order to provide a generic function for adding callback handling, e.g.
I do not think a class is necessary. But for signals that do have arguments, I suggest we put the arguments in a tuple. There will be several times where the library user would wish to ignore arguments and this would make it simpler.
onCurserPositionChange (paragraph, position) = -- ...
onCurserPositionChange _ = -- ...
1. Records (with named fields) would be preferable to tuples; Some
callbacks get passed a significant amount of data.
2. This approach requires a different function for each type of
callback. I would prefer a single polymorphic addCallback function.
--
Glynn Clements

--- Glynn Clements
David Sankel wrote: BTW, I'm curious as to what you were using 8 years ago; neither GTK nor Qt were around then.
Offtopic, but 8 years ago UNIX wasn't widely accessible. So I was stuck with either text based applications or winapi for the most part.
that this be an CGA implementation specific extension.
myButton <- newButton [ title := "Okay", i_name := "RightButton" ]
This would mean that a missing name would be a run-time error, when it should be caught at compile time.
How about myButton <- i_newButton "RightButton" [ title := "Okay" ] in accordance with my previous statements.
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?
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. Anyway, we can still use a single polymorphic addCallback function. instance HasAddCallback (Int,Int,Float) -- ... Cheers, David J. Sankel

David Sankel wrote:
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?
1. For Motif's XmText and XmTextField widgets, XmNlosingFocusCallback, XmNmodifyVerifyCallback, and XmNmotionVerifyCallback are passed: typedef struct { int reason; XEvent * event; Boolean doit; XmTextPosition currInsert, newInsert; XmTextPosition startPos, endPos; XmTextBlock text; } XmTextVerifyCallbackStruct, *XmTextVerifyPtr; 2. For Motif's XmList widget, various callbacks are passed: typedef struct { int reason; XEvent *event; XmString item; int item_length; int item_position; XmString *selected_items; int selected_item_count; int *selected_item_positions; char selection_type; unsigned char auto_selection_type; } XmListCallbackStruct; 3. For several Motif widgets, XmNdestinationCallback is passed: typedef struct { int reason; XEvent *event; Atom selection; XtEnum operation; int flags; XtPointer transfer_id; XtPointer destination_data; XtPointer location_data; Time time; } XmDestinationCallbackStruct;
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.
To some degree, that's inevitable if you wish to expose the full
functionality of Xt. Xt's design is inherently open-ended. E.g. a core
set of generalised functions are used for all widget and attribute
types, rather than each widget having a separate creation function,
each attribute having separate get/set functions, coupled with the use
of strings as keys and the ability to obtain widget-class descriptions
at run time.
This aspect can be important if widget hierarchies are written in UIL
and loaded at run-time.
Having said that, fixing a subset of functionality at compile-time
probably wouldn't impact most applications (so long as the subset
itself is adequate). I doubt that anyone considers interface builders
or an editres clone to be a priority.
Basically, the main reason I joined this list was to try to ensure
that any compromises were made after full consideration of the facts.
I've seen too many cases where supposedly generic interfaces were
based upon an over-simplified model (toolkits which assume that issues
related to X's client-server model can be ignored; wxWindows not using
widget names, resulting in the Motif implementation sucking badly;
that sort of thing).
Aside:
More generally (and getting somewhat off-topic for this list), "open"
interfaces are an area where static typing is problematic. For another
example, consider the issue of writing a Haskell binding for the BSD
sockets API.
In the underlying API, socket addresses are an abstract type. The
functions refer to addresses via a pointer/length pair; the only thing
that's known about the data is that the first two bytes indicate the
family. The set of valid families isn't known at compile time; with a
modular OS kernel, a new socket family could come into existence at
any time.
While the merits of static versus dynamic interfaces can be discussed
ad nauseum, when it comes to interfacing to existing libraries,
dynamic, open-ended interfaces do exist.
--
Glynn Clements

On Tue, 8 Apr 2003 08:20:29 +0100
Glynn Clements
I doubt that anyone considers interface builders or an editres clone to be a priority
I sometimes wonder how the model we are designing will fit with interface builders. I personally think that an interface builder is an important priority. While I don't like RAD, I like using the right instrument for every purpose, and designing an interface seems like a task for a paint-like program. Or for a latex-like language, I am not sure :) So, to remain in-topic, how do you people think about a GUI builder written in CGA, for the CGA? Perhaps a requirement for the project is that we need to be able to create custom widget that "look like a button, but behave like a drawing", or something like this. That would mean to have a type for "looks", and that widget should have an optional attribute of that type; one should to be able to: get the "look" property for widget "A" and then set the "look" property for widget "B" with the value obtained by the former operation. There could be "look" type constants. I don't see a real path in the definition of such an interface, nor I can understand immediatly if it's mid-level or high-level. Maybe somebody has clearer ideas on this? Vincenzo

Vincenzo Ciancia wrote:
I doubt that anyone considers interface builders or an editres clone to be a priority
I sometimes wonder how the model we are designing will fit with interface builders. I personally think that an interface builder is an important priority. While I don't like RAD, I like using the right instrument for every purpose, and designing an interface seems like a task for a paint-like program. Or for a latex-like language, I am not sure :)
So, to remain in-topic, how do you people think about a GUI builder written in CGA, for the CGA?
I think that it would add a whole bunch of additional requirements to
the CGA which would only be necessary for interface builders.
Part of the problem is that such tools often rely heavily on
functionality which is specific to a particular toolkit. E.g. a Motif
interface builder will generate UIL code, because that's the standard
for describing Motif UIs.
Also, Motif interface builders tend to rely upon the fact that Xt
widget classes are self-describing. Given a WidgetClass value (pointer
to a WidgetClassRec structure), you can enumerate the attributes of
the widget class. Consequently, Motif interface builders normally
allow you to use widget classes from extension libraries (i.e. widget
classes which were unknown at the time the interface builder was
written and compiled).
There's also the fact that different toolkits have substantially
different models for geometry management. E.g. Win32 uses absolute
positions and sizes, specified in fractions of the font size. OTOH, Xt
normally uses attachments; the actual positions and sizes are computed
at run-time based upon factors such as the label text and font of each
of the widgets, the size of the window (and whether the window size
can be changed to accommodate the layout or whether the layout must
adapt to the window size).
This is part of the reason why I would prefer an API which encouraged
the code to stick to the core application logic (i.e. perform a given
action in response to a given event), and have everything else
determined "by other means".
--
Glynn Clements

On Tue, Apr 08, 2003 at 09:31:31PM +0100, Glynn Clements wrote:
So, to remain in-topic, how do you people think about a GUI builder written in CGA, for the CGA? [..] Also, Motif interface builders tend to rely upon the fact that Xt widget classes are self-describing. Given a WidgetClass value (pointer to a WidgetClassRec structure), you can enumerate the attributes of the widget class. Consequently, Motif interface builders normally allow you to use widget classes from extension libraries (i.e. widget classes which were unknown at the time the interface builder was written and compiled). I get the impression that the model of Motif and Gtk are very similar...
There's also the fact that different toolkits have substantially different models for geometry management. E.g. Win32 uses absolute positions and sizes, specified in fractions of the font size. OTOH, Xt normally uses attachments; the actual positions and sizes are computed at run-time based upon factors such as the label text and font of each of the widgets, the size of the window (and whether the window size can be changed to accommodate the layout or whether the layout must adapt to the window size).
This is part of the reason why I would prefer an API which encouraged the code to stick to the core application logic (i.e. perform a given action in response to a given event), and have everything else determined "by other means". So you're saying we should not supply any layouting mechanism to arrange widgets? Does that mean we supply tools that convert MS Visual Studio .rc files, Glade's xml files and Motif's UIL files into backend-specific Haskell code that builds a widget tree? The advantage is that some higher level bindings (namely Fudgets) use combinators to arrange widgets how information flow is handled. If we force the user to use external tools for the layout, Fudgets couldn't be implemented on top of CGA. But I do not dislike the idea. It could safe us a lot of trouble.
Axel.

Axel Simon wrote:
So, to remain in-topic, how do you people think about a GUI builder written in CGA, for the CGA? [..] Also, Motif interface builders tend to rely upon the fact that Xt widget classes are self-describing. Given a WidgetClass value (pointer to a WidgetClassRec structure), you can enumerate the attributes of the widget class. Consequently, Motif interface builders normally allow you to use widget classes from extension libraries (i.e. widget classes which were unknown at the time the interface builder was written and compiled).
I get the impression that the model of Motif and Gtk are very similar...
No; they're far from similar.
There's also the fact that different toolkits have substantially different models for geometry management. E.g. Win32 uses absolute positions and sizes, specified in fractions of the font size. OTOH, Xt normally uses attachments; the actual positions and sizes are computed at run-time based upon factors such as the label text and font of each of the widgets, the size of the window (and whether the window size can be changed to accommodate the layout or whether the layout must adapt to the window size).
This is part of the reason why I would prefer an API which encouraged the code to stick to the core application logic (i.e. perform a given action in response to a given event), and have everything else determined "by other means".
So you're saying we should not supply any layouting mechanism to arrange widgets?
Not necessarily. I'm saying that different GUI APIs have radically different approaches to layout, so providing a common interface isn't just a matter of matching up equivalent functions in each native API. Win32 takes the simplistic approach; each widget has a specified position and size. The Xt approach is more involved. It has an open-ended set of container classes (subclasses of Composite), each of which is responsible for laying out its children. Most container classes are subclasses of Constraint, which allows the container to add attributes to each of its children. The most common Motif container classes are XmRowColumn (which lays out its children in rows/columns; menu bars and menus use this) and XmForm (a more generic class, which allows each widget to specify its position relative to its siblings or to the parent). The advantage of the Xt approach is that the layout can be (and usually is) computed dynamically based upon the preferred sizes of the individual widgets (which depends upon factors such as the label text and font). This simplifies I18N (you only have to change the strings; the layout will adjust itself), and allows dialogs to be resized sensibly without having to write any code.
Does that mean we supply tools that convert MS Visual Studio .rc files, Glade's xml files and Motif's UIL files into backend-specific Haskell code that builds a widget tree?
I wouldn't advocate that approach. Instead, I would advocate having the individual backends provide bindings for the native interface (e.g. MrmOpenHierarchy etc for Motif). Even if the the functions themselves aren't portable, the data that they return would be.
The advantage is that some higher level bindings (namely Fudgets) use combinators to arrange widgets how information flow is handled. If we force the user to use external tools for the layout, Fudgets couldn't be implemented on top of CGA. But I do not dislike the idea. It could safe us a lot of trouble.
Ultimately we still need to allow interfaces to be constructed
programmatically.
Basically, I'm suggesting to focus initially on the aspects which
absolutely have to be dealt with by code, and which are relatively
portable.
Code has to be able to get/set widgets' state (toggle state, text
field contents, slider positions, list selections etc), and there is
substantial commonality between the different platforms. This code
tends to be closely linked to the application logic.
OTOH, the actual UI creation tends to be logically separate from the
application logic. It could involve calling a few high-level functions
which read an entire widget hierarchy from an external source, or it
could be a chunk of fairly formulaic code which doesn't really
interact with the rest of the code except for storing a lot of
"handles" for later reference. It's also the area which has to deal
with many of the more significant portability issues; e.g. different
layout models, differences in the set of available widgets (e.g. are
toggle buttons and radio buttons different classes?).
There is an analogue to be found in the way in which OpenGL deals with
initialisation: it doesn't. OpenGL itself first assumes that you
already have a context in which rendering operations are meaningful,
and sticks to detailing what occurs within that context.
The actual initialisation is performed by "other means". At the lowest
level, you have the glX* functions on X11, wgl* on Win32, agl* on Mac
etc, but it's more common to just use a pre-built "OpenGL canvas"
widget (or GLUT).
This approach allows the core application code to remain unconcerned
with such details.
--
Glynn Clements

Glynn Clements
different GUI APIs have radically different approaches to layout [...]
Win32 takes the simplistic approach; each widget has a specified position and size. The Xt approach is more involved. It has an open-ended set of container classes (subclasses of Composite), each of which is responsible for laying out its children. [...] The most common Motif container classes are XmRowColumn [(which lays out its children in rows/columns; menu bars and menus use this) and XmForm (a more generic class, which allows each widget to specify its position relative to its siblings or to the parent).
It sounds like all GUI APIs provide absolute positioning and may provide automatic layout. So, unfortunate and limited as using absolute positioning is, I suggest that the core design should provide that as the base-level, portable, API. This will be easy to implement and let us move forward. We can then experiment with ways of implementing our own container classes in Haskell (for those platforms that lack them) and of making the different styles of container class behave in a consistent way. If it turns out that the best of the automatic layout APIs in common use can be simulated on all platforms, we can then go ahead and add that to the portable specification. (Also, people can play with their own layout mechanisms and algorithms if we give them the right base to build on. e.g., I think that TeX's layout mechanism (e.g., 2\vfill + \vfil + 10pt) would be cool to use for laying out windows. I may be wrong but it'd be fun to play with it.) -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/ ps What is the best automatic layout API out there these days? Is there some online documentation for it?

Absolute positioning is quite unsuitable for a portable GUI library. fonts are different sizes, widgets will be drawn differently, it will just encourage programs which look bad on systems they were not designed. not that that type of layout should not be provided, it can come in handy, but it should not be the encouraged way to do things unless you are writing a new portable layout algorithm, as it would require extensive querying of the exact properties of every widget used to make sure your layout looks good across platforms. As a useful tidbit, I was able to implement full gtk hbox and vbox layout algorithms in about 30 lines of Haskell. I think something like them would be a good middle-ground common layout algorithm which could be easily implemented on platforms that don't have it (on the Haskell side of things) and is powerful enough to express most layouts people want that will rescale and deal with differing sized widgets well. John On Thu, Apr 10, 2003 at 10:29:41AM +0100, Alastair Reid wrote:
Glynn Clements
writes: different GUI APIs have radically different approaches to layout [...]
Win32 takes the simplistic approach; each widget has a specified position and size. The Xt approach is more involved. It has an open-ended set of container classes (subclasses of Composite), each of which is responsible for laying out its children. [...] The most common Motif container classes are XmRowColumn [(which lays out its children in rows/columns; menu bars and menus use this) and XmForm (a more generic class, which allows each widget to specify its position relative to its siblings or to the parent).
It sounds like all GUI APIs provide absolute positioning and may provide automatic layout.
So, unfortunate and limited as using absolute positioning is, I suggest that the core design should provide that as the base-level, portable, API. This will be easy to implement and let us move forward.
We can then experiment with ways of implementing our own container classes in Haskell (for those platforms that lack them) and of making the different styles of container class behave in a consistent way. If it turns out that the best of the automatic layout APIs in common use can be simulated on all platforms, we can then go ahead and add that to the portable specification.
(Also, people can play with their own layout mechanisms and algorithms if we give them the right base to build on. e.g., I think that TeX's layout mechanism (e.g., 2\vfill + \vfil + 10pt) would be cool to use for laying out windows. I may be wrong but it'd be fun to play with it.)
-- --------------------------------------------------------------------------- John Meacham - California Institute of Technology, Alum. - john@foo.net ---------------------------------------------------------------------------

On Thu, Apr 10, 2003 at 02:41:48AM -0700, John Meacham wrote:
Absolute positioning is quite unsuitable for a portable GUI library. fonts are different sizes, widgets will be drawn differently, it will just encourage programs which look bad on systems they were not designed. not that that type of layout should not be provided, it can come in handy, but it should not be the encouraged way to do things unless you are writing a new portable layout algorithm, as it would require extensive querying of the exact properties of every widget used to make sure your layout looks good across platforms.
As a useful tidbit, I was able to implement full gtk hbox and vbox layout algorithms in about 30 lines of Haskell. I think something like them would be a good middle-ground common layout algorithm which could be easily implemented on platforms that don't have it (on the Haskell side of things) and is powerful enough to express most layouts people want that will rescale and deal with differing sized widgets well.
Our self-set goal is to provide native look-and-feel. If we don't abandon that, we need to provide each platform's mechanism for laying out widgets. I guess it's ok to use a dynamic layouting mechanism to construct Windows Dialogs as long as we don't make them resizable. But we don't want to restrict people using the native mechanisms (because the layout calculated by the Haskell code might not be ideal). Thus, maybe we aim for a simple cross-platform dynamic layout algorithm and provide the ability to override these settings with external resource files. Axel.

Hello everyone! The Easter Holidays are beginning, and finally I have some time to spare again.... So allow me to try to summarize the discussion about dynamic layout: We'll have one or more of the following (correct me if I left something out, or if I'm misrepresenting something): 1) programmatic creation of widgets with fixed positions 2) programmatic creation of widgets using dynamic layout (where the layout manager provides the same features across all platforms; implemented in Haskell at least for those platforms that don't have a native layout manager) 3) creation of widgets from platform-specifc resource files .. using platform-specific extensions Something like: nib <- loadAppleNibFile_MAC "foo.nib" -- a nib file is a document created by Apple's Interface Builder and contains one or more dialogs dialog <- createDialogFromNib_MAC nib "MyDialog" If "MyDialog" doesn't refer to a dialog in the file, a runtime error occurs... (_MAC indicates a Mac-specific extension, in analogy to the naming convention for OpenGL extensions). 4.) automatic layout using a self-made resource file format ... and perhaps, in the far future, a GUI layout editor to go along with it 5.) some conversion of platform-specific formats to Haskell code (frankly, I fail to see the point) ===== What about making layout management a feature of a containter widget? When adding widgets to a FixedLayoutContainer, you'd have to specify the coordinates, when adding widgets to a GridLayoutContainer, you'd specify row & column, etc. We could start with just a FixedLayoutContainer (that's easy), and then we can go on and add "more intelligent" layout mechanisms. Another, similar approach would be to make the "layout manager" an _attribute_ of a container widget (as is done in Java's AWT, if I remember correctly). Don't know if that has any advantages for our situation. Cheers, Wolfgang

Wolfgang Thaller wrote:
So allow me to try to summarize the discussion about dynamic layout:
We'll have one or more of the following (correct me if I left something out, or if I'm misrepresenting something):
For Xt (Athena, Motif), you have a choice of: 1. Programmatic creation of widgets with programmatic specification of layout attributes. 2. Programmatic creation of widgets with layout attributes taken from X resources. For Motif, you have some additional choices: 3. Creation of widgets from UID (compiled UIL) files, with layout attributes specified in the UIL/UID files. 4. Creation of widgets from UID (compiled UIL) files, with layout attributes taken from X resources. In all four cases, the set of available layout attributes depends upon the widget's parent (container). Some (e.g. XmRowColumn) have minimal or no layout attributes; others (e.g. XmForm) have extensive layout attributes. For XmForm, absolute positioning is just one possible option (effectively, the top/left attachments are to the edges of the parent, with offsets specified in pixels).
Something like: nib <- loadAppleNibFile_MAC "foo.nib" -- a nib file is a document created by Apple's Interface Builder and contains one or more dialogs dialog <- createDialogFromNib_MAC nib "MyDialog" If "MyDialog" doesn't refer to a dialog in the file, a runtime error occurs...
For UIL, you also have to provide definitions for any attribute values which can't be specified inside a UIL file (e.g. pointers, callbacks). Basically, the UIL file uses symbolic names, and you provide a list of name/value pairs to map the names to specific values when loading the file.
What about making layout management a feature of a containter widget? When adding widgets to a FixedLayoutContainer, you'd have to specify the coordinates, when adding widgets to a GridLayoutContainer, you'd specify row & column, etc. We could start with just a FixedLayoutContainer (that's easy), and then we can go on and add "more intelligent" layout mechanisms.
Specifying fixed coordinates is likely to violate native look-and-feel (you have no idea what the default font is), and will break down if the application is customised (e.g. changing labels). Allowing fixed-coordinate layouts is an open invitation for people to write code which only works correctly on their system; they will just tweak the numbers until it looks correct with their preferences.
Another, similar approach would be to make the "layout manager" an _attribute_ of a container widget (as is done in Java's AWT, if I remember correctly). Don't know if that has any advantages for our situation.
Java's AWT is a great way to write programs which are instantly
recognisable as "Java AWT" programs (i.e. instantly distinguishable
from "native" programs).
If you want native l&f, you have to redo the layout for each platform.
If you try to impose a portable solution, you'll end up with something
that doesn't have native l&f on anything. Even if all of the common
platforms provided exactly the same layout interface, unless they also
had identical style guides, you would need to re-implement the layout
for each platform.
The easiest way to get both portability and native l&f is to use
external interface definitions. The interface definitions themselves
aren't remotely portable, but at least it keeps the non-portability
out of the actual code.
Of course, people may ultimately decide that native l&f is just too
much work, and abandon it. It's certainly an option. But, if native
l&f is abandoned, it should be a conscious decision, rather than a
accidental consequence of not realising what's involved until too
late. Also, it makes it less likely that the end result will actually
be the one-and-only UI library; if Haskell is around long enough,
eventually someone may want native l&f badly enough to implement a
library which provides it.
--
Glynn Clements

Glynn Clements wrote:
Allowing fixed-coordinate layouts is an open invitation for people to write code which only works correctly on their system; they will just tweak the numbers until it looks correct with their preferences.
I agree that the use of fixed placement should be strongly discouraged. I would want the feature to be in the CGA anyway, mainly for two reasons: a) as a base to implement automatic layout on, at least for platforms that don't provide their own automatic layout features (Mac OS, Windows) b) because it is really easy to implement - it would be very useful for testing in the early phases of CGA development.
The easiest way to get both portability and native l&f is to use external interface definitions. The interface definitions themselves aren't remotely portable, but at least it keeps the non-portability out of the actual code.
I actually agree, but I think it shouldn't be the only way. Personally, I'd be happy to use Apple's Interface Builder to draw the interface of my Haskell programs. However, I'd sometimes prefer to have a slightly sub-optimal Mac version (interface created using automatic layout algorithms from the Mac backend of the CGA) than not to have a Mac version at all, which would mean I'd have to use the GTK or Motif versions via [local] X11 on my Mac (--> even less native look&feel). Then, there's the issue of dynamically-generated interfaces - we need programmatical automatic placement in that case.
For UIL, you also have to provide definitions for any attribute values which can't be specified inside a UIL file (e.g. pointers, callbacks). Basically, the UIL file uses symbolic names, and you provide a list of name/value pairs to map the names to specific values when loading the file.
Which brings us to another problem; the mechanisms for interfacing interface files with the rest of the program are vastly different: Motif/UIL/UID: (just what I heard on this list, I really no nothing) Each widget can be identified by its name. Callbacks referenced from the interface file are specified using a name. When loading the interface, a name/value mapping has to be provided. Other data (examples please... images?) may be provided in the same way Windows Resource files: Each widget can be identified by its numeric ID. You can't define separate callbacks for different widgets; instead, you define one for the entire dialog, which gets passed, which can take decisions based on the clicked button's ID. Only fixed layout is supported, no resizing. Resizable windows like document windows are usually created programmatically. Macintosh Carbon (nib files): Each widget ("control") inside a window/dialog box has an ID that consists of a four-character-code and a numeric ID. For each control, you can also set a four-character "command ID" that gets sent to the callback when e.g. a button is pressed. Only fixed coordinates are supported, no resizing. Resizable windows like document windows are usually created programmatically. Macintosh Cocoa (nib files): You usually don't identify individual widgets from the outside. Instead, you connect an objects "outlet" to the widget. When the nib file is loaded, the object is instantiated dynamically and a member variable is set to point to the widget. Only fixed coordinates are supported, but you can specify how the widget should react if its container is resized (stretch, stay on left, stay on right, stay in center etc...). Document windows are created from nib files, too (not programmatically). I currently don't see how those differences can be reconciled without a) requiring platform-specific code b) sacrificing features or c) both of the above :-( None of the approaches need to be mutually exclusive, though: 1) Explicit placement (should not be used directly except in some strange case that I just can't think of) 2) Platform-specific interface definition files 3) Dynamic layout For 3), I'm thinking of a high-level approach. Of course, there'd be the usual grid-based mechanisms (cf Java's GridBagLayout), but I'm also thinking of specialized layouts for special situations - like a "button bar" in a dialog box, where you just tell the layout manager which button is the OK button and which is the Cancel button and so on. I'd implement this in Haskell, on top of 1); I'd tweak the algorithms separately for each platform. For platforms with good support for dynamic layout, we might be able to do it on top of "native" features.
If you want native l&f, you have to redo the layout for each platform. If you try to impose a portable solution, you'll end up with something that doesn't have native l&f on anything. Even if all of the common platforms provided exactly the same layout interface, unless they also had identical style guides, you would need to re-implement the layout for each platform.
So let's not impose anything - but we might still want to provide it. Most style guide differences can probably be resolved by the right kind of abstraction (e.g. button ordering, sizing & spacing). Maybe I'd develop my programs on the Mac, and then move them over to Motif and only manually redo the layout where it needs fixing. I'm sure that it's possible to get "perfect" platform-independent layout for simple forms... (If it turns out to be absolutely impossible to create results that we can be happy with, we can still leave it out...) Cheers, Wolfgang

On Sat, Apr 12, 2003 at 06:54:13PM +0200, Wolfgang Thaller wrote:
None of the approaches need to be mutually exclusive, though:
1) Explicit placement (should not be used directly except in some strange case that I just can't think of) 2) Platform-specific interface definition files 3) Dynamic layout
I don't like the idea of separating explicit placement and dynamic layout. If we provide a set of simple combinators, these combinators can calculate fixed positions for Windows and Aqua but use the widget containers which allow resizing on Gtk and Motif. It is indeed possible to place widgets at fixed positions in Gtk but it is as difficult (or impossible as it is in pixels rather in font-specific dialog units as in Windows) as for all other platforms to get right. Using combinators doesn't go along with absolute pixel positions anyway as far as I can tell. I therefore opose to taking "explicit placement" as a starting point. Axel.

Axel Simon wrote:
On Sat, Apr 12, 2003 at 06:54:13PM +0200, Wolfgang Thaller wrote:
None of the approaches need to be mutually exclusive, though:
1) Explicit placement (should not be used directly except in some strange case that I just can't think of) 2) Platform-specific interface definition files 3) Dynamic layout
I don't like the idea of separating explicit placement and dynamic layout.
Explicit placement is, of course, just one very simple layout algorithm. I was not talking about separating it from the rest. * Explicit placement is a layout algorithm, just like a GridBagLayout * It is available on all platforms * It is easy to implement on all platforms * It is the only thing that is available on some platforms, so on those platforms, we'll have to implement the other algorithms on top of that * Because it's easy to implement on all platforms, it will be useful in the early testing phases * Because we need it as a base to implement better algorithms on Windows & Aqua, we might as well have a convenient interface to it * It's use for laying out dialogs should, of course, be discouraged, for the sake of cross-platform compatibility. * I'm sure that there are some strange situations where it is necessary, and if the application can query things like minimum and recommended sizes, font sizes are no problem
If we provide a set of simple combinators, these combinators can calculate fixed positions for Windows and Aqua but use the widget containers which allow resizing on Gtk and Motif.
Resizing is a different issue. Mac OS Cocoa only has explicit placement, but does automatic resizing. Also, consider a document window: You'd need to place some content-area widget (e.g. a text widget), and two scrollbar widgets. Of course we need resizing, even for windows. As I said, there is no mutual exclusion. We should probably have good layout combinators, but we should also have an "explicit placement" combinator because I need it as a base to implement the other combinators on on Mac OS; also, it's a useful thing to have in some situations.
Using combinators doesn't go along with absolute pixel positions anyway as far as I can tell.
I don't get that... what do you mean by this?
I therefore opose to taking "explicit placement" as a starting point.
I was suggesting that we should start with explicit placement because it is by far the simplest mechanism; we can then already implement and test other parts of the CGA, before all the dynamic layout mechanisms are implemented on Mac OS and Windows. Cheers, Wolfgang

On Sun, Apr 13, 2003 at 05:12:56PM +0200, Wolfgang Thaller wrote:
I don't like the idea of separating explicit placement and dynamic layout.
[..]
* I'm sure that there are some strange situations where it is necessary, and if the application can query things like minimum and recommended sizes, font sizes are no problem
I just assumed that we explicit placement is something nobody will ever use, so it just seemed odd that we should start with it.
Resizing is a different issue. Mac OS Cocoa only has explicit placement, but does automatic resizing. Also, consider a document window: You'd need to place some content-area widget (e.g. a text widget), and two scrollbar widgets. Of course we need resizing, even for windows.
Sorry, I only meant to say resizing in terms of dialogs. The normal document window certainly is resizable.
I therefore opose to taking "explicit placement" as a starting point.
I was suggesting that we should start with explicit placement because it is by far the simplest mechanism; we can then already implement and test other parts of the CGA, before all the dynamic layout mechanisms are implemented on Mac OS and Windows.
How would it look like? placeWidget :: Container -> Int -> Int -> Widget -> IO () What are the two Ints? Pixels? Dialog units? Fractions of something? I just wonder if it is really simple. Convince me that it is! :-)
Using combinators doesn't go along with absolute pixel positions anyway as far as I can tell.
I don't get that... what do you mean by this?
I meant that placing a widget at a specific position will be just an IO action like "placeWidget" above. This cannot be expressed by combinators, otherwise you already have the dynamic version. Axel.

Axel Simon
How would it look like?
placeWidget :: Container -> Int -> Int -> Widget -> IO ()
What are the two Ints? Pixels? Dialog units? Fractions of something? I just wonder if it is really simple. Convince me that it is! :-)
Note that this is a problem that will have to be resolved no matter what placement mechanism is used. For example, when resizing a dialog box, we might want to specify a minimum horizontal and vertical distance that will be maintained between the objects inside the box and we will have to decide whether it is relative to font size, screen resolution, etc. As far as I can see, the only thing different about absolute placement is that the issue is more visible because that's all we have to worry about but it will still be an issue for every alternate mechanism (with the exception of interface builders, I guess). Two obvious answers to your question are: 1) Pixels. (Easy to implement but hard to create portable systems) 2) All your suggestions and more: data Unit = -- raw pixels Pixel Int | -- millimetres MM Int | -- Printer's point size Point Int | -- fraction of some property of a font -- (but _which_ property??? width, height, ascent, descent?) FontFraction FontID Double | -- some property of some random GUI object -- where the proprty is extracted by the function argument GUIObject a => GUIFraction a (a -> Double) Double | ... | -- we can even put a little expression language in here... Plus Unit Unit | ... I should say that this isn't intended as a complete or fully worked out suggestion but, rather, as a starting point for further exploration with the hope of finding something which is useful both of absolute positioning and for many of the alternatives we want to cover. In particular, if we went down this road, I think we'd want to use constructor functions (i.e., pixel, not Pixel) in the API to make it easier to add new ways of specifying sizes in the future. -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/

Wolfgang Thaller wrote:
For UIL, you also have to provide definitions for any attribute values which can't be specified inside a UIL file (e.g. pointers, callbacks). Basically, the UIL file uses symbolic names, and you provide a list of name/value pairs to map the names to specific values when loading the file.
Which brings us to another problem; the mechanisms for interfacing interface files with the rest of the program are vastly different:
Motif/UIL/UID: (just what I heard on this list, I really no nothing) Each widget can be identified by its name. Callbacks referenced from the interface file are specified using a name. When loading the interface, a name/value mapping has to be provided. Other data (examples please... images?) may be provided in the same way
Essentially UIL has the same problem as X resources: all values are strings, so you can only specify values for attribute types which can be converted from strings. Strings (e.g. for labels), fonts, colours, numeric and enumerated types aren't a problem; the problem comes when you want to set an attribute whose value is a pointer, XID (basically a handle) etc. Images (both Pixmap and XImage types) suffer from a related problem. Specifically, you can't easily specify a value for an image attribute (e.g. XmNbackgroundPixmap) at widget creation time; the reason is that any images have to be compatible with the visual used for the widget's window, but the widget hasn't been realised (its window hasn't been created) at that point, so you can't easily discover the correct visual. Some widget sets (e.g. some of the Athena variants) do allow you to specify images at widget creation time; typically, they assume that the widget's window will use the default visual. This works fine on a typical Linux box, where the display only has one visual, but falls down on more complex hardware (e.g. SGIs) where multiple visuals are available (the program dies with a BadMatch error as soon as the window is realised).
I currently don't see how those differences can be reconciled without a) requiring platform-specific code b) sacrificing features or c) both of the above :-(
Neither do I. Which is why I'm so keen to see an API which separates the initialisation from the core application logic. The application logic needs to be provided with handles to any widgets which it needs to query or manipulate, and it needs an interface through which to do so. That's all. The code doesn't need to know anything about how those widgets were laid out; or even exactly what types of widget its dealing with, so long as it can perform the necessary operations. Real-world applications seldom achieve portability by using only portable libraries (e.g. OpenGL + GLUT). It's far more common to write portable code where possible, and accept that some (hopefully small) portion of the code will need to be re-written for each platform. To me, the most important part to get right initially is the stuff which interfaces with the application logic, because that tends to be scattered throughout the code. Primarily state management; not just the generic set/get interface, but the semantics of common widget types (e.g. how do you specify a scrollbar position?). Although it seems counter-intuitive, the mechanics of UI construction aren't so important, as it's relatively straightforward to isolate that to a separate file, and the code tends to be highly idiomatic; the differences between a typical BuildTheUI() function (which is code) and e.g. UIL (which is data) tend to be more syntactic than structural.
For 3), I'm thinking of a high-level approach. Of course, there'd be the usual grid-based mechanisms (cf Java's GridBagLayout), but I'm also thinking of specialized layouts for special situations - like a "button bar" in a dialog box, where you just tell the layout manager which button is the OK button and which is the Cancel button and so on.
That's one case you probably don't need to handle. OK/Cancel dialogs
should be available as complete components; if they are available,
they should be used.
--
Glynn Clements

On Sat, Apr 12, 2003 at 04:25:35PM +0100, Glynn Clements wrote:
Something like: nib <- loadAppleNibFile_MAC "foo.nib" -- a nib file is a document created by Apple's Interface Builder and contains one or more dialogs dialog <- createDialogFromNib_MAC nib "MyDialog" If "MyDialog" doesn't refer to a dialog in the file, a runtime error occurs...
For UIL, you also have to provide definitions for any attribute values which can't be specified inside a UIL file (e.g. pointers, callbacks).
...so you're saying you cannot connect to widgets in the UIL files if you haven't specified a correspoinding attribute for the callback? We have to find some common ground for resource files and I doubt that Apple's NIB and Windows resource files can contain callback attributes. Is it not possible to retrieve a widget from the resource file and then add a callback to it?
Allowing fixed-coordinate layouts is an open invitation for people to write code which only works correctly on their system; they will just tweak the numbers until it looks correct with their preferences. I agree with that. On the other hand it would be nice to provide some simple layout mechanism which people can use for prototyping or in case they don't care. It would be brilliant if this layout could then later be overridden by resource files.
Maybe we could stead the algebraic data description of the layout structure from ObjectIO: dia <- createDialog "ApplicationClose" (...the layout in terms of Haskell code...) case dia of (Dia d (VerCon [_,HorCon [buOk,buCancel]])) -> do onActivated buOk quitMain onActivated buOk (destroy d) And if the user bothered to supply an external resource file, the layout of the "ApplicationClose" dialog would come from there.
Of course, people may ultimately decide that native l&f is just too much work, and abandon it. It's certainly an option. But, if native l&f is abandoned, it should be a conscious decision, rather than a accidental consequence of not realising what's involved until too late. Also, it makes it less likely that the end result will actually be the one-and-only UI library; if Haskell is around long enough, eventually someone may want native l&f badly enough to implement a library which provides it. Well, if it is possible, we should aim at having native l&f right now.
5.) some conversion of platform-specific formats to Haskell code (frankly, I fail to see the point) Was that my point? Well I think it might be important for commercial
Wolfgang wrote: products to hide the user interface in the code, the application might be quicker to start and last but not least every resource is resolved statically at compile time. But maybe this can be up to each individual toolkit. Axel.

Axel Simon wrote:
Something like: nib <- loadAppleNibFile_MAC "foo.nib" -- a nib file is a document created by Apple's Interface Builder and contains one or more dialogs dialog <- createDialogFromNib_MAC nib "MyDialog" If "MyDialog" doesn't refer to a dialog in the file, a runtime error occurs...
For UIL, you also have to provide definitions for any attribute values which can't be specified inside a UIL file (e.g. pointers, callbacks).
...so you're saying you cannot connect to widgets in the UIL files if you haven't specified a correspoinding attribute for the callback? We have to find some common ground for resource files and I doubt that Apple's NIB and Windows resource files can contain callback attributes. Is it not possible to retrieve a widget from the resource file and then add a callback to it?
Yes, it's possible, but it's more common to specify a callback in the
UIL file via a symbolic name, and provide a name/value mapping when
you load the UID file, so the widget hierarchy is returned with the
callbacks already installed. You only need to programmatically add
callbacks if they come and go during execution.
The purpose of the name/value mapping is for providing values which
can't be stored in UIL/UID files; the most common cases are pointers
(primarily function pointers) and "handles". Most common attribute
types (numbers, colours, fonts, booleans, enumerated types) *can* be
specified as strings in the UIL file.
--
Glynn Clements

On Sun, Apr 06, 2003 at 09:37:41PM -0700, David Sankel wrote:
--- Glynn Clements
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.

--- Axel Simon
- Callbacks get separate functions which called on<WidgetName><ActionName> I agree with this. Ok, no problem.
how About doOn<ActionName/>
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.
Seems easy enough, but it isn't. Lets assume we can use multiparameter type classes (which is a ghc extention of haskell). So we have: data Attr w a = Attr (w -> IO a) (w -> a -> IO ()) data AttrRO w a = AttrRO (w -> IO a) class Attr_Readable s w a where getter :: s -> (w -> IO a) instance Attr_Readable (Attr w a) w a where getter (Attr p _) = p instance Attr_Readable (AttrRO w a) w a where getter (AttrRO p) = p get :: Attr_Readable p w a => w -> p -> IO a get w readable = (getter readable) w now say we have something like: position :: Attr A_Button (Int,Int) position = newRWAttr (\w -> do a <- button_memfun_x (b2A w) b <- button_memfun_y (b2A w) return (a,b) ) (\w (a,b) -> button_memfun_position (b2A w) a b ) And then in our main do function, we see p <- get hello size But it doesn't work! I got this error: est_midlevel.hs:20: No instance for (Attributes.Attr_Readable (Attributes.Attr A_Button (Int, Int)) A_Button a) arising from use of `get' at Test_midlevel.hs:20 This is because the return value using those multiparameter classes is ambigious. So we are forcing the users to do something like get hello size :: IO (Int,Int) , which sucks. So I suggest using the getSize type RO accessors and simply inform the user. WO variables = constructor/member functions (for callbacks) RW variables = attributes RO variables = member functions David J. Sankel

On Tue, 8 Apr 2003 10:49:46 +0100
Axel Simon
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.
You can also record the label's value, in algebraic form, in a mutable variable wich stays in the haskell land. Of course, if the toolkit itself can change the label, this makes few sense, and OTOH, it might be not so bad if you parse back the text: if the widget is not a label but a textbox, it's perfectly legitimate that the user cuts and pastes formatted text, and you have to read that back. Perhaps in this particular case it's easier to say: that is not markup, it's a string. A successive layer will take care of markup and similar issues. Vincenzo

Vincenzo Ciancia wrote:
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.
You can also record the label's value, in algebraic form, in a mutable variable wich stays in the haskell land. Of course, if the toolkit itself can change the label, this makes few sense, and OTOH, it might be not so bad if you parse back the text: if the widget is not a label but a textbox, it's perfectly legitimate that the user cuts and pastes formatted text, and you have to read that back.
Perhaps in this particular case it's easier to say: that is not markup, it's a string. A successive layer will take care of markup and similar issues.
What if you need to programmatically set a button's label to the
literal string "<red>" in a portable manner?
I suspect that similar issues will arise regarding I18N (do we discuss
those on the GUI list or the I18N list?). One toolkit may dictate that
everything is UTF-8 encoded. Motif uses compound text (basically
ISO-2022 encoding, with some restrictions). Athena just treats the
data as an octet stream, and it's up to the programmer (or user) to
ensure that the font uses the same encoding as the data.
It gets even more messy when you start dealing with cut/paste between
applications using different toolkits and/or different encodings.
--
Glynn Clements

On Tue, Apr 08, 2003 at 09:40:11PM +0100, Glynn Clements wrote:
I suspect that similar issues will arise regarding I18N (do we discuss those on the GUI list or the I18N list?). One toolkit may dictate that everything is UTF-8 encoded. Motif uses compound text (basically ISO-2022 encoding, with some restrictions). Athena just treats the data as an octet stream, and it's up to the programmer (or user) to ensure that the font uses the same encoding as the data.
As with the concurrency issue, this should be as transparent to the Haskell programmer as possible. Haskell uses Unicode characters, converting to whatever the toolkit uses is mandatory IMHO. Axel.

Glynn Clements
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.
If there is an alternative design which would allow errors to be reported statically (e.g., by using the typesystem or by not providing a 'set' operation for the offending attribute or whatever), then I think it would be significantly better. One of Haskell's huge strengths is that static error detection catches so many errors that would otherwise make it through to be caught by (dynamic) testing or included in released systems. -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/
participants (7)
-
Alastair Reid
-
Axel Simon
-
David Sankel
-
Glynn Clements
-
John Meacham
-
Vincenzo Ciancia
-
Wolfgang Thaller