
It seems that the discussion about how to represent widget attributes has died down again recently. I'll try to get it going again... I'll try to list a few very basic widgets and their most important attributes. I'm using Apple's terms, I don't always know the "proper" Windows, Motif or GTK terms. Push Button Read/Write attributes: Label (String), Enabled (Bool) Callbacks: onClick Check Boxes and Radio Buttons Read/Write attributes: Label (String), Enabled (Bool), Value (Bool) Callbacks: onClick Static Text (a.k.a. Labels) Read/Write attributes: Label (String), Enabled (Bool; yes, at least on Mac OS, static text can be greyed out) Scroll Bar: Read/Write attributes: Value, minimum, maximum, size of visible part, step sizes etc... Callbacks: onScrollStart onScroll onScrollFinished (or something like that) So far, I've only listed plain read/write attributes, and callbacks (I'm in favour of allowing multiple callbacks). We could add a whole lot of read-only attributes to each widget: current size, minimum size, maximum size, (platform-specific) native handle, etc. Were there any examples of write-only attributes, that can always be written, but never be read? What attributes are there that cannot be changed after creation or that have no sensible default? (Yes, I do consider "" to be a sensible default for a button label) On Widget Names: Then there is the Xt-style widget name, which would need to be specified on widget creation, and which would then be read-only. Is it really necessary to have this on all platforms? I gather it is used for looking up customization from X resources - does this make sense if the widgets have been created programmatically using a cross-platform interface? People targeting other platforms first wouldn't know what to use the name for, and why it's a good idea to specify a proper name. If the interface is loaded from an UIL file, then of course there would be a Xt widget name, but do we need it even in programs that don't care about UIL files and X resources? On linked states: a) I think that linked states and general state-change notification is outside the scope of the CGA. b) Allowing linked states would require every state change to go through the CGA; it would be more difficult to map to the underlying backend library, in case the backend library changes some state "on it's own". Cheers, Wolfgang

On Sun, 13 Apr 2003 23:11:51 +0200
Wolfgang Thaller
On Widget Names: Then there is the Xt-style widget name, which would need to be specified on widget creation, and which would then be read-only. Is it really necessary to have this on all platforms? I gather it is used for looking up customization from X resources - does this make sense if the widgets have been created programmatically using a cross-platform interface?
It does, even if you don't load UIL files, the names of the widgets can be used for customization by the user. The user should not know if the application uses or not an UIL, he just knows that with xeditres (s)he can find and change the properties. I wonder if a good automatic naming is possible. Maybe Glynn Clements knows by experience that it's not a good idea.
People targeting other platforms first wouldn't know what to use the name for, and why it's a good idea to specify a proper name.
This is a very good argument in favour of automatic naming. Naming given by an user wich is not aware of their use would be worse than an automatic one. Or at least automatic naming should be an option for the user.
On linked states: a) I think that linked states and general state-change notification is outside the scope of the CGA.
Yes, but if you don't take the right measures, state-change notification can be used in CGA by who cares only by simulation. Consider a library, written in CGA but without state-change notification. Nobody can compell it to notify its state changes without changing the source code of the library, wich is a bad idea since this kind of state handling seems very general and useful in GUI programs.
b) Allowing linked states would require every state change to go through the CGA; it would be more difficult to map to the underlying backend library, in case the backend library changes some state "on it's own".
I don't understand this point, can you give an example? It seems to me that you are not going to watch states that don't interest you, and that states that you don't watch are not going through the CGA. Vincenzo

