
Before I finish CGA-Example 1.2 we ought to reach a sort of concensus on certain issues. Here is what I think will be in 1.2. This is a RFC: #### ## 1. A matching LaTeX specification. This will include a section for platform specific extensions via the module system. There could be implementation (toolkit,back-end) specific extensions where the implementation is called IMPL. Those would go under: CGA.extentions.IMPL Their might be general extensions that might intersect implementations. Lets say the extension is called OpenGL: CGA.extentions.OpenGL And for extensions that use a different constructor (with the extension named EXT, we would use: data T_EXT = A_EXT Int | B_EXT float . . . #### ## 2. toWidget out of IO Monad. That is if we want to continue with this route. The other route being the class hierarchy. Benefits of toWidget style: Allows for lists of widgets. Allows for compositional construction of new widgets. Negatives of toWidget style/Benefits of class Hierarchy: OO style of custom widget construction No lists of Widgets. How would children work? Class hierarchy probably will not match all backends. #### ## 3. Multiple Callback Support with unregister support. This is fine and dandy minus the implementation details. If this is implemented in Haskell, we have two possible ways to do this. These refer to each callback list instance. a) We maintain a list of IO callback methods. Each callback is identified by it's index in this list. When a callback is unregistered, it's index is changed to the (return ()) callback. b) Each callback is given a unique identifier by some generator. When a callback is unregistered, it will filter the list and remove the invalid callback. #### ## 4. Is a Window a Widget? a) No Windows. A window is certainly different than an ordinary widget. It doesn't make any sense to have a window as a child of another window. However, we may, perhaps, be able to remove the window completely from CGA. (this may be a bit too different). Lets say that an object without a parent can be displayed on the screen. So, if I say
a <- mkButton [ label := "okay", visible := True ] doOnActivate a (exitWith ExitSuccess)
I will get a button on the screen with the title bar stating "okay" with the label being "okay". Or perhaps the title bar says nothing. Simple applications don't require messing around with windows. A window is simply the highest parent in the family. This way everything can logically be a widget. What about specialized windows with titlebars, etc. For some widgets, say VBox, we may want added attributes like title. Eventually when we get to cross-platform application widgets (with menu's, etc . . .) we just make one of these the highest in the hierarchy. If it is not the highest, it could still be useful . . . say in a gui builder. b) Windows are not widgets. This can be done easily enough. It is less elegant as a) IMHO opinion. c) Windows are widgets. We must think about and handle the cases when a window is not a parent. #### ## 5. Adding children to a parent. Certain widgets and windows can have children widgets. How do we add them? If 4a) was implemented, we could talk about widgets independent of parents and it wouldn't be a problem on Mac. Otherwise, we need to assess the usefulness of creating a widget without a parent. ## Benefits of no-parent construction: Code for certain container class would reflect the thinking of the programmer more and makes for easy change. For example:
set container [ children := [name, address, phoneNumber] ]
could be quickly rearranged to
set container [ children := [name, phoneNumber, address] ]
For certain containers, such as GridBox, the name of the child as well as other parameters must be set to make it a child. This creates problems. ## Benefits of parent construction: Writing backends for Mac platforms would be easier. It doesn't make any sense to create a widget without parent (unless 4a was implemented) #### ## 6. certain Has* functions need to be made for every widget The fltk implementation is more verbose than it needs to be. I could have made these redundant functions transform the item into a Fl.Widget for a default. #### ## 7. Heiarchy Should there be an object hierarchy? Since this is a simple thing to add after it has been implemented with the Has* functions, I suggest we push this back. Since toWidget is simple enough for now, I think we should stick too it. (I'm interested in counter-opinions though) -------------------------- David J. Sankel Head Consultant Electron Consulting www.electronconsulting.com --------------------------

