[GUI] RFC: CGA-Example 1.2 stuff

Wolfgang Thaller wolfgang.thaller@gmx.net
Sat, 3 May 2003 12:46:21 +0200


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