Vincenzo Ciancia wrote:
I wonder if a good automatic naming is possible. Maybe Glynn Clements knows by experience that it's not a good idea.
It isn't. wxMotif just names each widget after its type (e.g. all push buttons are called "button"), which makes it impossible to refer to individual widgets (Xt doesn't force siblings to have distinct names). This is the kind of thing that happens if you design a generic API without considering the specifics of individual toolkits first, and is the kind of thing that I'd like to help prevent. Also, auto-generated names wouldn't be much use for parts of the UI which have dynamic aspects (e.g. if a dialog sometimes has a "help" button and sometimes doesn't, any subsequent buttons would have varying names). And even if the UI is static, "button3" has far less mnemonic value than "apply".
People targeting other platforms first wouldn't know what to use the name for, and why it's a good idea to specify a proper name.
This is a very good argument in favour of automatic naming. Naming given by an user wich is not aware of their use would be worse than an automatic one. Or at least automatic naming should be an option for the user.
It's an argument in favour of documenting *why* widgets have names.
Also, if any examples are provided, it should be immediately apparent.
In any case, the programmer will often have to think up a name for the
variable which holds the widget's handle. The variable name is likely
to be suitable as the widget name. Ultimately, both serve the same
purpose: a means to refer to the widget.
--
Glynn Clements

Wolfgang Thaller wrote:
It seems that the discussion about how to represent widget attributes has died down again recently. I'll try to get it going again...
I'll try to list a few very basic widgets and their most important attributes. I'm using Apple's terms, I don't always know the "proper" Windows, Motif or GTK terms.
Push Button Read/Write attributes: Label (String), Enabled (Bool)
The label is more style than state; it seldom needs to be set programmatically; text output (e.g. status indicators) should use a read-only text widget.
Callbacks: onClick
"onActivate"; "onClick" implies use of a mouse. If the callback is passed the event which triggered it (which is normally the case for Xt callbacks), it's worth reminding the programmer that it won't necessarily be a mouse event.
Check Boxes and Radio Buttons Read/Write attributes: Label (String), Enabled (Bool), Value (Bool) Callbacks: onClick
Static Text (a.k.a. Labels) Read/Write attributes: Label (String), Enabled (Bool; yes, at least on Mac OS, static text can be greyed out)
The comments for push buttons apply to both of the above.
Scroll Bar: Read/Write attributes: Value, minimum, maximum, size of visible part, step sizes etc... Callbacks: onScrollStart onScroll onScrollFinished (or something like that)
Motif has separate callbacks for each type of motion: decrementCallback incrementCallback pageDecrementCallback pageIncrementCallback toBottomCallback toTopCallback dragCallback valueChangedCallback If one of the first six callback lists are empty, valueChangedCallback is used in its place. Also, Motif has "scale" widgets (Windows has something similar), which should be used for generic value input; scroll bars are specifically for scrolling.
What attributes are there that cannot be changed after creation or that have no sensible default? (Yes, I do consider "" to be a sensible default for a button label)
For all Xt widgets: colormap depth screen For all Motif widgets: layoutDirection stringDirection Scrollbar: orientation Also, XmRowColumn has a lot of attributes which can only be set at creation time; it also has a lot of pseudo-attributes related to menus which can be set at creation time but can't subsequently be written or read.
On Widget Names: Then there is the Xt-style widget name, which would need to be specified on widget creation, and which would then be read-only. Is it really necessary to have this on all platforms? I gather it is used for looking up customization from X resources - does this make sense if the widgets have been created programmatically using a cross-platform interface?
Yes.
People targeting other platforms first wouldn't know what to use the name for, and why it's a good idea to specify a proper name. If the interface is loaded from an UIL file, then of course there would be a Xt widget name, but do we need it even in programs that don't care about UIL files and X resources?
If you want code to work on all toolkits, you have to provide it. It
would also be necessary if we wanted to implement some of Xt's
features (e.g. customisation through external files, e.g. for I18N)
for other toolkits.
BTW, on Windows, dialog controls normally have "names", except that
they're integers rather than strings.
This also simplifies coding. The UI creation code doesn't need to
store lots of handles for later reference; code can obtain the
run-time handle using the widget's pathname.
--
Glynn Clements

Glynn Clements wrote:
The label is more style than state; it seldom needs to be set programmatically; text output (e.g. status indicators) should use a read-only text widget.
Of course. But I don't see why "style" attributes shouldn't be just "attributes", or why it should not be possible to modify them in the same way as the contents of a read-only text widget.
Callbacks: onClick
"onActivate"; "onClick" implies use of a mouse.
OK.
Scroll Bar: Read/Write attributes: Value, minimum, maximum, size of visible part, step sizes etc... Callbacks: onScrollStart onScroll onScrollFinished (or something like that)
Motif has separate callbacks for each type of motion:
decrementCallback incrementCallback pageDecrementCallback pageIncrementCallback toBottomCallback toTopCallback dragCallback valueChangedCallback
If one of the first six callback lists are empty, valueChangedCallback is used in its place.
No problem here, I think. I was trying not to impose my own view of a scroll bar, and to play nice with toolkits that insist on providing default behaviour for scroll bars. If all toolkits allow this kind of detail (Mac OS does), then no problem. One small note though: I would like to be able to tell if we're still tracking the mouse or if we are finished (in case I want a fast low-quality preview while scrolling). Can that be achieved on Motif?
Also, Motif has "scale" widgets (Windows has something similar), which should be used for generic value input; scroll bars are specifically for scrolling.
Same thing everywhere... Mac OS has "sliders".
For all Xt widgets: colormap depth screen
Screen? The one that the app is running on; if you want to access separate X displays from one app, that would probably need a platform-specific extension anyway. Depth & Colormap?? I definitely do not want to deal with that strange X visual and colormap business when I'm writing programs for Mac OS and Windows... also, I remember writing small Gtk programs without ever thinking about specifying such attributes. What should we specify here, and is it really absolutely positively impossible to automate it?
For all Motif widgets: layoutDirection stringDirection
Looks like an internationalization issue... a useful default would be the direction of the application's language. Normally, I'd expect the "text direction" of a button label to depend on the (language of the) button label.
Scrollbar: orientation
Of course. Nobody will mind passing a boolean to the constructor in this case...
Also, XmRowColumn has a lot of attributes which can only be set at creation time; it also has a lot of pseudo-attributes related to menus which can be set at creation time but can't subsequently be written or read.
Well, I doubt we will be using XmRowColumn exactly as-is for cross-platform programmatic layout of widgets. The various layout mechanisms deservere their own discussion... Cheers, Wolfgang

Wolfgang Thaller wrote:
The label is more style than state; it seldom needs to be set programmatically; text output (e.g. status indicators) should use a read-only text widget.
Of course. But I don't see why "style" attributes shouldn't be just "attributes", or why it should not be possible to modify them in the same way as the contents of a read-only text widget.
Oh, the label is an attribute. The point I'm making is that it's part of the widget's style, rather than it's state, so it should be discussed along with the other style attributes (font, colour, etc), rather than with state (toggle state, scrollbar position, text-field contents etc). State is something that the API has to provide an interface for at an early stage. You can't write useful UI code without being able to read/write state, and any changes to the interface would require a substantial re-write of application code. OTOH, the style issues can largely be deferred; in some cases (i.e. Xt), the code needn't deal with style attributes at all. In any case, application code shouldn't be attempting to modify labels. I'm not sure that the API should even support it; doing so is an invitation to write applications which can't easily be localised (bear in mind that, with Xt, anyone can localise an application simply by editing the resource file; this will break if the application programmatically changes the label to a hard-coded string).
Scroll Bar: Read/Write attributes: Value, minimum, maximum, size of visible part, step sizes etc... Callbacks: onScrollStart onScroll onScrollFinished (or something like that)
Motif has separate callbacks for each type of motion:
decrementCallback incrementCallback pageDecrementCallback pageIncrementCallback toBottomCallback toTopCallback dragCallback valueChangedCallback
If one of the first six callback lists are empty, valueChangedCallback is used in its place.
No problem here, I think. I was trying not to impose my own view of a scroll bar, and to play nice with toolkits that insist on providing default behaviour for scroll bars. If all toolkits allow this kind of detail (Mac OS does), then no problem. One small note though: I would like to be able to tell if we're still tracking the mouse or if we are finished (in case I want a fast low-quality preview while scrolling). Can that be achieved on Motif?
dragCallback is called during dragging; valueChangedCallback is called when the thumb is released.
For all Xt widgets: colormap depth screen
Screen? The one that the app is running on; if you want to access separate X displays from one app, that would probably need a platform-specific extension anyway.
Note: Display and Screen are different things. A display corresponds to an X server instance. A screen basically corresponds to a monitor; on a multi-headed system, you would normally have one screen per monitor (unless you use e.g. Xinerama to split a single screen across multiple monitors). Screens may have different bit depths and/or different visuals (e.g. you could have a colour screen and a grey-scale screen).
Depth & Colormap?? I definitely do not want to deal with that strange X visual and colormap business when I'm writing programs for Mac OS and Windows... also, I remember writing small Gtk programs without ever thinking about specifying such attributes. What should we specify here, and is it really absolutely positively impossible to automate it?
No, all of those resources have defaults; typically, the default screen and default visual determine everything. For the most part, it's possible to hide display format issues from the user. However, they have to be dealt with somewhere (i.e. the back-end). E.g. most of the underlying X functions use colour indices; obtaining a colour index for a specific RGB value is an explicit operation. Also, server-side resources such as Pixmaps and GCs are tied to a specific screen, so you can't create a Pixmap once and use it everywhere. It may be necessary to reflect some of this detail into the API, primarily due to efficiency considerations; repeatedly converting fixed data from a "portable" format is undesirable.
Also, XmRowColumn has a lot of attributes which can only be set at creation time; it also has a lot of pseudo-attributes related to menus which can be set at creation time but can't subsequently be written or read.
Well, I doubt we will be using XmRowColumn exactly as-is for cross-platform programmatic layout of widgets. The various layout mechanisms deservere their own discussion...
That's why XmRowColumn has so many non-writable attributes and
pseudo-attributes. It tends to be used in fairly idiomatic ways; e.g.
menu bars and menus are actually XmRowColumn widgets, radio groups are
implemented using XmRowColumn widgets etc. There are 7 different
convenience functions for creating specific types of XmRowColumn
widget, each of which provides specific values for certain resources
(the ones which create menus also create the shell).
--
Glynn Clements

OTOH, the style issues can largely be deferred; in some cases (i.e. Xt), the code needn't deal with style attributes at all.
In any case, application code shouldn't be attempting to modify labels. I'm not sure that the API should even support it; doing so is an invitation to write applications which can't easily be localised
That "doing so is an invitation" attitude sounds dangerous. Every other toolkit out there supports changing style attributes of widgets at runtime, so why shouldn't we? I would just manage the "style" attributes and the "label" attributes using exactly the same syntax. We should just provide good alternative means. After all, for an application where we create the UI programmatically, we would want to do localization via our own string-table mechanism. If I wanted to localize just the Xt version of an application, I'd use X resources, but if I want to localize a program for three platforms, I'd want the application to just use it's own string table mechanism... I wouldn't want to have to localize it three times.
Note: Display and Screen are different things. A display corresponds to an X server instance. Ähh... Somewhere I read that the DISPLAY variable contains a number referring to the server and one referring to the screen... so :0.0 would be server 0, screen 0. Is it true, therefore, that $DISPLAY in fact refers to a single screen of a display?
A screen basically corresponds to a monitor; on a multi-headed system, you would normally have one screen per monitor (unless you use e.g. Xinerama to split a single screen across multiple monitors).
Seems like I've been taking something like Xinerama for granted since I first used a Mac with two monitors a little over ten years ago. How do you use a computer with multiple X screens? Is it possible to move a window from one screen to another? Do any programs support placing their windows on more than one screen? Can this reasonably be inside the scope of a cross-platform library? (We might still have platform-specific extensions for this, but that's none of my business).
Screens may have different bit depths and/or different visuals (e.g. you could have a colour screen and a grey-scale screen).
Just as on Mac OS. Only on Mac OS, I can have it Xinerama-style, with one window being half on an 8-bit-greyscale monitor and on a 15-bit color monitor... and then the user could go and change color depths, resolutions and the arrangement of the monitor on the fly. Therefore we can't attach any fixed color depth attributes to any widgets...
For the most part, it's possible to hide display format issues from the user.
I hope so.
It may be necessary to reflect some of this detail into the API, primarily due to efficiency considerations; repeatedly converting fixed data from a "portable" format is undesirable.
I see. Are you talking about manipulating images and perhaps about offscreen drawing? In that case, I agree that there are some problems there, but we can deal with that later. A few things that need working out are: a) What types and classes should be used to represent widgets in Haskell? b) How can we write those attribute getters/setters? c) How do we attach callbacks? I think we already had some results on that, but no vote... what disagreements were left? Cheers, Wolfgang