Before I finish CGA-Example 1.2 we ought to reach a sort of concensus on certain issues. Here is what I think will be in 1.2. This is a RFC: #### ## 1. A matching LaTeX specification.
Sounds good.
#### ## 2. toWidget out of IO Monad.
That is if we want to continue with this route. The other route being the class hierarchy. Benefits of toWidget style:
I'm not sure if I got the terminology straight... Does "toWidget style" mean there will be more pairs of functions like toWidget/fromWidget, or does it just mean "keep fromWidget and toWidget in there"? If it's the second: I vote YES, as long as it doesn't include Windows, if it's the first, I vote "no, for now".
Allows for lists of widgets.
Yes. That's why I think we need it (or at least something like it) for implementing dynamic layout.
#### ## 3. Multiple Callback Support with unregister support. This is fine and dandy minus the implementation details. If this is implemented in Haskell, we have two possible ways to do this. These refer to each callback list instance. a) We maintain a list of IO callback methods. Each callback is identified by it's index in this list. When a callback is unregistered, it's index is changed to the (return ()) callback. b) Each callback is given a unique identifier by some generator. When a callback is unregistered, it will filter the list and remove the invalid callback.
a) is good enough for a sample implementation, but in the end, I'd recommend b) (for platforms where multiple callbacks aren't implemented in the foreign toolkit library).
#### ## 4. Is a Window a Widget? a) No Windows.
Sounds like a nice idea, but no. It's too "strange" (in the sense of "new"), we don't know yet if it can work properly, and it adds complexity to implementations.
b) Windows are not widgets. This can be done easily enough. It is less elegant as a) IMHO opinion. c) Windows are widgets. We must think about and handle the cases when a window is not a parent.
I'm for b), in the sense that Window should not be an instance of IsWidget. Implementing a common generic type for Windows and Controls/Panes is tedious/artificial on MacOS. I see no problem with Window being an instance of classes like HasSize, HasTitle etc. and providing many features that other widgets have.
#### ## 5. Adding children to a parent. If 4a) was implemented, we could talk about widgets independent of parents and it wouldn't be a problem on Mac.
The problem would still be there; but part of the complexity would already be necessary due to 4a.
Benefits of parent construction: Writing backends for Mac platforms would be easier.
In fact, if parents are not mandatory arguments to the widget constructor, writing backends for Carbon (Mac OS) and Xt (Motif) would become perhaps about twice as much work. The backend would have to keep a Haskell data structure with the values for all attributes that have been set; the native widget could only be created when the container (or, in the case of Mac OS, at least the window) became known. If someone moves a widget from one window to another, the Mac backend would have to automatically destroy the widget and create a new one. I think we can live without that functionality for now.
####
## 6. certain Has* functions need to be made for every widget The fltk implementation is more verbose than it needs to be. I could have made these redundant functions transform the item into a Fl.Widget for a default.
Yes. Most importantly, we will need CGA.Widget to be an instance of certain Has* functions, too, in order to implement dynamic layout managers like GridBox. The GridBox would have a list of widgets to lay out along with the corresponding layout attributes, and then it would have to query the widgets for things like minimum size, preferred size and so on, and then modify the layout of the widgets. So we do need access to that default.
#### ## 7. Heiarchy Should there be an object hierarchy? Since this is a simple thing to add after it has been implemented with the Has* functions, I suggest we push this back. Since toWidget is simple enough for now, I think we should stick too it. (I'm interested in counter-opinions though)
I agree. Please also have a short look at my last posting in the "Re: [GUI] Are windows just ordinary widgets?" thread. It contains a snapshot of my thoughts as of yesterday evening... Thanks for your work on the CGA Example; I think the example 1.1 has already brought the discussion a great deal forward. Cheers, Wolfgang

