Browse Source

Making progress on the UI, trying a css1 parser.

Beoran 6 years ago
parent
commit
5181de4baa

+ 0 - 3
engine/geometry/vector.go

@@ -25,6 +25,3 @@ func (v1 Vector) Sum(v2 Vector) Vector {
    return NewVector(v1.X + v2.X, v1.Y + v2.Y)
 }
 
-
-
-

+ 75 - 16
engine/global/state.go

@@ -5,7 +5,15 @@ import "math"
 import al "gitlab.com/beoran/al5go/al"
 import "gitlab.com/beoran/ebsgo/engine/tile"
 import "gitlab.com/beoran/ebsgo/engine/physics"
+// import "gitlab.com/beoran/ebsgo/engine/geometry"
 import "gitlab.com/beoran/ebsgo/monolog"
+import zori_backend     "gitlab.com/beoran/ebsgo/zori/backend"
+import al5go_backend    "gitlab.com/beoran/ebsgo/zori/backend/al5go"
+import "gitlab.com/beoran/ebsgo/zori"
+import "gitlab.com/beoran/ebsgo/zori/style"
+import ztypes "gitlab.com/beoran/ebsgo/zori/types"
+import zevent "gitlab.com/beoran/ebsgo/zori/event"
+import "gitlab.com/beoran/ebsgo/zori/widget"
 
 
 type FPS struct {
@@ -15,8 +23,6 @@ type FPS struct {
     Show    bool
 }
 
-
-
 type State struct {
     Display     * al.Display
     Fullscreen    bool
@@ -43,6 +49,8 @@ type State struct {
 //  zori.Console
 //  zori.State
 //  Actor area.Thing
+    zori_backend.Backend
+    * zori.UI
 }
 
 func (ms * State) Hf() float32 {
@@ -77,17 +85,17 @@ func (ms * State) SetupJoysticks() {
     
     ms.Joysticks = make([]*al.Joystick, 0)
     
-    num := al.GetNumJoysticks()
+    num := al.NumJoysticks()
     
     
     monolog.Printf("Found %d joysticks.\n", num)
     
     for i:= 0; i < num; i ++ {
-        joy := al.GetJoystick(i)
+        joy := al.FindJoystick(i)
         ms.Joysticks = append(ms.Joysticks, joy)
-        monolog.Printf("Joystick nr %d, name %s", i, joy.GetName())
-        snum := joy.GetNumSticks()
-        bnum := joy.GetNumButtons()
+        monolog.Printf("Joystick nr %d, name %s", i, joy.Name())
+        snum := joy.NumSticks()
+        bnum := joy.NumButtons()
         monolog.Printf("%d sticks and %d buttons", snum, bnum)        
     }
     
@@ -118,6 +126,40 @@ func (ms * State) InstallAllegro() bool {
     return res
 }
 
+func (ms * State) SetupTestUI(theme * style.Theme)  {
+    box       := ztypes.Bounds(80, 90, 120, 30)
+    label     := widget.NewLabel(ms.UI.Root, &box, "Heyho")
+    ms.UI.Root.AddChild(label)
+    
+    ms.UI      = zori.New(ms.Backend, theme)
+    box2      := ztypes.Bounds(80, 120, 120, 30)
+    var button1  * widget.Button
+    button1    = widget.NewButton(ms.UI.Root, &box2, "Button 1", 
+                 func(w widget.Widget, d... interface{}) zevent.Result {
+                        monolog.Info("Button pressed handler for %v", button1)
+                        return zevent.Done
+                 })
+    ms.UI.Root.AddChild(button1)
+}
+
+func (ms * State) SetupUI()  {
+    theme := &style.Theme{}
+    theme.Border.Color      = al.MapRGB(255, 255, 255)
+    theme.Border.Radius     = 5
+    theme.Background.Color  = al.MapRGB(0, 16, 64)
+    theme.Background.Radius = 5
+    theme.Text.Font         = ms.DefaultFont
+    theme.Text.Color        = al.MapRGB(0xff, 0xff, 0xee)
+            
+    ms.Backend = al5go_backend.New(ms.Display, ms.Queue)
+    ms.UI      = zori.New(ms.Backend, theme)
+    ms.UI.Root.Cursors.Mouse.TargetTheme = *theme
+    ms.UI.Root.Cursors.Keyjoy.TargetTheme = *theme
+    ms.UI.Root.Cursors.Mouse.TargetTheme.Background.Color = al.MapRGB(32, 32, 128)
+    
+    ms.SetupTestUI(theme)
+}
+
 func (ms * State) OpenDisplay(w, h int, title string, fullscreen bool) * al.Display {
     ms.Fullscreen = fullscreen
     ms.W          = w
@@ -136,8 +178,8 @@ func (ms * State) OpenDisplay(w, h int, title string, fullscreen bool) * al.Disp
     }
     ms.Display = display
     
-    ms.Queue.RegisterEventSource(al.GetKeyboardEventSource())
-    ms.Queue.RegisterEventSource(al.GetMouseEventSource())
+    ms.Queue.RegisterEventSource(al.KeyboardEventSource())
+    ms.Queue.RegisterEventSource(al.MouseEventSource())
     ms.DefaultFont = al.CreateBuiltinFont()    
     
     ms.FPS.Time = al.Time()
@@ -147,6 +189,8 @@ func (ms * State) OpenDisplay(w, h int, title string, fullscreen bool) * al.Disp
     
     tile.InitBlendMasks();
     
+    ms.SetupUI();
+    
     return display
 }
 
@@ -196,6 +240,10 @@ func (ms * State) Update() {
         ms.Map.Update(delta)
     }
     
+    if ms.UI != nil {
+        ms.UI.Update(delta)
+    }
+    
     if (now - ms.FPS.Time) >= 1.0 {
         realfps := float64(ms.Frames) / (now - ms.FPS.Time)
         /* Display and use a rounded value for FPS, the number after the comma is normally due to jitter anyway. */
@@ -209,8 +257,13 @@ func (ms * State) Update() {
 
 func (ms * State) Draw() {
     if ms.Map != nil {
-            ms.Map.Draw(&ms.Camera)
+        ms.Map.Draw(&ms.Camera)
     }
+    
+    if ms.UI != nil {
+        ms.UI.Draw()
+    }
+        
     yellow := al.MapRGB(255, 255, 0)
     al.DrawFilledCircle(30.0, 40.0, 15.0, yellow) 
     ms.DrawFPS()
@@ -218,7 +271,8 @@ func (ms * State) Draw() {
 }
 
 func (ms * State) Load() {
-    ms.LoadMap("map/map_0001_tiled115.tmx")
+    
+   //  ms.LoadMap("map/map_0001_tiled115.tmx")
 }
 
 func (ms * State) Close() {
@@ -252,22 +306,27 @@ func (ms * State) Run() {
 
 
 func (ms * State) HandleEvents() {
-    for event, ok := ms.Queue.GetNextEvent() ; ok ; event, ok = ms.Queue.GetNextEvent() {
+    for event, ok := ms.Queue.NextEvent() ; ok ; event, ok = ms.Queue.NextEvent() {
         ms.HandleEvent(event.Event())
     }
 }
 
 
 func (ms * State) HandleEvent(event al.Event) {
-    switch uevent := event.(type) {
-        case *al.KeyboardEvent: ms.HandleKeyboardEvent(uevent)
+    switch event.Type() {
+        case al.EVENT_KEY_DOWN: ms.HandleKeyboardEvent(event)
         default: break
     }
+    if ms.UI != nil {
+        evwrap := al5go_backend.WrapEvent(ms.Backend, event)
+        ms.UI.Dispatch(evwrap)
+    }
 
 }
 
-func (ms * State) HandleKeyboardEvent(event * al.KeyboardEvent ) {
-    switch (event.Keycode()) {
+func (ms * State) HandleKeyboardEvent(event al.Event ) {
+    kev := event.(*al.KeyboardEvent)
+    switch kev.KeyCode() {
         case al.KEY_ESCAPE: ms.Done = true
         case al.KEY_UP: ms.Camera.Y-= 10
         case al.KEY_DOWN: ms.Camera.Y+= 10

+ 3 - 1
monolog/monolog.go

@@ -85,7 +85,9 @@ func (me *FileLogger) Log(level string, file string, line int, format string, ar
     } else {
         fmt.Fprint(me.file, format)
     }
-    fmt.Fprint(me.file, "\n")
+    if format[len(format)-1] != '\n' {
+        fmt.Fprint(me.file, "\n")
+    }
 }
 
 func NewFileLogger(filename string) (logger Logger, err error) {

+ 17 - 17
zori/backend/backend.go

@@ -1,33 +1,33 @@
 package backend
 
 import . "gitlab.com/beoran/ebsgo/zori/types"
-import "gitlab.com/beoran/ebsgo/zori/event"
+// import "gitlab.com/beoran/ebsgo/zori/event"
 
 
 type Loader interface {
-    Bitmap(name string) (Bitmap, error)
-    Font(name string) (Bitmap, error)
+    LoadBitmap(name string) (Bitmap, error)
+    LoadFont(name string, size int) (Font, error)
 }
 
 type Drawer interface {
-    Bitmap(x, y int, bitmap Bitmap)
-    ScaledBitmap(x, y, dw, dh int, bitmap Bitmap)
-    Rectangle(x, y, w, h int, color Color)
-    Slab(x, y, w, h int, color Color)
-    RoundedRectangle(x, y, w, h, rx, ry int, color Color)
-    RoundedSlab(x, y, w, h, rx, ry int, color Color)
+    DrawBitmap(x, y int, bitmap Bitmap)
+    DrawScaledBitmap(sx, sy, sw, sh, dx, dy, dw, dh int, bitmap Bitmap)
+    DrawRectangle(x, y, w, h int, color Color, thickness int)
+    DrawSlab(x, y, w, h int, color Color)
+    DrawRoundedRectangle(x, y, w, h, rx, ry int, color Color, thickness int)
+    DrawRoundedSlab(x, y, w, h, rx, ry int, color Color)
+    DrawText(font Font, color Color, x, y int, text string)
+    DrawMultilineText(font Font, color Color, x, y, max_width, line_height int, text string)    
 }
 
 
-type Poller interface {
-    Empty() bool
-    Poll() event.Event
+type Displayer interface {
+    Display() Display
 }
 
 type Backend interface {
-    Display()   Display
-    Load()      Loader
-    Draw()      Drawer
-    Queue()     Poller
-    Close()
+    Displayer
+    Loader
+    Drawer
 }
+

+ 57 - 0
zori/css1.peg

@@ -0,0 +1,57 @@
+# CSS1 parser for a PEG parser generator.
+# and a user command parser
+# Doesn't support import statements.
+
+
+STYLESHEET      ← (CDO / CDC)*  (RULESET/DECLARATIONS/COMMENT/ATRULE)* (CDO / CDC)*
+RULESET         ← SELECTORS "{" DECLARATIONS? "}"
+SELECTORS       ← SELECTOR ("," SELECTOR)*
+SELECTOR        ← ( NAME ID? CLASS? PSEUDO_CLASS? /
+                  ID CLASS? PSEUDO_CLASS? /
+                  CLASS PSEUDO_CLASS? /
+                  PSEUDO_CLASS )+ PSEUDO_ELEMENT* / PSEUDO_ELEMENT 
+ATRULE          ← ATID STRING ";" / ATID "{" DECLARATIONS "}" 
+NAME            ← <IDENT>
+DECLARATIONS    ← DECLARATION (';' DECLARATION)* ';'?
+DECLARATION     ← NAME ":" EXPR PRIO? / COMMENT 
+OPERATOR        ← '/' / ','
+# UNARY_OPERATOR  ← '-' / '+'
+PRIO            ← IMPORTANT
+EXPR            ← TERM (OPERATOR TERM)*
+TERM            ← STRING / PERCENTAGE / LENGTH / HEXCOLOR / URL / RGB / NUM / IDENT
+~COMMENT        ←  "/*" (!("*/").)* "*/"
+ATID            ← < "@" [a-zA-Z][-_a-zA-Z0-9]+ >
+IMPORTANT       ← < "!" WS "important" >
+IDENT           ← < [-_a-zA-Z][-_a-zA-Z0-9]* >
+PERCENTAGE      ← < NUM > "%"
+NUM             ← < [+-]*[0-9]+ / [0-9]* "." [0-9]+ >
+LENGTH          ← NUM UNIT 
+UNIT            ← <"pt" / "mm" / "pt" / "in" / "px" / "em" / "ex" >
+PSEUDO_ELEMENT  ←  <":first-letter" / ":first-line" >
+PSEUDO_CLASS    ←  <":hover" / ":link" / ":mark" / ":active" / ":visited" >
+HEXCOLOR        ← "#" < ([0-9a-fA-F])+ > 
+ID              ← "#" < IDENT >
+CLASS           ← "." < IDENT >
+
+# Raw string macro.
+RAWSM(O,C)          ← O < (!(C).)* > C
+# Escaped string macro.
+ESCSM(O,C)          ← O < (!(C)STRCHAR)* > C
+# String escapes
+STRESC1      ← "\\" [nrtfv\'\\"\[\]\\]
+STRESC2      ← "\\" [0-3] [0-7] [0-7]
+STRESC3      ← "\\" [0-7] [0-7]*
+STRESC4      ← "\\x" [0-9a-fA-F] [0-9a-fA-F]?
+STRESC5      ← "\\u" [0-9a-fA-F]+
+STRNOESC     ← (!('\\\\').)
+STRCHAR      ←   STRESC1 / STRESC2 / STRESC3 / STRESC4 / STRESC5 / STRNOESC  
+# Strings
+STRING       ← ESCSM("\"", "\"") / ESCSM("'", "'")
+URL                 ← "url(" STRING  ")"
+RGB                 ← < "rgb("[ \t]*[0-9]+[ \t]* "," [ \t]*[0-9]+[ \t]* "," [ \t]*[0-9]+[ \t]*  ")" >
+~WS                 ←   [ \t\n\r]*
+CDO                 ←   "<!--"
+CDC                 ←   "-->"
+%whitespace         ←   [ \t\n\r]*
+# %word               ←  [a-zA-Z][-+/*_a-zA-Z0-9]*
+

+ 21 - 0
zori/css1_example.css

@@ -0,0 +1,21 @@
+:hover {
+    background-color: red;
+}
+
+button.small:hover, root { 
+    quux: 2.7%;
+    foo : #fff;
+    col: rgb(255, 241, 235) ;
+    bar : -21%; 
+    baz: 77 !important;
+}
+
+foo   : +10.5px;
+bar   : quux;
+
+
+@print {
+    bar : 50px;
+}
+
+@import "foo.bar" ;

+ 197 - 146
zori/event/event.go

@@ -5,10 +5,62 @@ package event
 import . "gitlab.com/beoran/ebsgo/zori/types"
 
 
+// event types, intentionally same integer values as Allegro 5's event types
+// apart from the Zori-specific events.
+const (
+   TypeJoystickAxis          =  1
+   TypeJoystickButtonDown    =  2
+   TypeJoystickButtonUp      =  3
+   TypeJoystickConfiguration =  4   
+   TypeKeyDown               = 10
+   TypeKeyChar               = 11
+   TypeKeyUp                 = 12   
+   TypeMouseAxes             = 20
+   TypeMouseButtonDown       = 21
+   TypeMouseButtonUp         = 22
+   TypeMouseEnterDisplay     = 23
+   TypeMouseLeaveDisplay     = 24
+   TypeMouseWarped           = 25   
+   TypeTimer                 = 30   
+   TypeDisplayExpose         = 40
+   TypeDisplayResize         = 41
+   TypeDisplayClose          = 42
+   TypeDisplayLost           = 43
+   TypeDisplayFound          = 44
+   TypeDisplaySwitchIn       = 45
+   TypeDisplaySwitchOut      = 46
+   TypeDisplayOrientation    = 47
+   TypeDisplayHaltDrawing    = 48
+   TypeDisplayResumeDrawing  = 49   
+   TypeTouchBegin            = 50
+   TypeTouchEnd              = 51
+   TypeTouchMove             = 52
+   TypeTouchCancel           = 53
+   TypeDisplayConnected      = 60
+   TypeDisplayDisconnected   = 61
+   TypeZoriUpdate            = 70311  
+   TypeZoriResize            = 70312
+   TypeZoriDraw              = 70313
+   TypeZoriDestroy           = 70314
+   TypeZoriAction            = 70315   
+   TypeZoriClose             = 70316
+   TypeZoriNewChild          = 70317
+)
+
+
 type Basic struct {
     Stamp  float64
-    // Widget Widget
-    Data   interface{}
+    // Cannot be Widget type to avoid dependency loop, but should hold a widget or nil.
+    EventWidget interface{}
+    EventData   interface{}
+}
+
+func (be Basic) Data() interface{} {
+    return be.EventData
+}
+
+func (be Basic) Widget() interface{} {
+    return be.EventWidget
 }
 
 func (be Basic) Timestamp() float64 {
@@ -21,103 +73,106 @@ type Update struct {
     TimePassed float64
 }
 
+func (be Update) Type() int {
+    return TypeZoriUpdate
+}
+
+
 /* Resize event, when the parent of an element has resized. */
 type Resize struct {
     Basic
     NewSize Box
 }
 
+func (be Resize) Type() int {
+    return TypeZoriResize
+}
+
+
 /* Draw event when the UI has to draw itself. */
 type Draw struct {
     Basic
 }
 
+func (be Draw) Type() int {
+    return TypeZoriDraw
+}
+
+
 /* Cleanup event. */
 type Destroy struct {
     Basic
 }
 
+func (be Destroy) Type() int {
+    return TypeZoriDestroy
+}
+
+
+type ActionCallback func (data ... interface{}) Result
+
 /* Action event. */
 type Action struct {
     Basic
+    ActionCallback
+}
+
+func (be Action) Type() int {
+    return TypeZoriAction
 }
 
 /* Close event. */
 type Close struct {
     Basic
-    // Parent Widget
+    Parent interface {}
 }
 
-/* Close event. */
+func (be Close) Type() int {
+    return TypeZoriClose
+}
+
+/* New child event. */
 type NewChild struct {
     Basic
-    // Child Widget
+    Child interface {}
 }
 
-type Event interface {
-    Timestamp() float64
+func (be NewChild) Type() int {
+    return TypeZoriNewChild
 }
 
-type JoyButtonPress interface {
-    ID() int
-    Button() int
-    IsJoyButtonPress()
+
+type Event interface {
+    Type() int
+    Timestamp() float64
 }
 
-type JoyButtonRelease interface {
-    ID() int
-    Button() int
-    IsJoyButtonRelease()
+type Zori interface {
+    Event
+    Data()   interface{}
+    Widget() interface{}
 }
 
-type JoyAxis interface {
+type Joystick interface {
     Event
     ID() int
+    Button() int
     Stick() int
     Axis() int
     Pos() float32
-    IsJoyAxis()
 }
 
-type KeyPress interface {
-    Event
-    KeyCode() int
-    Display() Display
-    IsKeyPress()
-}
-
-type KeyRelease interface {
-    Event
-    KeyCode() int
-    Display() Display
-    IsKeyRelease()
-}
-
-type KeyChar interface {
+type Key interface {
     Event
     KeyCode() int
     Display() Display
     Unichar() rune
     Modifiers() int
     Repeat() bool
-    IsKeyChar()
 }
 
-type MouseAxes interface {
-    Event
-    Display() Display
-    X() int
-    Y() int
-    Z() int
-    W() int
-    DX() int
-    DY() int
-    DZ() int
-    DW() int
-    Pressure() float32
-}
 
-type MouseWarped interface {
+type Mouse interface {
     Event
     Display() Display
     X() int
@@ -128,50 +183,10 @@ type MouseWarped interface {
     DY() int
     DZ() int
     DW() int
-    Pressure() float32
-}
-
-type MouseButtonPress interface {
-    Event
-    Display() Display
-    X() int
-    Y() int
-    Z() int
-    W() int
-    Button() int
-    Pressure() float32
-}
-
-type MouseButtonRelease interface {
-    Event
-    Display() Display
-    X() int
-    Y() int
-    Z() int
-    W() int
     Button() int
     Pressure() float32
 }
 
-type MouseEnterDisplay interface {
-    Event
-    Display() Display
-    X() int
-    Y() int
-    Z() int
-    W() int
-}
-
-type MouseLeaveDisplay interface {
-    Event
-    Display() Display
-    X() int
-    Y() int
-    Z() int
-    W() int
-}
-
-
 type Touch interface {
     Event
     Display() Display
@@ -183,26 +198,11 @@ type Touch interface {
     Primary() bool
 }
 
-type TouchBegin interface {
-    Touch
-}
-
-
-type TouchEnd interface {
-    Touch
-}
-
-type TouchMove interface {
-    Touch
-}
-
-type TouchCancel interface {
-    Touch
-}
-
-
 type Result bool
 
+const Done    = Result(true)
+const Pass    = Result(false)
+
 type UIHandler interface {
     Update(ev Update) Result
     Resize(ev Resize) Result
@@ -213,32 +213,32 @@ type UIHandler interface {
     NewChild(ev NewChild) Result
 }
 
-type JoyHandler interface {
-    JoyButtonPress(ev JoyButtonPress) Result
-    JoyButtonRelease(ev JoyButtonRelease) Result
-    JoyAxis(ev JoyAxis) Result
+type JoystickHandler interface {
+    JoystickButtonPress(ev Joystick) Result
+    JoystickButtonRelease(ev Joystick) Result
+    JoystickAxis(ev Joystick) Result
 }
 
 type KeyHandler interface {
-    KeyPress(ev KeyPress) Result
-    KeyRelease(ev KeyRelease) Result
-    KeyChar(ev KeyChar) Result
+    KeyPress(ev Key) Result
+    KeyRelease(ev Key) Result
+    KeyChar(ev Key) Result
 }
 
 type MouseHandler interface {
-    MouseAxes(ev MouseAxes) Result
-    MouseWarped(ev MouseWarped) Result
-    MouseButtonPress(ev MouseButtonPress) Result
-    MouseButtonRelease(ev MouseButtonRelease) Result
-    MouseEnterDisplay(ev MouseEnterDisplay) Result
-    MouseLeaveDisplay(ev MouseLeaveDisplay) Result
+    MouseAxes(ev Mouse) Result
+    MouseWarped(ev Mouse) Result
+    MouseButtonPress(ev Mouse) Result
+    MouseButtonRelease(ev Mouse) Result
+    MouseEnterDisplay(ev Mouse) Result
+    MouseLeaveDisplay(ev Mouse) Result
 }
 
 type TouchHandler interface {
-    TouchBegin(ev TouchBegin) Result
-    TouchEnd(ev TouchEnd) Result
-    TouchMove(ev TouchMove) Result
-    TouchCancel(ev TouchCancel) Result    
+    TouchBegin(ev Touch) Result
+    TouchEnd(ev Touch) Result
+    TouchMove(ev Touch) Result
+    TouchCancel(ev Touch) Result
 }
 
 
@@ -246,7 +246,7 @@ type TouchHandler interface {
  * of Zori's events. */
 type Handler interface {
     UIHandler
-    JoyHandler
+    JoystickHandler
     KeyHandler
     MouseHandler
     TouchHandler
@@ -254,31 +254,82 @@ type Handler interface {
 }
 
 
+/* Dispatches joystick events. */
+func DispatchJoystick(handler JoystickHandler, de Joystick) Result {
+    switch de.Type() {
+        case TypeJoystickButtonDown:    return handler.JoystickButtonPress(de)
+        case TypeJoystickButtonUp:      return handler.JoystickButtonRelease(de)
+        case TypeJoystickAxis:          return handler.JoystickAxis(de)
+        default:                        return Pass
+    }
+}
+
+/* Dispatches key events. */
+func DispatchKey(handler KeyHandler, de Key) Result {
+    switch de.Type() {
+        case TypeKeyDown:    return handler.KeyPress(de)
+        case TypeKeyUp:      return handler.KeyRelease(de)
+        case TypeKeyChar:    return handler.KeyChar(de)
+        default:             return Pass
+    }
+}
+
+
+/* Dispatches mouse events. */
+func DispatchMouse(handler MouseHandler, de Mouse) Result {
+    switch de.Type() {
+        case TypeMouseAxes:             return handler.MouseAxes(de)
+        case TypeMouseWarped:           return handler.MouseWarped(de)
+        case TypeMouseButtonDown:       return handler.MouseButtonPress(de)
+        case TypeMouseButtonUp:         return handler.MouseButtonRelease(de)
+        case TypeMouseEnterDisplay:     return handler.MouseEnterDisplay(de)
+        case TypeMouseLeaveDisplay:     return handler.MouseLeaveDisplay(de)
+        default:                        return Pass
+    }
+}
+
+
+/* Dispatches touch events. */
+func DispatchTouch(handler TouchHandler, de Touch) Result {
+    switch de.Type() {
+        case TypeTouchBegin:        return handler.TouchBegin(de)
+        case TypeTouchEnd:          return handler.TouchEnd(de)
+        case TypeTouchMove:         return handler.TouchMove(de)
+        case TypeTouchCancel:       return handler.TouchCancel(de)
+        default:                    return Pass
+    }
+}
+
+
 func Dispatch(event Event, handler Handler) Result {
     switch de := event.(type) {
-    case Update:             return handler.Update(de)
-    case Draw:               return handler.Draw(de)
-    case Destroy:            return handler.Destroy(de)
-    case Action:             return handler.Action(de)
-    case Close:              return handler.Close(de)
-    case NewChild:           return handler.NewChild(de)
-    case JoyButtonPress:     return handler.JoyButtonPress(de)
-    case JoyButtonRelease:   return handler.JoyButtonRelease(de)
-    case JoyAxis:            return handler.JoyAxis(de)
-    case KeyPress:           return handler.KeyPress(de)
-    case KeyRelease:         return handler.KeyRelease(de)
-    case KeyChar:            return handler.KeyChar(de)
-    case MouseAxes:          return handler.MouseAxes(de)
-    case MouseWarped:        return handler.MouseWarped(de)
-    case MouseButtonPress:   return handler.MouseButtonPress(de)
-    case MouseButtonRelease: return handler.MouseButtonRelease(de)
-    case MouseEnterDisplay:  return handler.MouseEnterDisplay(de)
-    case MouseLeaveDisplay:  return handler.MouseLeaveDisplay(de)
-    case TouchBegin:         return handler.TouchBegin(de)
-    case TouchEnd:           return handler.TouchEnd(de)
-    case TouchMove:          return handler.TouchMove(de)
-    case TouchCancel:        return handler.TouchCancel(de)            
-    default:                 return handler.Default(event)
+        case Update:                return handler.Update(de)
+        case Draw:                  return handler.Draw(de)
+        case Destroy:               return handler.Destroy(de)
+        case Action:                return handler.Action(de)
+        case Close:                 return handler.Close(de)
+        case NewChild:              return handler.NewChild(de)
+        case Joystick:              return DispatchJoystick(handler, de)
+        case Key:                   return DispatchKey(handler, de)
+        case Mouse:                 return DispatchMouse(handler, de)
+        case Touch:                 return DispatchTouch(handler, de)
+        default:                    return handler.Default(event)
+    }
+}
+
+func String(event Event) string {
+    switch event.(type) {
+        case Update:             return "Update"
+        case Draw:               return "Draw"
+        case Destroy:            return "Destroy"
+        case Action:             return "Action"
+        case Close:              return "Close"
+        case NewChild:           return "NewChild"
+        case Joystick:           return "Joystick"
+        case Key:                return "Key"
+        case Mouse:              return "Mouse"
+        case Touch:              return "Touch"
+        default:                 return "DefaultEvent"
     }
 }
 

+ 182 - 0
zori/keycode/keycode.go

@@ -0,0 +1,182 @@
+package keycode
+
+// Keyboard constants for use with Zori
+// Your backend should map it's key codes to these.
+const (
+/* Note: these values are deliberately the same as in Allegro */
+   KEY_A        = 1
+   KEY_B        = 2
+   KEY_C        = 3
+   KEY_D        = 4
+   KEY_E        = 5
+   KEY_F        = 6
+   KEY_G        = 7
+   KEY_H        = 8
+   KEY_I        = 9
+   KEY_J        = 10
+   KEY_K        = 11
+   KEY_L        = 12
+   KEY_M        = 13
+   KEY_N        = 14
+   KEY_O        = 15
+   KEY_P        = 16
+   KEY_Q        = 17
+   KEY_R        = 18
+   KEY_S        = 19
+   KEY_T        = 20
+   KEY_U        = 21
+   KEY_V        = 22
+   KEY_W        = 23
+   KEY_X        = 24
+   KEY_Y        = 25
+   KEY_Z        = 26
+
+   KEY_0        = 27
+   KEY_1        = 28
+   KEY_2        = 29
+   KEY_3        = 30
+   KEY_4        = 31
+   KEY_5        = 32
+   KEY_6        = 33
+   KEY_7        = 34
+   KEY_8        = 35
+   KEY_9        = 36
+
+   KEY_PAD_0        = 37
+   KEY_PAD_1        = 38
+   KEY_PAD_2        = 39
+   KEY_PAD_3        = 40
+   KEY_PAD_4        = 41
+   KEY_PAD_5        = 42
+   KEY_PAD_6        = 43
+   KEY_PAD_7        = 44
+   KEY_PAD_8        = 45
+   KEY_PAD_9        = 46
+
+   KEY_F1       = 47
+   KEY_F2       = 48
+   KEY_F3       = 49
+   KEY_F4       = 50
+   KEY_F5       = 51
+   KEY_F6       = 52
+   KEY_F7       = 53
+   KEY_F8       = 54
+   KEY_F9       = 55
+   KEY_F10      = 56
+   KEY_F11      = 57
+   KEY_F12      = 58
+
+   KEY_ESCAPE   = 59
+   KEY_TILDE        = 60
+   KEY_MINUS        = 61
+   KEY_EQUALS   = 62
+   KEY_BACKSPACE    = 63
+   KEY_TAB      = 64
+   KEY_OPENBRACE    = 65
+   KEY_CLOSEBRACE   = 66
+   KEY_ENTER        = 67
+   KEY_SEMICOLON    = 68
+   KEY_QUOTE        = 69
+   KEY_BACKSLASH    = 70
+   KEY_BACKSLASH2   = 71 
+   KEY_COMMA        = 72
+   KEY_FULLSTOP = 73
+   KEY_SLASH        = 74
+   KEY_SPACE        = 75
+
+   KEY_INSERT   = 76
+   KEY_DELETE   = 77
+   KEY_HOME     = 78
+   KEY_END      = 79
+   KEY_PGUP     = 80
+   KEY_PGDN     = 81
+   KEY_LEFT     = 82
+   KEY_RIGHT        = 83
+   KEY_UP       = 84
+   KEY_DOWN     = 85
+
+   KEY_PAD_SLASH    = 86
+   KEY_PAD_ASTERISK = 87
+   KEY_PAD_MINUS    = 88
+   KEY_PAD_PLUS = 89
+   KEY_PAD_DELETE   = 90
+   KEY_PAD_ENTER    = 91
+
+   KEY_PRINTSCREEN  = 92
+   KEY_PAUSE        = 93
+
+   KEY_ABNT_C1  = 94
+   KEY_YEN      = 95
+   KEY_KANA     = 96
+   KEY_CONVERT  = 97
+   KEY_NOCONVERT    = 98
+   KEY_AT       = 99
+   KEY_CIRCUMFLEX   = 100
+   KEY_COLON2   = 101
+   KEY_KANJI        = 102
+   KEY_PAD_EQUALS   = 103 
+   KEY_BACKQUOTE    = 104 
+   KEY_SEMICOLON2   = 105 
+   KEY_COMMAND  = 106  
+   
+   KEY_BACK = 107       
+   KEY_VOLUME_UP = 108
+   KEY_VOLUME_DOWN = 109
+   KEY_SEARCH       = 110
+   KEY_DPAD_CENTER  = 111
+   KEY_BUTTON_X     = 112
+   KEY_BUTTON_Y     = 113
+   KEY_DPAD_UP      = 114
+   KEY_DPAD_DOWN    = 115
+   KEY_DPAD_LEFT    = 116
+   KEY_DPAD_RIGHT   = 117
+   KEY_SELECT       = 118
+   KEY_START        = 119
+   KEY_BUTTON_L1    = 120
+   KEY_BUTTON_R1    = 121
+   KEY_BUTTON_L2    = 122
+   KEY_BUTTON_R2    = 123
+   KEY_BUTTON_A     = 124
+   KEY_BUTTON_B     = 125
+   KEY_THUMBL       = 126
+   KEY_THUMBR       = 127   
+   KEY_UNKNOWN      = 128
+
+   KEY_MODIFIERS    = 215
+   KEY_LSHIFT   = 215
+   KEY_RSHIFT   = 216
+   KEY_LCTRL    = 217
+   KEY_RCTRL    = 218
+   KEY_ALT      = 219
+   KEY_ALTGR    = 220
+   KEY_LWIN     = 221
+   KEY_RWIN     = 222
+   KEY_MENU     = 223
+   KEY_SCROLLLOCK = 224
+   KEY_NUMLOCK  = 225
+   KEY_CAPSLOCK = 226
+
+   KEY_MAX
+)
+
+
+
+const(
+   KEYMOD_SHIFT       = 0x00001
+   KEYMOD_CTRL        = 0x00002
+   KEYMOD_ALT         = 0x00004
+   KEYMOD_LWIN        = 0x00008
+   KEYMOD_RWIN        = 0x00010
+   KEYMOD_MENU        = 0x00020
+   KEYMOD_ALTGR       = 0x00040
+   KEYMOD_COMMAND     = 0x00080
+   KEYMOD_SCROLLLOCK  = 0x00100
+   KEYMOD_NUMLOCK     = 0x00200
+   KEYMOD_CAPSLOCK    = 0x00400
+   KEYMOD_INALTSEQ   = 0x00800
+   KEYMOD_ACCENT1     = 0x01000
+   KEYMOD_ACCENT2     = 0x02000
+   KEYMOD_ACCENT3     = 0x04000
+   KEYMOD_ACCENT4     = 0x08000
+)
+

+ 7 - 7
zori/style/style.go

@@ -44,7 +44,7 @@ const (
  * white-space: white space display.
  * list-style: list styling with type, image and position of the image.
  *
- * Squeezing it down to the essential, we retain:
+ * Squeezing it down to an essential subset we can retain the following:
  *
  * text.font    : Font for texts.
  * text.color   : Color for texts.
@@ -84,7 +84,7 @@ const (
  */
 
 type Basic struct {
-    Color
+    Color Color
     Bitmap
     Flag
     Radius int
@@ -117,16 +117,16 @@ type Theme struct {
 }
 
 type Themed interface {
-    Theme() Theme
+    Theme() * Theme
 }
 
 
-type BasicThemed struct {
-    Looks Theme
+type BasicTheme struct {
+    MyTheme Theme
 }
 
-func (bs BasicThemed) Theme() * Theme {
-    return & bs.Looks
+func (bs BasicTheme) Theme() * Theme {
+    return & bs.MyTheme
 }
 
 

+ 10 - 4
zori/types/types.go

@@ -37,7 +37,7 @@ func (re Box) Width() int {
 }
 
 func (re Box) Height() int {
-    return re.W
+    return re.H
 }
 
 type Bitmap interface {
@@ -46,9 +46,10 @@ type Bitmap interface {
 }
 
 type Font interface {
-    Width(string) int
-    Height(string) int
-    Draw(x, y int, color Color, text string)
+    TextWidth(text string) int 
+    LineHeight() int
+    Ascent() int
+    Descent() int
     Close()
 }
 
@@ -57,3 +58,8 @@ type Display interface {
 }
 
 
+func Bounds(x,y, w, h int) Box {
+    return Box{Point{x, y}, w, h}
+}
+
+

+ 463 - 40
zori/widget/basic.go

@@ -2,57 +2,153 @@ package widget
 
 import "fmt"
 import . "gitlab.com/beoran/ebsgo/zori/types"
-// import _ "gitlab.com/beoran/ebsgo/zori/backend"
+import "gitlab.com/beoran/ebsgo/zori/backend"
 import "gitlab.com/beoran/ebsgo/zori/state"
 import  "gitlab.com/beoran/ebsgo/zori/style"
 import "gitlab.com/beoran/ebsgo/zori/event"
+import "gitlab.com/beoran/ebsgo/monolog"
 
+var LastID = 0
+
+type ActionCallback func (w Widget, data ... interface{}) event.Result
 
 type Basic struct {
-    ID    int
-    Root  Widget
+    Be          backend.Backend
+    ID          int
+    MyRoot      RootWidget
     Box
-    Outer Box
-    Inner Box
-    Z     int
-    style.BasicThemed
-    Parent   Widget
-    Children []Widget
+    Outer       Box
+    Inner       Box
+    Z           int
+    style.BasicTheme
+    MyParent    Widget
+    MyChildren []Widget
     state.Flag
-    Result  interface{}
-}
-                                                                        
-func (bw *Basic) Update(ev event.Update) event.Result                         { fmt.Println("Update"); return event.Result(false); }
-func (bw *Basic) Draw(ev event.Draw) event.Result                             { fmt.Println("Draw"); return event.Result(false); }
-func (bw *Basic) Resize(ev event.Resize) event.Result                             { fmt.Println("Resize"); return event.Result(false); }
-func (bw *Basic) Destroy(ev event.Destroy) event.Result                       { fmt.Println("Destroy"); return event.Result(false); }
-func (bw *Basic) Action(ev event.Action) event.Result                         { fmt.Println("Action"); return event.Result(false); }
-func (bw *Basic) Close(ev event.Close) event.Result                           { fmt.Println("Close"); return event.Result(false); }
-func (bw *Basic) NewChild(ev event.NewChild) event.Result                     { fmt.Println("Child"); return event.Result(false); }
-func (bw *Basic) JoyButtonPress(ev event.JoyButtonPress) event.Result         { fmt.Println("JBP"); return event.Result(false); }
-func (bw *Basic) JoyButtonRelease(ev event.JoyButtonRelease) event.Result     { fmt.Println("JBR"); return event.Result(false); }
-func (bw *Basic) JoyAxis(ev event.JoyAxis) event.Result                       { fmt.Println("JAX"); return event.Result(false); }
-func (bw *Basic) KeyPress(ev event.KeyPress) event.Result                     { fmt.Println("KEP"); return event.Result(false); }
-func (bw *Basic) KeyRelease(ev event.KeyRelease) event.Result                 { fmt.Println("KER"); return event.Result(false); }
-func (bw *Basic) MouseAxes(ev event.MouseAxes) event.Result                   { fmt.Println("MAX"); return event.Result(false); }
-func (bw *Basic) MouseWarped(ev event.MouseWarped) event.Result               { fmt.Println("MWA"); return event.Result(false); }
-func (bw *Basic) MouseButtonPress(ev event.MouseButtonPress) event.Result     { fmt.Println("MBP"); return event.Result(false); }
-func (bw *Basic) MouseButtonRelease(ev event.MouseButtonRelease) event.Result { fmt.Println("MBR"); return event.Result(false); }
-func (bw *Basic) MouseEnterDisplay(ev event.MouseEnterDisplay) event.Result   { fmt.Println("MED"); return event.Result(false); }
-func (bw *Basic) MouseLeaveDisplay(ev event.MouseLeaveDisplay) event.Result   { fmt.Println("MLD"); return event.Result(false); }
-func (bw *Basic) TouchBegin(ev event.TouchBegin) event.Result                 { fmt.Println("TB"); return event.Result(false); }
-func (bw *Basic) TouchEnd(ev event.TouchEnd) event.Result                     { fmt.Println("TE"); return event.Result(false); }
-func (bw *Basic) TouchMove(ev event.TouchMove) event.Result                   { fmt.Println("TM"); return event.Result(false); }
-func (bw *Basic) TouchCancel(ev event.TouchCancel) event.Result               { fmt.Println("TC"); return event.Result(false); }
+    Result      interface{}
+    ActionCallback        
+}
+
+func (b * Basic) SetBounds(bounds * Box) {
+    b.Box.X   = bounds.X
+    b.Box.Y   = bounds.Y
+    b.Box.W   = bounds.W
+    b.Box.H   = bounds.H
+    b.Outer   = b.Box
+    b.Inner   = b.Box
+}
+
+
+func NewBasicWithTheme(parent Widget, bounds * Box, theme * style.Theme, cb ActionCallback) * Basic {
+    b := &Basic{}
+    
+    LastID++
+    b.ID = LastID
+    
+    if parent != nil {
+        b.Be        = parent.Backend()
+        if parent.Theme() != nil {
+            b.MyTheme   = * parent.Theme()
+        }
+        
+        if root, ok := parent.(RootWidget) ; ok {
+            b.MyRoot = root
+        } else {
+            b.MyRoot    = parent.Root()
+        }
+        
+        b.MyParent  = parent
+        box := Bounds(parent.Top(), parent.Left(), parent.Width(), parent.Height())
+        b.SetBounds(&box)
+    }
+    
+    if bounds != nil {
+        b.SetBounds(bounds)
+    }   
+    
+    if theme != nil {
+        b.MyTheme = *theme
+    }
+    
+    if cb != nil {
+        b.ActionCallback = cb
+    }
+
+    return b;
+}
+
+func NewBasic(parent Widget, bounds * Box, cb ActionCallback) * Basic {
+    return NewBasicWithTheme(parent, bounds, nil, cb)
+}
+
+func NewBasicWidgetWithTheme(parent Widget, bounds * Box, theme * style.Theme, cb ActionCallback) Widget {
+    return Widget(NewBasicWidgetWithTheme(parent, bounds, theme, cb));
+}
+
+func NewBasicWidget(parent Widget, bounds * Box, cb ActionCallback) Widget {
+    return Widget(NewBasicWidget(parent, bounds, cb));
+}
+
+func (b Basic) Backend() backend.Backend {
+    return b.Be
+}
+
+func (b Basic) Parent() Widget {
+    return b.MyParent
+}
+
+func (b Basic) Root() RootWidget {
+    return b.MyRoot
+}
+
+func (b Basic) Children() []Widget {
+    return b.MyChildren
+}
+
+
+func (b * Basic) AddChild(widget Widget) Widget {
+    b.MyChildren = append(b.MyChildren, widget)
+    return widget
+}
+
+func (bw *Basic) Action(ev event.Action) event.Result { 
+    monolog.Info("Action"); 
+    if bw.ActionCallback != nil {
+        return bw.ActionCallback(bw, ev.Data)
+    }
+    return event.Pass; 
+}
+
+                                                                       
+func (bw *Basic) Update(ev event.Update) event.Result                   { monolog.Info("Update"); return event.Pass; }
+func (bw *Basic) Draw(ev event.Draw) event.Result                       { monolog.Info("Draw"); return event.Pass; }
+func (bw *Basic) Resize(ev event.Resize) event.Result                   { monolog.Info("Resize"); return event.Pass; }
+func (bw *Basic) Destroy(ev event.Destroy) event.Result                 { monolog.Info("Destroy"); return event.Pass; }
+func (bw *Basic) Close(ev event.Close) event.Result                     { monolog.Info("Close"); return event.Pass; }
+func (bw *Basic) NewChild(ev event.NewChild) event.Result               { monolog.Info("Child"); return event.Pass; }
+func (bw *Basic) JoystickButtonPress(ev event.Joystick) event.Result    { monolog.Info("JBP"); return event.Pass; }
+func (bw *Basic) JoystickButtonRelease(ev event.Joystick) event.Result  { monolog.Info("JBR"); return event.Pass; }
+func (bw *Basic) JoystickAxis(ev event.Joystick) event.Result           { monolog.Info("JAX"); return event.Pass; }
+func (bw *Basic) KeyPress(ev event.Key) event.Result                    { monolog.Info("KEP"); return event.Pass; }
+func (bw *Basic) KeyRelease(ev event.Key) event.Result                  { monolog.Info("KER"); return event.Pass; }
+func (bw *Basic) MouseAxes(ev event.Mouse) event.Result                 { monolog.Info("MAX"); return event.Pass; }
+func (bw *Basic) MouseWarped(ev event.Mouse) event.Result               { monolog.Info("MWA"); return event.Pass; }
+func (bw *Basic) MouseButtonPress(ev event.Mouse) event.Result          { monolog.Info("MBP"); return event.Pass; }
+func (bw *Basic) MouseButtonRelease(ev event.Mouse) event.Result        { monolog.Info("MBR"); return event.Pass; }
+func (bw *Basic) MouseEnterDisplay(ev event.Mouse) event.Result         { monolog.Info("MED"); return event.Pass; }
+func (bw *Basic) MouseLeaveDisplay(ev event.Mouse) event.Result         { monolog.Info("MLD"); return event.Pass; }
+func (bw *Basic) TouchBegin(ev event.Touch) event.Result                { monolog.Info("TB"); return event.Pass; }
+func (bw *Basic) TouchEnd(ev event.Touch) event.Result                  { monolog.Info("TE"); return event.Pass; }
+func (bw *Basic) TouchMove(ev event.Touch) event.Result                 { monolog.Info("TM"); return event.Pass; }
+func (bw *Basic) TouchCancel(ev event.Touch) event.Result               { monolog.Info("TC"); return event.Pass; }
 
 func (bw *Basic) Default(ev event.Event) event.Result { 
-    fmt.Println("Warning using default event handler!"); 
-    return event.Result(false); 
+    monolog.Warning("Warning using default event handler for type %d %s!", ev.Type(), event.String(ev));
+    return event.Pass;
 }
 
-func (bw *Basic) KeyChar(kc event.KeyChar) event.Result {
-    fmt.Println("KeyChar of Basic")
-    return event.Result(true)
+func (bw *Basic) KeyChar(kc event.Key) event.Result {
+    monolog.Info("KeyChar of Basic")
+    return event.Done
 }
 
 // Checks if the coordinates are inside the main rectange of the widget.
@@ -60,7 +156,334 @@ func (bw *Basic) IsInside(x, y int) bool {
     return (x >= bw.X) && (x <= bw.X + bw.W) && (y >= bw.Y) && (y <= (bw.Y + bw.H))
 }
 
+func (bw * Basic) String() string {
+    return fmt.Sprintf("widget %d", bw.ID)
+}
+
+func DispatchRecursive(bw Widget, ev event.Event) event.Result {
+    res := event.Dispatch(ev, bw)
+    dlev := "INFO"
+    switch ev.(type) { 
+        case event.Draw: dlev = "DEBUG"
+        case event.Update: dlev = "DEBUG"
+        case event.Mouse: if ev.Type() == event.TypeMouseAxes { dlev = "DEBUG" ; }
+        default: break
+    }
+    monolog.Log(dlev, "Parent dispatch: %s -> %s", event.String(ev), bw.String())
+
+    if event.Done != res {
+        one_ok := false
+        for _ , child := range bw.Children() {
+            monolog.Log(dlev, "Child dispatch: %v -> %s", child, event.String(ev))
+            res := event.Dispatch(ev, child)
+            if res == event.Done {
+                one_ok = true
+            }
+        }
+        if one_ok {
+            return event.Done
+        } else {
+            return event.Pass
+        }        
+    } else {
+        return event.Done
+    }
+}
+
 func (bw * Basic) Dispatch(ev event.Event) event.Result {
-    return event.Dispatch(ev, bw)
+    return DispatchRecursive(bw, ev)
+}
+
+
+/** Returns whether or not a widget will even handle an event in the first place.
+ * For example, a hidden widget won't draw, and a disabled widget won't 
+ * accept any system events, and a NULL widget doesn't accept events at all.. */
+func (bw * Basic) AcceptsEvent(ev event.Event) bool {
+    switch ev.(type) {
+        case event.Draw: return bw.Visible()
+        default: return bw.Active()
+    }
+}
+
+func (bw * Basic) Priority() int {
+    return bw.Z
+}
+
+func (bw * Basic) Compare(w Widget) int {
+    return (w.Priority() - bw.Priority())
+}
+
+
+
+/* Gets the theme to use for a marked widget. Looks up a root widget and then 
+ * looks for the cursor to determine this. */
+func (bw * Basic) MarkedTheme() style.Theme {
+    return bw.Root().MarkedTheme()
+}
+
+/* Gets the style to use for a hovered widget. Looks up a screen widget and then 
+ * looks for the cursor t determine this. */
+func (bw * Basic) HoveredTheme() style.Theme {
+    return bw.Root().HoveredTheme()
+}
+
+
+/* Sets the widget's margins */
+func (bw * Basic) SetMargins(left, top, right, bottom int) * Basic {
+    bw.Outer = bw.Box
+    bw.Outer.X -=  left
+    bw.Outer.Y -=  top
+    bw.Outer.W +=  (left+ right)
+    bw.Outer.H +=  (top + bottom)
+    return bw
+}
+
+/* Sets the widget's margins, all equal */
+func (bw * Basic) SetMargin(size int) * Basic {
+    return bw.SetMargins(size, size,  size,  size)
+}
+
+/* Sets the widget's paddins */
+func (bw * Basic) SetPaddings(left, top, right, bottom int) * Basic {
+    bw.Inner   = bw.Box
+    bw.Inner.X +=  left
+    bw.Inner.Y +=  top
+    bw.Inner.W -=  (left+ right)
+    bw.Inner.H -=  (top + bottom)
+    return bw
+}
+
+/* Sets the widget's paddings, all equal */
+func (bw * Basic) SetPadding(size int) * Basic {
+    return bw.SetPaddings(size, size,  size,  size)
+}
+
+
+/* Draws a widget's frame based on it's style. If the background bitmap is set 
+ * then that is used, otherwise,  the background color is used, including a border
+ * if needed. */
+func (bw * Basic) DrawBackground() { 
+    be := bw.Backend()
+    dx := bw.X
+    dy := bw.Y
+    dw := bw.W
+    dh := bw.H
+    
+    theme := bw.MyTheme
+        
+    if bw.Hovered() {
+        theme = bw.HoveredTheme()
+    } else if bw.Marked() {
+        theme = bw.MarkedTheme()
+    }
+    
+    if theme.Background.Bitmap != nil {
+        bmp := theme.Background.Bitmap
+        sw  := bmp.Width()
+        sh  := bmp.Height()
+        // XXX should use draw9 algorithm here
+        be.DrawScaledBitmap(0,0,sw,sh,dx,dy,dw,dh, bmp)
+    } else { 
+        fillcol := theme.Background.Color
+        bordcol := theme.Border.Color
+        thick   := theme.Border.Size
+        rad     := theme.Border.Radius
+        be.DrawRoundedSlab(dx, dy, dw, dh, rad, rad, fillcol)
+        be.DrawRoundedRectangle(dx, dy, dw, dh, rad, rad, bordcol, thick)
+    }
 }
 
+func (bw * Basic) SetState(flag state.Flag, set bool) bool {
+    if set {
+        bw.Flag |= flag        
+    } else {
+        bw.Flag &= (^flag)
+    }
+    return bw.IsState(flag)
+}
+
+func (bw * Basic) IsState(flag state.Flag) bool { 
+    return ((bw.Flag & flag) == flag)
+}
+
+func (bw * Basic) Visible() bool {
+    return bw != nil && ((bw.Flag & state.HIDDEN) != state.HIDDEN)
+}
+
+func (bw * Basic) Active() bool {
+    return bw != nil && ((bw.Flag & state.DISABLED) != state.DISABLED )
+}
+
+func (bw * Basic) Hovered() bool {
+    return bw != nil && bw.IsState(state.HOVERED)
+}
+
+func (bw * Basic) Marked() bool {
+    return bw != nil && bw.IsState(state.MARKED)
+}
+
+func (bw * Basic) SetHovered(set bool) bool {
+   return bw.SetState(state.HOVERED, set)
+}
+
+func (bw * Basic) SetMarked(set bool) bool {
+    return bw.SetState(state.MARKED, set)
+}
+
+func (bw * Basic) SetActive(set bool) bool {
+    bw.SetState(state.DISABLED, !set)
+    return bw.Active()
+}
+
+func (bw * Basic) SetVisible(set bool) bool {
+    bw.SetState(state.HIDDEN, !set)
+    return bw.Visible()
+}
+
+/** Get active and visible */
+func (bw * Basic) Live() bool {
+    return bw.Active() && bw.Visible()
+}
+
+/** Set active and visible */
+func (bw * Basic) SetLive(live bool) {
+    bw.SetActive(live)
+    bw.SetVisible(live)
+}
+
+
+func (bw * Basic) FindParent(predicate func (wi Widget) bool) Widget {    
+    for parent := bw.Parent() ; parent != nil ; parent = parent.Parent() {
+        if predicate(parent) {
+            return parent
+        }
+    }
+    return nil    
+}
+
+func (bw * Basic) Mark() event.Result {
+    root := bw.Root().(*Root)
+    root.Cursors.Keyjoy.Point = bw.Box.Point
+    return event.Done
+}
+
+/* Moves the widget by the offset (dx, dy). Does not move it's children.  */
+func (bw * Basic) MoveSelfBy(dx, dy int) {
+    bw.Box.X += dx
+    bw.Box.Y += dy
+    bw.Inner.X += dx
+    bw.Inner.Y += dy
+    bw.Outer.X += dx
+    bw.Outer.Y += dy
+}
+
+
+/* Resizes the widget by the offset (dw, dh). Does not move it's children.  */
+func (bw * Basic) ResizeSelfBy(dw, dh int) {
+    bw.Box.W += dw
+    bw.Box.H += dh
+    bw.Inner.W += dw
+    bw.Inner.H += dh
+    bw.Outer.W += dw
+    bw.Outer.H += dh
+}
+
+
+/* Moves the widget by the offset (dx, dy). It's children will be moved as well recursively
+ * keeping the offset */
+func (bw * Basic) MoveBy(dx, dy int) {
+    bw.MoveSelfBy(dx, dy)
+    for _, child := range bw.MyChildren {
+        child.MoveBy(dx, dy)
+    }
+}
+
+
+/* Moves the widget to (x, y). It's children will be moved as well recursively
+ * keeping the offset */
+func (bw * Basic) MoveTo(x, y int) {
+    dx := bw.Box.X - x
+    dy := bw.Box.Y - y
+    bw.MoveBy(dx, dy)
+}
+
+/* Moves the widget to (x, y). Doesn't move children. */
+func (bw * Basic) MoveSelfTo(x, y int) {
+    dx := bw.Box.X - x
+    dy := bw.Box.Y - y
+    bw.MoveSelfBy(dx, dy)
+}
+
+/* Resizes the widget to (w, h). Doesn't resize children. Margins and padding will be 
+ * resized accordingly. */
+func (bw * Basic) ResizeSelfTo(w, h int) {
+    dx := bw.Box.W - w
+    dy := bw.Box.H - h
+    bw.ResizeSelfBy(dx, dy)
+}
+ 
+
+    
+/* Makes the widget fit itself to all direct children.
+ * Does not resize nor move the children.
+ */
+func (bw * Basic) FitToChildren(w, h int) {
+    min_x := 640
+    min_y := 480
+    min_w := 0
+    min_h := 0
+    be    := bw.Backend()
+    
+    if be.Display() != nil {
+        min_x = be.Display().Width()  
+        min_y = be.Display().Height()          
+    }
+    
+
+    for _,child := range bw.MyChildren {
+        if child.Left() < min_x {
+            min_x = child.Left()
+        }        
+        if child.Top() < min_y {
+            min_y = child.Top()
+        }
+        if child.Width() > min_w {
+            min_w = child.Width()
+        }
+        if child.Height() > min_h {
+            min_w = child.Height()
+        }
+    }
+    
+    bw.MoveSelfTo(min_x, min_y)
+    bw.ResizeSelfTo(min_w, min_h)
+}
+
+
+/* Style getters for ease of use. */
+func (bw * Basic) TextFont() Font {
+    theme := bw.MyTheme
+    if (theme.Text.Font != nil) { 
+        return theme.Text.Font
+    } 
+    
+    if bw.MyParent != nil {
+        return bw.Root().Theme().Text.Font
+    }
+    return nil
+}
+
+func (bw * Basic) TextColor() Color {
+    theme := bw.MyTheme
+    return theme.Text.Color
+}
+
+func (bw * Basic) BackgroundColor() Color {
+    theme := bw.MyTheme
+    return theme.Background.Color
+}
+
+
+
+
+

+ 59 - 3
zori/widget/button.go

@@ -1,14 +1,70 @@
 package widget
 
-// import . "gitlab.com/beoran/ebsgo/zori/types"
+import "gitlab.com/beoran/ebsgo/zori/types"
+import "gitlab.com/beoran/ebsgo/zori/event"
 // import _ "gitlab.com/beoran/ebsgo/zori/backend"
 // import "gitlab.com/beoran/ebsgo/zori/state"
 // import  "gitlab.com/beoran/ebsgo/zori/style"
-
+import  "gitlab.com/beoran/ebsgo/monolog"
 
 
 type Button struct {
-    Basic
+    * Captioned
+}
+
+
+/** Handles a mouse axis event and pass it on.  */
+func (bu * Button) MouseAxes(ev event.Mouse) event.Result {
+    if bu.IsInside(ev.X(), ev.Y()) {
+        monolog.Info("Button motion: %d %d.\n", ev.X(), ev.Y())
+        bu.SetHovered(true)
+    } else {
+        bu.SetHovered(false)
+    }    
+    return event.Pass
+}
+        
+ 
+/** Handles a mouse click or activation and set the 
+ * button and it's parent's result. */
+func (bu * Button) Action(ev event.Action) event.Result { 
+    bu.Result = interface{}(bu.ID)
+    monolog.Info("Button clicked: %d.\n", bu.ID)
+    
+    menu, is_menu := bu.Parent().(*Menu)
+    if is_menu {
+        /* If the parent is a menu, also set it's result. */
+        menu.Result = bu.ID
+    }    
+    /* Basic.Action will call the action callback. */
+    return bu.Basic.Action(ev)
+}
+
+/** Handles a mouse click event and pass it on.  */
+func (bu* Button) MouseButtonPress(ev event.Mouse) event.Result {
+    if bu.IsInside(ev.X(), ev.Y()) { 
+        monolog.Info("Mouse clicked: %v!\n", bu)
+        ae := event.Action{}        
+        return bu.Dispatch(ae)
+    } else {
+        monolog.Info("Mouse NOT clicked: %d %d in %d %d %d %d!\n", ev.X(), ev.Y(), bu.X, bu.Y, bu.W, bu.H )
+    }
+    return event.Pass
+}
+
+func (bu * Button) Dispatch(ev event.Event) event.Result {
+    return DispatchRecursive(bu, ev)
 }
 
+func (bu * Button) Draw(ev event.Draw) event.Result {    
+    bu.DrawBackground()
+    bu.Captioned.Draw(ev)
+    return event.Pass
+}
+
+func NewButton(parent Widget, bounds * types.Box, text string, cb ActionCallback) * Button {
+    but := &Button{}
+    but.Captioned = NewCaptioned(parent, bounds, text)
+    return but
+}
 

+ 37 - 4
zori/widget/caption.go

@@ -4,18 +4,51 @@ import . "gitlab.com/beoran/ebsgo/zori/types"
 // import _ "gitlab.com/beoran/ebsgo/zori/backend"
 // import "gitlab.com/beoran/ebsgo/zori/state"
 import  "gitlab.com/beoran/ebsgo/zori/style"
+import  "gitlab.com/beoran/ebsgo/zori/event"
+import  "gitlab.com/beoran/ebsgo/monolog"
 
 
 /* Caption helper struct for use by widgets that have captions. */
 type Caption struct {
-    Label string
-    style.Theme
-    Box
+    Label   string
+    LabelTheme style.Theme
+    LabelBox   Box
 }
 
 /* Widget with a caption. */
 type Captioned struct {
-    Basic
+    * Basic
     Caption
 }
 
+func (cap Captioned) Update(ev event.Update) event.Result {
+    return event.Pass
+}
+
+func (cap Captioned) Draw(ev event.Draw) event.Result {
+    them := cap.LabelTheme
+    font := them.Text.Font
+    colo := them.Text.Color
+    lh  := font.LineHeight()
+    y   := cap.Caption.LabelBox.X + lh
+    x   := cap.Caption.LabelBox.Y + 5 // Needs to use margin
+    cap.Be.DrawText(font, colo, x, y, cap.Label)
+    monolog.Debug("Draw Caption: %d %d %s", x, y, cap.Label)
+    return event.Pass
+}
+
+func NewCaptioned(parent Widget, bounds * Box, text string) * Captioned {
+    cap := &Captioned{}
+    cap.Basic = NewBasic(parent, bounds, nil)
+    cap.Caption.Label = text
+    cap.Caption.LabelTheme = * cap.Basic.Theme()
+    if bounds != nil {
+        cap.Caption.LabelBox = *bounds
+    }
+    return cap
+}
+
+
+
+
+

+ 159 - 8
zori/widget/console.go

@@ -1,7 +1,10 @@
 package widget
 
-import "fmt"
-// import . "gitlab.com/beoran/ebsgo/zori/types"
+// import "fmt"
+import "strings"
+import "gitlab.com/beoran/ebsgo/zori/keycode"
+
+import "gitlab.com/beoran/ebsgo/zori/types"
 // import _ "gitlab.com/beoran/ebsgo/zori/backend"
 // import "gitlab.com/beoran/ebsgo/zori/state"
 // import  "gitlab.com/beoran/ebsgo/zori/style"
@@ -9,22 +12,170 @@ import "fmt"
 import  "gitlab.com/beoran/ebsgo/zori/event"
 
 
+type ConsoleCommand func(cw *Console, command string, data interface{}) int
+
+const ConsoleLimit = 1000
+
 type Console struct {
-    Basic
+  * Basic
     Lines  []string
     Input  string
+    Position int
     Cursor int
+    Command ConsoleCommand
+    Data    interface{}
 }
 
-
 func (cw * Console) Dispatch(ev event.Event) event.Result {
     return event.Dispatch(ev, cw)
 }
 
 
-func (cw *Console) KeyChar(kc event.KeyChar) event.Result {
-    cw.Basic.KeyChar(kc)
-    fmt.Println("KeyChar of ConsoleWidget")
-    return event.Result(true)
+/** Let the console perform a command if possible. returns nonzero on error,
+zero if OK. */
+func (cw *Console) DoCommand(text string) int {
+    if cw.Command == nil {
+        return -1
+    }
+    return cw.Command(cw, text, cw.Data)
+}
+    
+/** Adds a line of text to the console. */
+func (cw * Console) AddLine(line string) {
+    cw.Lines = append(cw.Lines, line)
+    if len(cw.Lines) > ConsoleLimit {
+        cw.Lines[0] = ""
+        cw.Lines = cw.Lines[1:]
+    }
+}
+
+/** Puts a string on the console .*/
+func (cw * Console) Puts(text string) {    
+    font  := cw.TextFont()
+    // Assume a fixed width font for the console.
+    charw := font.TextWidth("W")
+    lines := strings.Split(text, "\n")
+    for _, line := range lines {
+        runes := []rune(line)
+        max_chars := cw.Inner.W / charw
+        for len(runes) > max_chars {
+            sub := string(runes[0:max_chars-1])
+            runes = runes[max_chars:]
+            cw.AddLine(sub)            
+        }        
+        cw.AddLine(string(runes))
+    }
+}
+
+
+/** Draws a console. */
+func (cw * Console) Draw(ev event.Draw) event.Result {
+    cw.DrawBackground()
+    font        := cw.TextFont()
+    colo        := cw.TextColor()
+    be          := cw.Backend()
+    linehigh    := font.LineHeight()
+    available   := (cw.Inner.H / linehigh - 1)
+    x           := cw.Inner.X
+    y           := cw.Inner.Y
+    start       := len(cw.Lines) - available - cw.Position
+    if start < 0 {
+        start = 0
+    } else if start >= len(cw.Lines) {
+        start = len(cw.Lines) - 1
+    }
+    
+    for index := start; index < len(cw.Lines) ; index ++ {
+        be.DrawText(font, colo, x, y, cw.Lines[index])
+        x += linehigh        
+    }
+    
+    // input = cw.Input[0:cw.Cursor-1] + "|" + cw.Input[cw.Cursor:]
+    // XXX draw cursor.
+    be.DrawText(font, colo, x, y, cw.Input)
+    return event.Pass
+}
+
+func (cw * Console) Scroll (dir int) event.Result {
+    cw.Position += dir
+    if cw.Position < 0 {
+        cw.Position = 0
+    } else if cw.Position >= len(cw.Lines) {
+        cw.Position = len(cw.Lines) - 1
+    }
+    return event.Done
+}
+
+
+func (cw * Console) Backspace() event.Result {
+    if len(cw.Input) > 0 {
+        runes := []rune(cw.Input)
+        runes = runes[0:len(runes)-1]    
+        cw.Input = string(runes)
+    }
+    return event.Done
 }
 
+
+/* Key input event handler for console. */
+func (cw *Console) KeyChar(ke event.Key) event.Result {
+  cw.Basic.KeyChar(ke)
+  ru := ke.Unichar()
+  kc := ke.KeyCode()
+  switch (kc) {
+    // ignore the start-console key
+    case keycode.KEY_F1: fallthrough
+    case keycode.KEY_F3: return event.Done
+    case keycode.KEY_PGUP: return cw.Scroll(1)
+    case keycode.KEY_PGDN: return cw.Scroll(-1)
+    case keycode.KEY_BACKSPACE: return cw.Backspace()
+    case keycode.KEY_ENTER: 
+      if cw.DoCommand(cw.Input) < 0 {
+            cw.Puts("Error executing command.");
+      }
+      cw.Input = ""
+      return event.Done
+    default:
+        cw.Input = string(append([]rune(cw.Input), ru))
+        return event.Done
+  }  
+}
+
+
+func (cw *Console) KeyPress(ke event.Key) event.Result {
+  cw.Basic.KeyPress(ke)
+  kc := ke.KeyCode()
+  switch (kc) {
+    // ignore the start-console key
+    case keycode.KEY_F1: fallthrough
+    case keycode.KEY_F3:  
+        cw.SetLive(false)
+    return event.Done
+    default:
+    return event.Pass
+  }
+}
+
+
+func (cw * Console) MouseAxes(ev event.Mouse) event.Result {
+    dz := ev.DZ()
+    if dz == 0 {    
+        return event.Pass
+    } else if dz < 0 {
+        return cw.Scroll(-1)
+    } else {
+        return cw.Scroll(+1)        
+    }
+}
+
+func NewConsole(parent Widget, bounds * types.Box, 
+com ConsoleCommand) * Console {
+    con := &Console{}
+    con.Basic = NewBasic(parent, bounds, nil)
+    con.Command = com
+    return con
+}
+
+
+
+

+ 3 - 3
zori/widget/dragger.go

@@ -23,7 +23,7 @@ type Dragger struct {
 }
 
 
-func (dr Dragger) MouseAxes(ev event.MouseAxes) event.Result {
+func (dr Dragger) MouseAxes(ev event.Mouse) event.Result {
     if dr.Draggable != nil {
         dr.Draggable.MouseAxes(ev)
         if dr.Draggable.CanDrag() && dr.Dragging {
@@ -34,7 +34,7 @@ func (dr Dragger) MouseAxes(ev event.MouseAxes) event.Result {
 }
 
 
-func (dr Dragger) MouseButtonPress(ev event.MouseButtonPress) event.Result {
+func (dr Dragger) MouseButtonPress(ev event.Mouse) event.Result {
     if dr.Draggable != nil {
         dr.Draggable.MouseButtonPress(ev)
         if (!dr.Draggable.IsInside(ev.X(), ev.Y()) || !dr.Draggable.CanDrag()) {
@@ -49,7 +49,7 @@ func (dr Dragger) MouseButtonPress(ev event.MouseButtonPress) event.Result {
     }
 }
 
-func (dr Dragger) MouseButtonRelease(ev event.MouseButtonRelease) event.Result {
+func (dr Dragger) MouseButtonRelease(ev event.Mouse) event.Result {
     if dr.Draggable != nil {
         dr.Draggable.MouseButtonRelease(ev)
         if (!dr.Dragging) {

+ 8 - 2
zori/widget/label.go

@@ -1,7 +1,7 @@
 package widget
 
 // import "gitlab.com/beoran/ebsgo/zori/event"
-// import . "gitlab.com/beoran/ebsgo/zori/types"
+import . "gitlab.com/beoran/ebsgo/zori/types"
 // import _ "gitlab.com/beoran/ebsgo/zori/backend"
 // import "gitlab.com/beoran/ebsgo/zori/state"
 // import  "gitlab.com/beoran/ebsgo/zori/style"
@@ -9,6 +9,12 @@ package widget
 
 /* Label widget. */
 type Label struct {
-    Captioned
+    * Captioned
+}
+
+func NewLabel(parent Widget, bounds * Box, text string) * Label {
+    lab := &Label{}
+    lab.Captioned = NewCaptioned(parent, bounds, text)
+    return lab
 }
 

+ 9 - 0
zori/widget/menu.go

@@ -0,0 +1,9 @@
+package widget
+
+
+type Menu struct {
+    Captioned
+    Selected int
+}
+
+

+ 58 - 5
zori/widget/root.go

@@ -2,9 +2,11 @@ package widget
 
 
 import . "gitlab.com/beoran/ebsgo/zori/types"
-// import _ "gitlab.com/beoran/ebsgo/zori/backend"
+import "gitlab.com/beoran/ebsgo/zori/event"
+import "gitlab.com/beoran/ebsgo/zori/backend"
 // import "gitlab.com/beoran/ebsgo/zori/state"
 import  "gitlab.com/beoran/ebsgo/zori/style"
+import  "gitlab.com/beoran/ebsgo/monolog"
 
 
 /*
@@ -13,11 +15,62 @@ import  "gitlab.com/beoran/ebsgo/zori/style"
  * It's ID is always 0;
  */
 type Root struct {
-    Basic
-    Icons map[string]Bitmap
-    Display
-    Console
+  * Basic
+    Icons map[string]Bitmap    
+    MyConsole Console
     UITheme style.Theme
     Cursors
     Active *PageWidget
 }
+
+
+func NewRoot (backend backend.Backend, theme * style.Theme) * Root {
+    root := &Root{}
+    if theme != nil {
+        root.UITheme = * theme
+    }
+    root.Basic = NewBasicWithTheme(nil, nil, theme, nil)
+    root.Be    = backend
+    root.MyRoot= root // Root widget is self-rooted. 
+    return root
+}
+
+func NewRootWidget(backend backend.Backend, theme * style.Theme) Widget {
+    return Widget(NewRoot(backend, theme))
+}
+
+func (root * Root)  Dispatch(ev event.Event) event.Result {
+    return DispatchRecursive(root, ev)
+}
+
+func (root * Root) Draw(ev event.Draw) event.Result {
+    monolog.Debug("Root.Draw")
+    return event.Pass
+}
+
+func (root * Root) Update(ev event.Update) event.Result {
+    return event.Pass
+}
+
+func (root * Root) MouseCursor() Cursor {
+    return root.Cursors.Mouse
+}
+
+func (root * Root) KeyjoyCursor() Cursor {
+    return root.Cursors.Keyjoy
+}
+
+
+/* Gets the theme to use for a marked widget. Looks for the 
+ * cursor to determine this. */
+func (root Root) MarkedTheme() style.Theme {
+    return root.Cursors.Keyjoy.TargetTheme
+}
+
+/* Gets the style to use for a hovered widget. Looks up the cursor 
+ * to determine this. */
+func (root Root) HoveredTheme() style.Theme {
+    return root.Cursors.Mouse.TargetTheme
+}
+
+

+ 44 - 2
zori/widget/widget.go

@@ -2,9 +2,10 @@ package widget
 
 
 import . "gitlab.com/beoran/ebsgo/zori/types"
-// import _ "gitlab.com/beoran/ebsgo/zori/backend"
+import "gitlab.com/beoran/ebsgo/zori/backend"
 import "gitlab.com/beoran/ebsgo/zori/state"
 import  "gitlab.com/beoran/ebsgo/zori/style"
+import  "gitlab.com/beoran/ebsgo/zori/event"
 
 
 /* Mouse or keyboard/joystick cursor. */
@@ -15,7 +16,7 @@ type Cursor struct {
     Bitmap
     state.Flag
     style.Basic
-    TargetStyle style.Theme
+    TargetTheme style.Theme
 }
 
 /* Support multiple cursors...  */
@@ -24,8 +25,49 @@ type Cursors struct {
     Keyjoy Cursor
 }
 
+
+type Prioritized interface {
+    Priority() int
+}
+
+
+type Backended interface {
+    Backend() backend.Backend
+}
+
+type Parented interface {
+    Parent() Widget
+    Children() []Widget
+}
+
+type Rooted interface {
+    Root() RootWidget
+}
+
+type Moveable interface {
+    MoveBy(dx, dy int)
+}
+
+
 type Widget interface {
     Sized
     Positioned
+    Prioritized
     style.Themed
+    Backended
+    Parented
+    Rooted
+    Moveable
+    String() string
+    event.Handler
 }
+
+type RootWidget interface {
+    Widget
+    MouseCursor() Cursor
+    KeyjoyCursor() Cursor
+    MarkedTheme() style.Theme 
+    HoveredTheme() style.Theme
+}
+
+

+ 24 - 11
zori/zori.go

@@ -3,28 +3,41 @@ package zori
 // import "fmt"
 
 // import "gitlab.com/beoran/ebsgo/monolog"
-import _ "gitlab.com/beoran/ebsgo/zori/event"
+import "gitlab.com/beoran/ebsgo/zori/event"
 import _ "gitlab.com/beoran/ebsgo/zori/types"
-import _ "gitlab.com/beoran/ebsgo/zori/backend"
+import "gitlab.com/beoran/ebsgo/zori/backend"
 import _ "gitlab.com/beoran/ebsgo/zori/state"
-import _ "gitlab.com/beoran/ebsgo/zori/style"
+import "gitlab.com/beoran/ebsgo/zori/style"
 import "gitlab.com/beoran/ebsgo/zori/widget"
 
 
 
-
-
-
-type MenuWidget struct {
-    widget.Captioned
-    Selected int
+type UI struct {
+  * widget.Root
+    backend.Backend
 }
 
+func New(be backend.Backend, theme * style.Theme ) *UI {
+    res := &UI{}    
+    res.Backend = be
+    res.Root = widget.NewRoot(res.Backend, theme)
+    return res
+}
 
+func (ui * UI) Dispatch(ev event.Event) event.Result {
+    return ui.Root.Dispatch(ev) 
+}
 
+func (ui * UI) Draw() event.Result {
+    ev := event.Draw{}
+    return ui.Root.Dispatch(ev) 
+}
 
+func (ui * UI) Update(dt float64) event.Result {
+    ev := event.Update{}
+    ev.TimePassed = dt
+    return ui.Root.Dispatch(ev) 
+}
 
 
- 
-