Wolfgang Thaller wrote:
After all, for an application where we create the UI programmatically, we would want to do localization via our own string-table mechanism. If I wanted to localize just the Xt version of an application, I'd use X resources, but if I want to localize a program for three platforms, I'd want the application to just use it's own string table mechanism... I wouldn't want to have to localize it three times.
While I can see the advantage of providing your own i18n mechanism (potentially less work), it would be a "second best", IMHO. For the Xt version, allowing the user to customise it via X resources is the "native" approach. In order for that to work, it's necessary that the application doesn't override the X resources programmatically.
Note: Display and Screen are different things. A display corresponds to an X server instance.
Ähh... Somewhere I read that the DISPLAY variable contains a number referring to the server and one referring to the screen... so :0.0 would be server 0, screen 0. Is it true, therefore, that $DISPLAY in fact refers to a single screen of a display?
The number after the period specifies the *default* screen, as returned by the DefaultScreen[OfDisplay] macros. It's possible to use a different screen when creating shell widgets (top-level windows); obviously, a shell's descendents have to be on the same screen as the shell.
A screen basically corresponds to a monitor; on a multi-headed system, you would normally have one screen per monitor (unless you use e.g. Xinerama to split a single screen across multiple monitors).
Seems like I've been taking something like Xinerama for granted since I first used a Mac with two monitors a little over ten years ago. How do you use a computer with multiple X screens? Is it possible to move a window from one screen to another?
No. The program would have to destroy the window and re-create it on the new screen.
Do any programs support placing their windows on more than one screen?
It's most common with graphics software, e.g. allowing a monochrome screen to be used for the UI to free up space on the colour screen for the graphics.
Can this reasonably be inside the scope of a cross-platform library? (We might still have platform-specific extensions for this, but that's none of my business).
Yes; a platform which has no concept of separate screens will only ever have screen 0 (i.e. like most of the boxes which run X).
It may be necessary to reflect some of this detail into the API, primarily due to efficiency considerations; repeatedly converting fixed data from a "portable" format is undesirable.
I see. Are you talking about manipulating images and perhaps about offscreen drawing? In that case, I agree that there are some problems there, but we can deal with that later.
Well, if you want to display a true-colour image, you would typically query the pixel format, generate an internal version which is compatible that format, then use the internal version thereafter. You wouldn't want to convert the image every time it was drawn. Similarly, a GC's foreground/background colours are colour indices; converting an RGB value to a colour index may involve searching the colour map to find the closest colour, or may require allocating a new entry in the colour map. You don't want to convert the same RGB colour to its corresponding index for every operation. It's possible that some of the details could be hidden by demand-conversion and caching, although that's potentially wasteful (internal representations could persist when they are no longer required).
A few things that need working out are:
a) What types and classes should be used to represent widgets in Haskell?
Even if you have specific types for specific widget classes, you
definitely need a generic Widget type, and any operation which could
reasonably be expected operate on any widget should.
--
Glynn Clements