Wolfgang Thaller wrote:
b) Windows are not widgets. This can be done easily enough. It is less elegant as a) IMHO opinion. c) Windows are widgets. We must think about and handle the cases when a window is not a parent.
I'm for b), in the sense that Window should not be an instance of IsWidget. Implementing a common generic type for Windows and Controls/Panes is tedious/artificial on MacOS. I see no problem with Window being an instance of classes like HasSize, HasTitle etc. and providing many features that other widgets have.
So, if you take an arbitrary widget, and find all of its ancestors by repeated application of getParent (or whatever it's called), where do you end up? I'm dubious as to the implications of trying to declare that windows aren't widgets on backends where windows really are widgets. Implementing the fiction is easy enough; you just end up with lots of pairs of functions with different names (e.g. getWidgetSize, getWindowSize) which happen to have identical implementations. The problems lie in keeping up the illusion; unless you trap every single place where widgets escape to Haskell-land, code will end up being passed widgets which turn out to be windows.
Benefits of parent construction: Writing backends for Mac platforms would be easier.
In fact, if parents are not mandatory arguments to the widget constructor, writing backends for Carbon (Mac OS) and Xt (Motif) would become perhaps about twice as much work.
On Xt, it would become almost impossible, given that most of the
attribute values get initialised at the point that the actual widget
gets created.
In case I haven't made this point enough already: I consider total
portability to be a chimera. Sun put a lot of effort (more than we
have the resources for) into it for Java, with the net result that
even simple Java applications look decidedly non-native on all
platforms, and they still didn't entirely get rid of portability
issues.
My other point is that, when a trade-off has to be made between
portability and native-ness, it should be up to the application
programmer as to which aspect wins. If the CGA ends up forcing a
particular set of compromises onto the application, the first time
that a developer is unable to accept those compromises they will end
up writing their own UI library (and, in the process, reducing the CGA
to being just another UI library; it's only *the* UI library if we
don't need any others).
--
Glynn Clements

Glynn Clements wrote:
Wolfgang Thaller wrote:
b) Windows are not widgets. This can be done easily enough. It is less elegant as a) IMHO opinion. c) Windows are widgets. We must think about and handle the cases when a window is not a parent.
I'm for b), in the sense that Window should not be an instance of IsWidget. Implementing a common generic type for Windows and Controls/Panes is tedious/artificial on MacOS. I see no problem with Window being an instance of classes like HasSize, HasTitle etc. and providing many features that other widgets have.
So, if you take an arbitrary widget, and find all of its ancestors by repeated application of getParent (or whatever it's called), where do you end up?
On MacOS, you'd repeatedly call GetSuperControl() until you reach the root control of a window, for which there is no super control. For any "Control" (widget inside window, but not window), you can call GetControlOwner(), which returns the window that the control is in. Windows on MacOS do not have parents. Only while a window-modal dialog is _active_ it has a parent window. On Win32, IIRC, you get all the way to the top-level windows created by the application. This will be just one MDI frame window for MDI apps, but several windows for apps that use separate toplevels. We can map the hierarchy within windows one-to-one on all (?) platforms. The relationships between windows are less "standardized" and will require an additional abstraction layer. That's another reason why a distinction between Windows and Panes makes sense for CGA. BTW: why do you need to walk the widget hierarchy all the way to the top? The only reason why I ever needed to do that in any toolkit was to find the toplevel window that contained a widget (which could have been much easier if there had been a dedicated function for finding the Window, as there is on MacOS).
I'm dubious as to the implications of trying to declare that windows aren't widgets on backends where windows really are widgets.
Implementing the fiction is easy enough; you just end up with lots of pairs of functions with different names (e.g. getWidgetSize, getWindowSize) which happen to have identical implementations.
I thought that's what we have type classes for in Haskell. We'd have getSize implementations for Windows and for Panes (non-window widgets).
The problems lie in keeping up the illusion; unless you trap every single place where widgets escape to Haskell-land, code will end up being passed widgets which turn out to be windows.
Keeping up an illusion is always difficult. But we have to keep up at least one illusion, because otherwise we'd have not one cross-platform API but several platform-specific APIs. Now I realize that having different functions (like getWindowSize and getWidgetSize) is undesirable. Axel will probably oppose having another type class here. Also, using a type class will probably require explicit instance declarations for every widget type. It _is_ possible to unify Panes and Windows under MacOS; the MacOS implementation would have to look about like this: import Carbon(WindowRef, ControlRef) class IsWidget w where toWidget :: w -> Widget data Widget = WindowWidget WindowRef | ControlWidget ControlRef getSize :: IsWidget w => w -> IO (Int, Int) getSize w = case (toWidget w) of WindowWidget win -> getWindowSize win ControlWidget ctl -> getControlSize win getWindowSize win = ... getControlSize ctl = ... -- and so on for every function that all widgets have in common. I don't like that, but perhaps it's necessary. In any case, I still want class IsPane p where toPane :: p -> Pane Also, we should be very careful about where to put the container functionality. Not all widgets are containers, and I don't think the relation between windows can be referred to as a containment relationship (it should be treated separately). I'm in favour of a class Container, that is implemented by Window and by some specific types like GridBox. Widget constructors would then look like newFoo :: Container c => c -> [Prop Foo] -> IO Foo. The disadvantage of this is that it becomes hard to have a general getParent function... what are the alternatives? Cheers, Wolfgang

On Fri, May 02, 2003 at 06:08:19PM +0100, Glynn Clements wrote:
Wolfgang Thaller wrote:
b) Windows are not widgets. This can be done easily enough. It is less elegant as a) IMHO opinion.
[..]
I'm for b), in the sense that Window should not be an instance of IsWidget. [..] I'm dubious as to the implications of trying to declare that windows aren't widgets on backends where windows really are widgets.
Implementing the fiction is easy enough; you just end up with lots of pairs of functions with different names (e.g. getWidgetSize, getWindowSize) which happen to have identical implementations. The problems lie in keeping up the illusion; unless you trap every single place where widgets escape to Haskell-land, code will end up being passed widgets which turn out to be windows.
If I may poke around in the future of our discussion, I see different types of application setups: SDI, MDI, Control. These could be special toplevel windows. Thus I think making Windows and Panes distinct is the better solution (or: illusion).
In case I haven't made this point enough already: I consider total portability to be a chimera. Sun put a lot of effort (more than we have the resources for) into it for Java, with the net result that even simple Java applications look decidedly non-native on all platforms, and they still didn't entirely get rid of portability issues.
I do believe we can make some functionality available that does look and feel totally native, provided that a) the user codes careful (e.g. provides different resource files for different platforms) b) we restrict the CGA appropriately
My other point is that, when a trade-off has to be made between portability and native-ness, it should be up to the application programmer as to which aspect wins. If the CGA ends up forcing a particular set of compromises onto the application, the first time that a developer is unable to accept those compromises they will end up writing their own UI library (and, in the process, reducing the CGA to being just another UI library; it's only *the* UI library if we don't need any others). Yes, we shouldn't stand in the way of using the native backend's functions. But we have to make a decision on the core functionality, or don't you think so? Our aim is that an application written just with the CGA should work with all backends. If Windows and Panes have the same root on one platform but not on another, then you cannot write an application that runs with different backends!?
Axel.

