[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