While I can see the advantage of providing your own i18n mechanism (potentially less work), it would be a "second best", IMHO. For the Xt version, allowing the user to customise it via X resources is the "native" approach. In order for that to work, it's necessary that the application doesn't override the X resources programmatically.
But surely we shouldn't limit what applications can do. I have no problem with placing a note in the documentation that says "don't change labels programmatically, you'll prevent Xt users from customizing your App the way they like".
Do any programs support placing their windows on more than one screen?
It's most common with graphics software, e.g. allowing a monochrome screen to be used for the UI to free up space on the colour screen for the graphics.
Can this reasonably be inside the scope of a cross-platform library? (We might still have platform-specific extensions for this, but that's none of my business).
Yes; a platform which has no concept of separate screens will only ever have screen 0 (i.e. like most of the boxes which run X).
Well, it looks like platform-specific extension to me... You'd have myWindow <- window DocumentWindow [... attributes ...] ... which uses the default screen for Xt, and for Xt only, you'd have something along the lines of myWindow <- windowOnScreen_XT 2 DocumentWindow [... attributes ...] which, on X, will use screen 2. (We could still provide a stub implementation of this function for other platforms, to allow compilation everywhere...) The reason why I prefer to have that clearly marked as an extension is that I, for one, would have been thoroughly confused by a window creation function that expects me to specify a screen number. And I would especially have had problems with understanding the notion that my dual-monitor Mac only has one "screen".
It may be necessary to reflect some of this detail into the API, primarily due to efficiency considerations; repeatedly converting fixed data from a "portable" format is undesirable. [...] [...]
Okay, I see the problem. Discussion topic number one for the chapter "Images and Drawing" :-).
A few things that need working out are:
a) What types and classes should be used to represent widgets in Haskell?
Even if you have specific types for specific widget classes, you definitely need a generic Widget type, and any operation which could reasonably be expected operate on any widget should.
Probably. The question is, to what extent do we use type classes to achieve this, etc... I would advocate specifc types for specific widget classes (e.g. Button, ScrollBar, etc.), just because it's the Haskell Way ;-) . If we also have a general Widget type, we could have functions like fromWidget :: Widget a => Widget -> Maybe a and toWidget :: Widget a => a -> Widget That's all for now, Cheers, Wolfgang

