JuliaLang/julia — pull request #3981 by timholy (2013-08-07 20:01:23+00:00)
Title: WIP: initial stab at an abstract graphics/GUI interface

Here's a beginning at a generic specification of a window-management/GUI interface. It is toolkit-agnostic (and consequently can't do very much), but it already attempts to provide some facilities that we currently lack. I'm posting it at this early phase because there's enough in place to be able to ask whether this is moving in the right direction, and it's a lot of work to go much further than this.

Most importantly, this organizes graphics objects into a tree, making it possible to query the list of windows, delete objects, etc. For example, currently we have no way (that I know of) of closing all opened windows in a session, short of quitting the session; this will fix that, among other things. In theory you should be able to use multiple toolkits simultaneously (e.g., Tk and Gtk) and everything will work automagically (whether that's wise is another matter...).

For Tk, something like this pull request this will allow us to resolve a currently-messy issue: the split between `TkWidget` and `Tk_Widget` etc (the various container types). We have two ways of creating windows, one of which can store elementary parent/child relationships and the other which cannot. Moreover, even if you create objects with the `Tk_Widget` type, traversing towards the root returns a `TkWidget`, which means that you've lost the type information (including information about the children in container-objects). Consequently, if you go up a level, you can't go back down again. Fixing such issues should, among other things, make it much easier to address https://github.com/JuliaLang/Tk.jl/issues/53.

I've gone to some effort to insulate the "toolkit implementer" from the details of tree traversal (and particularly, from having to maintain the organization of the tree). The basic design principle is that `GraphicsHandle`s are to be returned by all operations that create graphics objects, and these handles are simply nodes in a tree. However, they are _typed_ nodes, and consequently when used as arguments they will dispatch to the right functions.

None of this is "wired in" yet; you have to `include()`/`using` (which facilitates testing at this early stage). Here's a demo session:
```
julia> include("trees.jl")

julia> include("GUI.jl")

julia> include("/tmp/guitest.jl")

julia> using GUI, GUItest

julia> win = window("test window", 400, 200)
Window node:
parent: root node
children:
data: Window("test window",400,200)

julia> b = button(win, "Done", x->x) # deliberate mistake, we want buttons in frames
ERROR: no method button(AryNode{Window},ASCIIString,Function)

julia> f = frame(win)
Placing at position 1, 1
Frame node:
parent: Window node
children:
data: Frame(RGB(0.85,0.85,0.85))

julia> b = button(f, "Done", x->x)
Placing at position 1, 1
Button node:
parent: Frame node
children:
data: Button("Done",# function)

julia> win
Window node:
parent: root node
children: Frame
data: Window("test window",400,200)

julia> f
Frame node:
parent: Window node
children: Button
data: Frame(RGB(0.85,0.85,0.85))

```

And here was my `guitest.jl`, which is basically what the "toolkit implementer" needs to write:

```
module GUItest

using Trees
using GUI
using Color

export # types
Button,
Frame,
Window,
# functions
grid

type Window <: AbstractWindow
title::String
w::Int
h::Int
end

width(win::Window) = win.w
height(win::Window) = win.h

type Frame <: AbstractFrame
color::ColorValue
end
Frame(rgb::(Real,Real,Real)) = Frame(RGB(rgb[1],rgb[2],rgb[3]))

type Button <: AbstractButton
msg::String
callback::Function
end

grid(obj, col, row, justify) = println("Placing at position ", col, ", ", row)

end
```

You can see the generic wrapper releases the implementer from having to worry about any of the tree-hierarchy details.