Axel Simon wrote:
My other point is that, when a trade-off has to be made between portability and native-ness, it should be up to the application programmer as to which aspect wins. If the CGA ends up forcing a particular set of compromises onto the application, the first time that a developer is unable to accept those compromises they will end up writing their own UI library (and, in the process, reducing the CGA to being just another UI library; it's only *the* UI library if we don't need any others).
Yes, we shouldn't stand in the way of using the native backend's functions. But we have to make a decision on the core functionality, or don't you think so?
Enough functionality has to be specified for the CGA to be usable, but it's possible to leave some things unspecified. In fact, it's almost inevitable that some things will be unspecified; I don't think that it's possible to specify the semantics so thoroughly that a CGA program simply could not tell which backend was in use.
Our aim is that an application written just with the CGA should work with all backends.
I would aim slightly lower: it should be possible to write
applications which work on all backends; I don't think that we need to
go so far as to support the converse: that it's impossible to write an
application which doesn't work on all backends.
--
Glynn Clements

On Sun, May 04, 2003 at 04:49:28AM +0100, Glynn Clements wrote:
Our aim is that an application written just with the CGA should work with all backends.
I would aim slightly lower: it should be possible to write applications which work on all backends; I don't think that we need to go so far as to support the converse: that it's impossible to write an application which doesn't work on all backends.
Ok, I think we mean the same thing here. With CGA I meant the standarized parts, additional backend features not included. Axel.