Wolfgang Thaller wrote:
Can this reasonably be inside the scope of a cross-platform library? (We might still have platform-specific extensions for this, but that's none of my business).
Yes; a platform which has no concept of separate screens will only ever have screen 0 (i.e. like most of the boxes which run X).
Well, it looks like platform-specific extension to me...
You'd have
myWindow <- window DocumentWindow [... attributes ...]
... which uses the default screen for Xt, and for Xt only, you'd have something along the lines of
myWindow <- windowOnScreen_XT 2 DocumentWindow [... attributes ...]
No: myWindow <- window DocumentWindow [ screen =: theScreen, ... attributes ...] [Actually, you also have to force the visual and colourmap, but the point is that it's simply a matter of setting attributes. If you registered string-to-screen etc type converters, the user could choose the screen via resources.] In any case, I'm less concerned about providing a specific mechanism to allow the use of alternate screens, and more concerned about ensuring that the rest of the API doesn't fail badly if the windows aren't all on the same screen.
A few things that need working out are:
a) What types and classes should be used to represent widgets in Haskell?
Even if you have specific types for specific widget classes, you definitely need a generic Widget type, and any operation which could reasonably be expected operate on any widget should.
Probably. The question is, to what extent do we use type classes to achieve this, etc... I would advocate specifc types for specific widget classes (e.g. Button, ScrollBar, etc.), just because it's the Haskell Way ;-) .
Unfortunately, it's not the "typical GUI toolkit" way; at least, both Xt and Qt have an OO-style class hierarchy. While they differ on the exact hierarchy, both have a generic "Widget" class, with operations which apply to all widgets.
If we also have a general Widget type, we could have functions like fromWidget :: Widget a => Widget -> Maybe a and toWidget :: Widget a => a -> Widget
Having a generic Widget type only helps if there are functions which
actually operate upon it.
--
Glynn Clements

Wolfgang Thaller wrote:
[...] The question is, to what extent do we use type classes to achieve this, etc... I would advocate specifc types for specific widget classes (e.g. Button, ScrollBar, etc.), just because it's the Haskell Way ;-) .
Glynn Clements
Unfortunately, it's not the "typical GUI toolkit" way; at least, both Xt and Qt have an OO-style class hierarchy. While they differ on the exact hierarchy, both have a generic "Widget" class, with operations which apply to all widgets.
Since Haskell has a different type system from OO languages, we simply cannot do things the 'typical GUI toolkit' way. Instead, we have to find a way to encode as much as possible of the foreign type system in Haskell's type system. One way to do this is the one Wolfgang advocates: we provide a bunch of separate datatypes for each leaf node in the OO class hierarchy, we provide a class for each non-leaf node (or 'abstract class' if you prefer) in the OO class hierarchy and we use inheritance between the Haskell classes and instance declarations to encode the inheritance relations in the OO class hierarchy. This approach suffers a bit from being tedious to specify all the instances you need for each datatype but otherwise works pretty well. There are other ways, of course, and we should explore them. Perhaps you could make a concrete proposal Glynn? -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/

On Tue, Apr 15, 2003 at 09:34:29PM +0100, Alastair Reid wrote:
Since Haskell has a different type system from OO languages, we simply cannot do things the 'typical GUI toolkit' way. Instead, we have to find a way to encode as much as possible of the foreign type system in Haskell's type system.
One way to do this is the one Wolfgang advocates: we provide a bunch of separate datatypes for each leaf node in the OO class hierarchy, we provide a class for each non-leaf node (or 'abstract class' if you prefer) in the OO class hierarchy and we use inheritance between the Haskell classes and instance declarations to encode the inheritance relations in the OO class hierarchy. This approach suffers a bit from being tedious to specify all the instances you need for each datatype but otherwise works pretty well.
Manuel uses type classes to enforce the inheritance relationship. I construct these classes automatically for gtk2hs. I think this matches very closely what Glynn and you propose. One question which I asked earlier is: a) Shall the classes containt the widget's functionality as well? (Or, alternatively: b) Shall we have a class for each attribute (like "onActivate") and create instances for all widgets that implement it.) The type definition for a Button in gtk2hs looks like this (and as you might guess, it is generated automatically): Cheers, Axel. -- ********************************************************* Button newtype Button = Button (ForeignPtr (Button)) mkButton = Button unButton (Button o) = o class BinClass o => ButtonClass o where toButton :: o -> Button fromButton :: Button -> o instance ButtonClass Button where toButton = id fromButton = id instance BinClass Button where toBin = mkBin.castForeignPtr.unButton fromBin = mkButton.castForeignPtr.unBin instance ContainerClass Button where toContainer = mkContainer.castForeignPtr.unButton fromContainer = mkButton.castForeignPtr.unContainer instance WidgetClass Button where toWidget = mkWidget.castForeignPtr.unButton fromWidget = mkButton.castForeignPtr.unWidget instance ObjectClass Button where toObject = mkObject.castForeignPtr.unButton fromObject = mkButton.castForeignPtr.unObject instance GObjectClass Button where toGObject = mkGObject.castForeignPtr.unButton fromGObject = mkButton.castForeignPtr.unGObject

Axel Simon
One question which I asked earlier is: a) Shall the classes containt the widget's functionality as well? (Or, alternatively: b) Shall we have a class for each attribute (like "onActivate") and create instances for all widgets that implement it.)
Having the type system detect use of unsupported attributes is highly desirable but having a separate class for each attribute is unappealing since I expect there's a lot of them. Just one of the disadvantages is that type signatures get very, very long so you're disinclined to write them and error messages get quite outrageously long (I once got a two-page long error message from Hugs when using T-Rex). Is there some way to cluster the member functions so that each class provides multiple attributes and to use inheritance to further reduce the size of contexts one typically writes? In particular, can we exploit the inheritance hierarchy in the widget sets we are building on? -- Alastair

Alastair Reid wrote:
Having the type system detect use of unsupported attributes is highly desirable
The main problem which I see with that is that the portability
constraint would dictate that you either:
a) only support those attributes which exist on all platforms (which
may only be a small subset of the attributes which exist on any given
platform), or
b) support all of the attributes which exist on any platform, and
ignore any which don't exist on the platform in use (which undermines
the purpose of using type checks).
A more viable approach might be to use type checks with regard to
state attributes, but not for stylistic attributes.
BTW, there is a side issue in that the complete set of attributes
might only be known at run time. This would be most relevent to an
Athena backend, as there are a lot of "enhanced" Athena replacements
(Xaw3d, Xaw-Xpm, Xaw95 etc) which have additional attributes which
aren't present in the standard[1] Athena library.
[1] Actually, "standard" is rather vague, as the XFree86 version
includes enhancements which aren't in the Open Group version, and you
could probably get a decent flame-war from the question of whether
XFree86 or The Open Group constitutes the "standard".
--
Glynn Clements