Axel Simon wrote:
Our aim is that an application written just with the CGA should work with all backends.
I would aim slightly lower: it should be possible to write applications which work on all backends; I don't think that we need to go so far as to support the converse: that it's impossible to write an application which doesn't work on all backends.
Ok, I think we mean the same thing here. With CGA I meant the standarized parts, additional backend features not included.
So did I. Even without using any backend-specific extensions, it is
likely to be possible to write code which doesn't work correctly on
all backends.
The only ways to avoid that are:
1. Limit the functionality which the CGA provides to that which works
identically on all platforms. Unfortunately, that would be too minimal
to be of any real use.
2. Specify everything down to the most minute detail. Implementing a
backend which entirely meets such a specification would be more work
than anyone is likely to be willing to perform. It would also
eliminate the possibility of native look-and-feel.
Any realistic CGA specification will end up leaving some aspects as
either implementation-dependent or undefined.
We have to define enough to make the CGA usable. We don't have to
define everything, and I don't think that we should even try. Some
things *should* be platform-specific, and some of those differences
should be visible to applications which care to enquire about them.
--
Glynn Clements

On Thu, May 01, 2003 at 11:59:48PM -0400, David J. Sankel wrote:
Before I finish CGA-Example 1.2 we ought to reach a sort of concensus on certain issues. Here is what I think will be in 1.2. This is a RFC:
#### ## 1. A matching LaTeX specification.
This will include a section for platform specific extensions via the module system.
I don't think we really need a specification in Latex. I'd rather see a set of Haskell modules with empty function bodies. The explanaition can be in the Haddocked comments.
There could be implementation (toolkit,back-end) specific extensions where the implementation is called IMPL. Those would go under:
CGA.extentions.IMPL
Backend provide a superset of the entities defined in the CGA specification. But the difference to the CGA is not an extension.
Their might be general extensions that might intersect implementations. Lets say the extension is called OpenGL:
CGA.extentions.OpenGL
And for extensions that use a different constructor (with the extension named EXT, we would use:
data T_EXT = A_EXT Int | B_EXT float . . .
No, please. I think using the module system is nicer. We don't need to create aritifical name spaces because we are not in C. Let me clearify: Let's suppose the file CGA.hs in the fptools repository contains the following definitons: module CGA(Foo(..), foo) where data Foo = Bar foo :: Int -> Int foo = undefined Anyone providing a backend to the CGA copies the first line into her/his library under the same name. If that person is me, I would write: module CGA(Foo(..), foo) where import Gtk (Foo(Bar), foo) thus I only make things from Gtk visible which are defined in the CGA. If the user wants to use the Gtk specific constructors Bar2 and the function bar (s)he needs to write: import CGA import Gtk -- Gtk specific function: uses Bar2 constructor. myFunc :: Foo -> Foo myFunc Bar2 = Bar myFunc Bar = Bar And by explicitly importing Gtk the user is aware that (s)he uses backend specific extensions. Additional benefits: - the user can try to compile her/his GUI application with -package CGA. It won't run, but if it compiles (s)he can be sure the program would run with another backend as well. - we could provide a program that uses all functions from the CGA in fptools repository. By compiling this program against different backends, we could check that we export everything that the CGA requires. - the modules in the fptools repository can serve as the documentation for people writing cross-platform applications.
#### ## 2. toWidget out of IO Monad.
That is if we want to continue with this route. The other route being the class hierarchy.
Benefits of toWidget style: Allows for lists of widgets. Allows for compositional construction of new widgets.
Negatives of toWidget style/Benefits of class Hierarchy: OO style of custom widget construction No lists of Widgets. How would children work? Class hierarchy probably will not match all backends. I am confused about the different routes. I would need to have a closer look to see these points.
#### ## 3. Multiple Callback Support with unregister support.
This is fine and dandy minus the implementation details. If this is implemented in Haskell, we have two possible ways to do this. These refer to each callback list instance.
a) We maintain a list of IO callback methods. Each callback is identified by it's index in this list. When a callback is unregistered, it's index is changed to the (return ()) callback.
b) Each callback is given a unique identifier by some generator. When a callback is unregistered, it will filter the list and remove the invalid callback.
Why do you need to keep the unregister functions in a list? Is this specific to your example?
#### ## 4. Is a Window a Widget?
a) No Windows.
A window is certainly different than an ordinary widget. It doesn't make any sense to have a window as a child of another window. However, we may, perhaps, be able to remove the window completely from CGA. (this may be a bit too different).
Lets say that an object without a parent can be displayed on the screen. So, if I say
a <- mkButton [ label := "okay", visible := True ] doOnActivate a (exitWith ExitSuccess)
I will get a button on the screen with the title bar stating "okay" with the label being "okay". Or perhaps the title bar says nothing. Simple applications don't require messing around with windows. A window is simply the highest parent in the family. This way everything can logically be a widget.
What about specialized windows with titlebars, etc. For some widgets, say VBox, we may want added attributes like title. Eventually when we get to cross-platform application widgets (with menu's, etc . . .) we just make one of these the highest in the hierarchy. If it is not the highest, it could still be useful . . . say in a gui builder.
b) Windows are not widgets. This can be done easily enough. It is less elegant as a) IMHO opinion.
I don't really see an advantage in a), though. Just avoiding two lines to create a containing dialog around a button is not a good justification of all the implementation work involved in your proposal IMHO. I think b) is most elegant and fits in when we define different classes of applications/dialogs.
c) Windows are widgets. We must think about and handle the cases when a window is not a parent.
#### ## 5. Adding children to a parent.
Certain widgets and windows can have children widgets. How do we add them?
If 4a) was implemented, we could talk about widgets independent of parents and it wouldn't be a problem on Mac.
Otherwise, we need to assess the usefulness of creating a widget without a parent.
## Benefits of no-parent construction:
Code for certain container class would reflect the thinking of the programmer more and makes for easy change. For example:
set container [ children := [name, address, phoneNumber] ]
could be quickly rearranged to
set container [ children := [name, phoneNumber, address] ]
For certain containers, such as GridBox, the name of the child as well as other parameters must be set to make it a child. This creates problems.
## Benefits of parent construction:
Writing backends for Mac platforms would be easier. It doesn't make any sense to create a widget without parent (unless 4a was implemented)
Can we come up with an example where the latter falls over (providing the parent when you create a widget)? This approach always avoid the problem of the user adding one widget to two different widget hierarchies.
#### ## 6. certain Has* functions need to be made for every widget
The fltk implementation is more verbose than it needs to be. I could have made these redundant functions transform the item into a Fl.Widget for a default.
I don't like Has* classes. But I guess I made myself clear already.
#### ## 7. Heiarchy
Should there be an object hierarchy? Since this is a simple thing to add after it has been implemented with the Has* functions, I suggest we push this back. Since toWidget is simple enough for now, I think we should stick too it. (I'm interested in counter-opinions though)
I'll have a look. Axel.
participants (4)
-
Axel Simon
-
David J. Sankel
-
Glynn Clements
-
Wolfgang Thaller