Alastair Reid wrote:
Having the type system detect use of unsupported attributes is highly desirable
Glynn Clements
The main problem which I see with that is that the portability constraint would dictate that you either:
a) only support those attributes which exist on all platforms [...] b) support all of the attributes which exist on any platform, [...]
This is a portability issue. We can deal with portability issues through static checks (e.g., typesystem) or dynamic checks which either lead to problems being ignored or are flagged as runtime errors but, whatever we do, we still have to tackle the portability issue. I don't see any value in confusing questions of how we enforce restrictions and catch errors with questions of portability. -- Alastair

On Wed, Apr 16, 2003 at 10:12:00AM +0100, Alastair Reid wrote:
Glynn Clements
writes: The main problem which I see with that is that the portability constraint would dictate that you either:
a) only support those attributes which exist on all platforms [...] b) support all of the attributes which exist on any platform, [...]
This is a portability issue.
We can deal with portability issues through static checks (e.g., typesystem) or dynamic checks which either lead to problems being ignored or are flagged as runtime errors but, whatever we do, we still have to tackle the portability issue.
Actually, I thought CGA is just saying what each GUI backend contains. Each backend implementor would just copy the classes specified in the CGA and add members to it if necessary. CGA is just a specification, not a library you link to. You just have the guarantee as a user that if you program does link against CGA, it will link and run against any other backend. Axel.

Alastair Reid wrote:
There are other ways, of course, and we should explore them. Perhaps you could make a concrete proposal Glynn?
The most obvious solution is to simply use a generic Widget type for
all widgets; this is the way that Xt behaves at the application layer.
The class hierarchy is only really relevant if you're writing new
widgets (and the CGA simply isn't going to be able to handle that
aspect in a reasonable way). The only sense in which the class
hierarchy is relevant at the application level is that a subclass will
have all of the attributes of its parent class.
In any case, it probably isn't practical to model the class hierarchy
in the CGA, as different toolkits have different hierarchies (e.g.
Motif's push-button class is a subclass of label, but Qt's isn't).
Although, it's likely that there is a common ancestor for "container"
classes.
--
Glynn Clements

Glynn Clements
The class hierarchy is only really relevant if you're writing new widgets [...] The only sense in which the class hierarchy is relevant at the application level is that a subclass will have all of the attributes of its parent class.
I don't think you should dismiss the importance of this. If Haskell allowed just one member function per class and didn't provide class inheritance, class contexts would be many, many times larger than they are now. Just look at all the things that you can do with an instance of 'Num'.
In any case, it probably isn't practical to model the class hierarchy in the CGA, as different toolkits have different hierarchies (e.g. Motif's push-button class is a subclass of label, but Qt's isn't).
Remember that we're not interested in the internal details of widgets or even in the interfaces between widgets - as you say, this is only relevant if we're writing new widgets (which we're not). What we're interested in is the external interfaces of widgets. Our concern for portability of applications will force us to standardize these external interfaces across all implementations of the CGA. Once the set of operations and attributes is standardized, there is no problem using Haskell-style inheritance to make types more concise. Or, to put it another way: OO-style inheritance is about inheritance of implementation whereas Haskell-style inheritance is about inheritance of interface. Since the underlying library (Athena, Motif, Tk, etc) provides the implementation, we're free to focus on the interface and, fortunately, Haskell-style inheritance is well suited to this task. -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/

On Wed, Apr 16, 2003 at 10:26:23AM +0100, Alastair Reid wrote:
Glynn Clements
writes: The class hierarchy is only really relevant if you're writing new widgets [...] The only sense in which the class hierarchy is relevant at the application level is that a subclass will have all of the attributes of its parent class.
In any case, it probably isn't practical to model the class hierarchy in the CGA, as different toolkits have different hierarchies (e.g. Motif's push-button class is a subclass of label, but Qt's isn't). ...and Gtk's Button contain Labels. Of course we have to rearrange things a bit, but that's what a standard layer is for. I agree with Alastair here
If you extract all the widgets from a container then you can actually return a list of widgets in Haskell. You couldn't do without a type hierarchy. that creating a widget hierarchy with type classes is useful.
Remember that we're not interested in the internal details of widgets or even in the interfaces between widgets - as you say, this is only relevant if we're writing new widgets (which we're not). BTW.: I think drawing custom widgets is one aspect where people could really benefit from a common set of operations. But of course, that's for later.
I see three strands of modelling widget hierarchies in Haskell: 1.) Don't. Each widget has an individual type, unconnected to the other widgets. (Glynn's ponit of view, if I am not mistaken) 2.) Model only the hierarchy in classes. (This follows the example code I've sent earlier.) 2a.) Use one additional class for each attribute. 2b.) Lump together related attributes into a class. 2c.) Use different names for the same functionality if the functionality appears in different branches in the hierarchy tree. 3.) Model the hierarchy and the all operations/attributes in classes. I would like to see 2., in particular 2c. 2b will always be ad-hoc, thus probably not right the first time round and hard to change in hindsight. As said before, 2a is messy, especially as types get very long and there is no way in Haskell to control the export of type classes. 3 seems dodgy to me since dictionaries become huge and will contain nearly always the same functionality. Any comments? Axel.

Axel Simon wrote:
I see three strands of modelling widget hierarchies in Haskell:
1.) Don't. Each widget has an individual type, unconnected to the other widgets. (Glynn's ponit of view, if I am not mistaken)
No; my view is *not* to have distinct Haskell types for each widget
class. I should be able to replace any widget by a "compatible" widget
(e.g. a subclass) without having to change all of the code which uses
that widget.
Either have a single generic Widget type (with type checks deferred to
run time), or use type classes.
--
Glynn Clements

On Mon, Apr 14, 2003 at 09:34:50PM +0100, Glynn Clements wrote:
Wolfgang Thaller wrote:
After all, for an application where we create the UI programmatically, we would want to do localization via our own string-table mechanism. If I wanted to localize just the Xt version of an application, I'd use X resources, but if I want to localize a program for three platforms, I'd want the application to just use it's own string table mechanism... I wouldn't want to have to localize it three times.
While I can see the advantage of providing your own i18n mechanism (potentially less work), it would be a "second best", IMHO. For the Xt version, allowing the user to customise it via X resources is the "native" approach. In order for that to work, it's necessary that the application doesn't override the X resources programmatically.
We want to provide the native look'n'feel for every platform as far as possible. But when it comes to the underlying architecture, we can make compromises. IMHO we should make compromises otherwise the CGA will be nothing but a vage set of recommendation on how to name your functions. Translating strings into different languages should be centralized and, since it is not GUI specific, should be done in the internationalization library. So it should probably be something like gettext. Of course, if you specify your resources in a file, you need to make sure that gettext is called on each string. Axel.
participants (5)
-
Alastair Reid
-
Axel Simon
-
Glynn Clements
-
Vincenzo Ciancia
-
Wolfgang Thaller