Browse Source

Modify GUI widgets to conform with new infrastructure (manager.go)

Daniel Salvadori 6 years ago
parent
commit
23541b82ca
20 changed files with 227 additions and 841 deletions
  1. 14 14
      gui/button.go
  2. 5 9
      gui/checkradio.go
  3. 33 88
      gui/dropdown.go
  4. 8 11
      gui/edit.go
  5. 36 19
      gui/events.go
  6. 16 2
      gui/folder.go
  7. 3 7
      gui/image_button.go
  8. 5 10
      gui/itemscroller.go
  9. 11 17
      gui/list.go
  10. 11 26
      gui/menu.go
  11. 14 26
      gui/panel.go
  12. 0 452
      gui/root.go
  13. 2 5
      gui/scrollbar.go
  14. 18 57
      gui/scroller.go
  15. 13 19
      gui/slider.go
  16. 9 9
      gui/splitter.go
  17. 3 16
      gui/tabbar.go
  18. 15 32
      gui/table.go
  19. 7 15
      gui/tree.go
  20. 4 7
      gui/window.go

+ 14 - 14
gui/button.go

@@ -59,6 +59,7 @@ func NewButton(text string) *Button {
 	b.Subscribe(OnKeyUp, b.onKey)
 	b.Subscribe(OnMouseUp, b.onMouse)
 	b.Subscribe(OnMouseDown, b.onMouse)
+	b.Subscribe(OnMouseUpOut, b.onMouse)
 	b.Subscribe(OnCursor, b.onCursor)
 	b.Subscribe(OnCursorEnter, b.onCursor)
 	b.Subscribe(OnCursorLeave, b.onCursor)
@@ -127,11 +128,9 @@ func (b *Button) onCursor(evname string, ev interface{}) {
 		b.mouseOver = true
 		b.update()
 	case OnCursorLeave:
-		b.pressed = false
 		b.mouseOver = false
 		b.update()
 	}
-	b.root.StopPropagation(StopAll)
 }
 
 // onMouseEvent process subscribed mouse events
@@ -139,37 +138,38 @@ func (b *Button) onMouse(evname string, ev interface{}) {
 
 	switch evname {
 	case OnMouseDown:
-		b.root.SetKeyFocus(b)
+		Manager().SetKeyFocus(b)
 		b.pressed = true
 		b.update()
-		b.Dispatch(OnClick, nil)
+	case OnMouseUpOut:
+		fallthrough
 	case OnMouseUp:
+		if b.pressed && b.mouseOver {
+			b.Dispatch(OnClick, nil)
+		}
 		b.pressed = false
 		b.update()
 	default:
 		return
 	}
-	b.root.StopPropagation(StopAll)
 }
 
 // onKey processes subscribed key events
 func (b *Button) onKey(evname string, ev interface{}) {
 
 	kev := ev.(*window.KeyEvent)
-	if evname == OnKeyDown && kev.Keycode == window.KeyEnter {
+	if kev.Key != window.KeyEnter {
+		return
+	}
+	switch evname {
+	case OnKeyDown:
 		b.pressed = true
 		b.update()
 		b.Dispatch(OnClick, nil)
-		b.root.StopPropagation(Stop3D)
-		return
-	}
-	if evname == OnKeyUp && kev.Keycode == window.KeyEnter {
+	case OnKeyUp:
 		b.pressed = false
 		b.update()
-		b.root.StopPropagation(Stop3D)
-		return
 	}
-	return
 }
 
 // update updates the button visual state
@@ -179,7 +179,7 @@ func (b *Button) update() {
 		b.applyStyle(&b.styles.Disabled)
 		return
 	}
-	if b.pressed {
+	if b.pressed && b.mouseOver {
 		b.applyStyle(&b.styles.Pressed)
 		return
 	}

+ 5 - 9
gui/checkradio.go

@@ -142,7 +142,7 @@ func (cb *CheckRadio) toggleState() {
 	// Subscribes once to the root panel for OnRadioGroup events
 	// The root panel is used to dispatch events to all checkradios
 	if !cb.subroot {
-		cb.root.Subscribe(OnRadioGroup, func(name string, ev interface{}) {
+		Manager().Subscribe(OnRadioGroup, func(name string, ev interface{}) {
 			cb.onRadioGroup(ev.(*CheckRadio))
 		})
 		cb.subroot = true
@@ -163,24 +163,22 @@ func (cb *CheckRadio) toggleState() {
 	cb.update()
 	cb.Dispatch(OnChange, nil)
 	if !cb.check && len(cb.group) > 0 {
-		cb.root.Dispatch(OnRadioGroup, cb)
+		Manager().Dispatch(OnRadioGroup, cb)
 	}
 }
 
 // onMouse process OnMouseDown events
 func (cb *CheckRadio) onMouse(evname string, ev interface{}) {
 
-	cb.root.SetKeyFocus(cb)
-	cb.root.StopPropagation(Stop3D)
-	cb.toggleState()
 	// Dispatch OnClick for left mouse button down
 	if evname == OnMouseDown {
 		mev := ev.(*window.MouseEvent)
 		if mev.Button == window.MouseButtonLeft {
+			Manager().SetKeyFocus(cb)
+			cb.toggleState()
 			cb.Dispatch(OnClick, nil)
 		}
 	}
-	cb.root.StopPropagation(StopAll)
 }
 
 // onCursor process OnCursor* events
@@ -192,18 +190,16 @@ func (cb *CheckRadio) onCursor(evname string, ev interface{}) {
 		cb.cursorOver = false
 	}
 	cb.update()
-	cb.root.StopPropagation(StopAll)
 }
 
 // onKey receives subscribed key events
 func (cb *CheckRadio) onKey(evname string, ev interface{}) {
 
 	kev := ev.(*window.KeyEvent)
-	if evname == OnKeyDown && kev.Keycode == window.KeyEnter {
+	if evname == OnKeyDown && kev.Key == window.KeyEnter {
 		cb.toggleState()
 		cb.update()
 		cb.Dispatch(OnClick, nil)
-		cb.root.StopPropagation(Stop3D)
 		return
 	}
 	return

+ 33 - 88
gui/dropdown.go

@@ -6,7 +6,6 @@ package gui
 
 import (
 	"github.com/g3n/engine/gui/assets/icon"
-	"github.com/g3n/engine/window"
 )
 
 // DropDown represents a dropdown GUI element.
@@ -20,7 +19,6 @@ type DropDown struct {
 	overDropdown bool
 	overList     bool
 	focus        bool
-	clickOut     bool
 }
 
 // DropDownStyle contains the styling of a DropDown.
@@ -42,7 +40,6 @@ func NewDropDown(width float32, item *ImageLabel) *DropDown {
 	dd.litem = item
 
 	dd.Panel.Initialize(dd, width, 0)
-	dd.Panel.Subscribe(OnKeyDown, dd.onKeyEvent)
 	dd.Panel.Subscribe(OnMouseDown, dd.onMouse)
 	dd.Panel.Subscribe(OnCursorEnter, dd.onCursor)
 	dd.Panel.Subscribe(OnCursorLeave, dd.onCursor)
@@ -63,10 +60,22 @@ func NewDropDown(width float32, item *ImageLabel) *DropDown {
 	dd.list.dropdown = true
 	dd.list.SetVisible(false)
 
-	dd.list.Subscribe(OnMouseDown, dd.onListMouse)
-	dd.list.Subscribe(OnMouseOut, dd.onListMouse)
+	dd.Panel.Subscribe(OnKeyDown, dd.list.onKeyEvent)
+	dd.Subscribe(OnMouseDownOut, func(s string, i interface{}) {
+		// Hide list when clicked out
+		if dd.list.Visible() {
+			dd.list.SetVisible(false)
+		}
+	})
+
+	dd.list.Subscribe(OnCursorEnter, func(evname string, ev interface{}) {
+		dd.Dispatch(OnCursorLeave, ev)
+	})
+	dd.list.Subscribe(OnCursorLeave, func(evname string, ev interface{}) {
+		dd.Dispatch(OnCursorEnter, ev)
+	})
+
 	dd.list.Subscribe(OnChange, dd.onListChangeEvent)
-	dd.list.Subscribe(OnCursor, func(evname string, ev interface{}) { dd.root.StopPropagation(StopAll) })
 	dd.Panel.Add(dd.list)
 
 	dd.update()
@@ -115,11 +124,13 @@ func (dd *DropDown) Selected() *ImageLabel {
 
 // SelectedPos returns the currently selected position or -1 if no item was selected
 func (dd *DropDown) SelectedPos() int {
+
 	return dd.list.selected()
 }
 
 // SetSelected sets the selected item
 func (dd *DropDown) SetSelected(item *ImageLabel) {
+
 	dd.list.SetSelected(dd.selItem, false)
 	dd.list.SetSelected(item, true)
 	dd.copySelected()
@@ -128,9 +139,10 @@ func (dd *DropDown) SetSelected(item *ImageLabel) {
 
 // SelectPos selects the item at the specified position
 func (dd *DropDown) SelectPos(pos int) {
-    dd.list.SetSelected(dd.selItem, false)
+
+	dd.list.SetSelected(dd.selItem, false)
 	dd.list.SelectPos(pos, true)
-    dd.Dispatch(OnChange, nil)
+	dd.Dispatch(OnChange, nil)
 }
 
 // SetStyles sets the drop down styles overriding the default style
@@ -140,31 +152,12 @@ func (dd *DropDown) SetStyles(dds *DropDownStyles) {
 	dd.update()
 }
 
-// onKeyEvent is called when key event is received when this dropdown has the key focus.
-func (dd *DropDown) onKeyEvent(evname string, ev interface{}) {
-
-	kev := ev.(*window.KeyEvent)
-	switch kev.Keycode {
-	case window.KeyF1:
-		if dd.list.Visible() {
-			dd.list.SetVisible(false)
-		}
-	default:
-		return
-	}
-}
-
 // onMouse receives subscribed mouse events over the dropdown
 func (dd *DropDown) onMouse(evname string, ev interface{}) {
 
+	Manager().SetKeyFocus(dd.list)
 	if evname == OnMouseDown {
-		// If clickOut list already closed
-		if dd.clickOut {
-			dd.clickOut = false
-			return
-		}
-		dd.list.SetVisible(true)
-		dd.root.SetKeyFocus(dd.list)
+		dd.list.SetVisible(!dd.list.Visible())
 		return
 	}
 }
@@ -179,70 +172,22 @@ func (dd *DropDown) onCursor(evname string, ev interface{}) {
 		dd.overDropdown = false
 	}
 	dd.update()
-	dd.root.StopPropagation(StopAll)
 }
 
-// onListMouseEvent receives mouse events over the list
-func (dd *DropDown) onListMouse(evname string, ev interface{}) {
-
-	mev := ev.(*window.MouseEvent)
-	// List was clicked
-	if evname == OnMouseDown {
-		// If click occurred inside the list scrollbar ignore it
-		if dd.list.vscroll != nil {
-			if dd.list.vscroll.InsideBorders(mev.Xpos, mev.Ypos) {
-				return
-			}
-		}
-		// Otherwise, closes the list
-		dd.list.SetVisible(false)
-		//dd.copySelected()
-		dd.overList = false
-		dd.update()
-		return
-	}
-	// Hide list when clicked out
-	if evname == OnMouseOut {
-		if dd.list.Visible() {
-			dd.list.SetVisible(false)
-		}
-		// If list clickout occurred inside the dropdown, set 'clickOut' to
-		// indicate that the list was already closed
-		if dd.Panel.InsideBorders(mev.Xpos, mev.Ypos) {
-			dd.clickOut = true
-		}
-	}
-}
-
-// onListCursor receives subscribed events over the list
-//func (dd *DropDown) onListCursor(evname string, ev interface{}) {
-//
-//	if evname == OnCursorEnter {
-//		dd.overList = true
-//		dd.update()
-//		return
-//	}
-//	if evname == OnCursorLeave {
-//		dd.overList = false
-//		dd.update()
-//		return
-//	}
-//	dd.root.StopPropagation(StopAll)
-//}
-
 // copySelected copy to the dropdown panel the selected item
 // from the list.
 func (dd *DropDown) copySelected() {
-    selected := dd.list.Selected()
-    if len(selected) > 0 {
-        dd.selItem = selected[0].(*ImageLabel)
-        dd.litem.CopyFields(dd.selItem)
-        dd.litem.SetWidth(dd.selItem.Width())
-        dd.recalc()
-        dd.Dispatch(OnChange, nil)
-    } else {
-        return
-    }
+
+	selected := dd.list.Selected()
+	if len(selected) > 0 {
+		dd.selItem = selected[0].(*ImageLabel)
+		dd.litem.CopyFields(dd.selItem)
+		dd.litem.SetWidth(dd.selItem.Width())
+		dd.recalc()
+		dd.Dispatch(OnChange, nil)
+	} else {
+		return
+	}
 }
 
 // onListChangeEvent is called when an item in the list is selected

+ 8 - 11
gui/edit.go

@@ -72,6 +72,7 @@ func NewEdit(width int, placeHolder string) *Edit {
 	ed.Label.Subscribe(OnCursorEnter, ed.onCursor)
 	ed.Label.Subscribe(OnCursorLeave, ed.onCursor)
 	ed.Label.Subscribe(OnEnable, func(evname string, ev interface{}) { ed.update() })
+	ed.Subscribe(OnFocusLost, ed.OnFocusLost)
 
 	ed.update()
 	return ed
@@ -109,11 +110,11 @@ func (ed *Edit) SetStyles(es *EditStyles) {
 
 // LostKeyFocus satisfies the IPanel interface and is called by gui root
 // container when the panel loses the key focus
-func (ed *Edit) LostKeyFocus() {
+func (ed *Edit) OnFocusLost(evname string, ev interface{}) {
 
 	ed.focus = false
 	ed.update()
-	ed.root.ClearTimeout(ed.blinkID)
+	Manager().ClearTimeout(ed.blinkID)
 }
 
 // CursorPos sets the position of the cursor at the
@@ -221,7 +222,7 @@ func (ed *Edit) redraw(caret bool) {
 func (ed *Edit) onKey(evname string, ev interface{}) {
 
 	kev := ev.(*window.KeyEvent)
-	switch kev.Keycode {
+	switch kev.Key {
 	case window.KeyLeft:
 		ed.CursorLeft()
 	case window.KeyRight:
@@ -237,7 +238,6 @@ func (ed *Edit) onKey(evname string, ev interface{}) {
 	default:
 		return
 	}
-	ed.root.StopPropagation(Stop3D)
 }
 
 // onChar receives subscribed char events
@@ -256,7 +256,7 @@ func (ed *Edit) onMouse(evname string, ev interface{}) {
 	}
 
 	// Set key focus to this panel
-	ed.root.SetKeyFocus(ed)
+	Manager().SetKeyFocus(ed)
 
 	// Find clicked column
 	var nchars int
@@ -269,27 +269,24 @@ func (ed *Edit) onMouse(evname string, ev interface{}) {
 	}
 	if !ed.focus {
 		ed.focus = true
-		ed.blinkID = ed.root.SetInterval(750*time.Millisecond, nil, ed.blink)
+		ed.blinkID = Manager().SetInterval(750*time.Millisecond, nil, ed.blink)
 	}
 	ed.CursorPos(nchars - 1)
-	ed.root.StopPropagation(Stop3D)
 }
 
 // onCursor receives subscribed cursor events
 func (ed *Edit) onCursor(evname string, ev interface{}) {
 
 	if evname == OnCursorEnter {
-		ed.root.SetCursorText()
+		window.Get().SetCursor(window.IBeamCursor)
 		ed.cursorOver = true
 		ed.update()
-		ed.root.StopPropagation(Stop3D)
 		return
 	}
 	if evname == OnCursorLeave {
-		ed.root.SetCursorNormal()
+		window.Get().SetCursor(window.ArrowCursor)
 		ed.cursorOver = false
 		ed.update()
-		ed.root.StopPropagation(Stop3D)
 		return
 	}
 }

+ 36 - 19
gui/events.go

@@ -8,24 +8,41 @@ import (
 	"github.com/g3n/engine/window"
 )
 
-// Consolidate window events plus GUI events
+// Core events sent by the GUI manager.
+// The target panel is the panel immediately under the mouse cursor.
 const (
-	OnClick       = "gui.OnClick"       // Widget clicked by mouse left button or key
-	OnCursor      = window.OnCursor     // cursor (mouse) position events
-	OnCursorEnter = "gui.OnCursorEnter" // cursor enters the panel area
-	OnCursorLeave = "gui.OnCursorLeave" // cursor leaves the panel area
-	OnMouseDown   = window.OnMouseDown  // any mouse button is pressed
-	OnMouseUp     = window.OnMouseUp    // any mouse button is released
-	OnMouseOut    = "gui.OnMouseOut"    // mouse button pressed outside of the panel
-	OnKeyDown     = window.OnKeyDown    // key is pressed
-	OnKeyUp       = window.OnKeyUp      // key is released
-	OnKeyRepeat   = window.OnKeyRepeat  // key is pressed again by automatic repeat
-	OnChar        = window.OnChar       // key is pressed and has unicode
-	OnResize      = "gui.OnResize"      // panel size changed (no parameters)
-	OnEnable      = "gui.OnEnable"      // panel enabled state changed (no parameters)
-	OnChange      = "gui.OnChange"      // onChange is emitted by List, DropDownList, CheckBox and Edit
-	OnScroll      = window.OnScroll     // scroll event
-	OnChild       = "gui.OnChild"       // child added to or removed from panel
-	OnRadioGroup  = "gui.OnRadioGroup"  // radio button from a group changed state
-	OnRightClick  = "gui.OnRightClick"  // Widget clicked by mouse right button
+	// Events sent to target panel's lowest subscribed ancestor
+	OnMouseDown = window.OnMouseDown // Any mouse button is pressed
+	OnMouseUp   = window.OnMouseUp   // Any mouse button is released
+	OnScroll    = window.OnScroll    // Scrolling mouse wheel
+
+	// Events sent to all panels except the ancestors of the target panel
+	OnMouseDownOut = "gui.OnMouseDownOut" // Any mouse button is pressed
+	OnMouseUpOut   = "gui.OnMouseUpOut"   // Any mouse button is released
+
+	// Event sent to new target panel and all of its ancestors up to (not including) the common ancestor of the new and old targets
+	OnCursorEnter = "gui.OnCursorEnter" // Cursor entered the panel or a descendant
+	// Event sent to old target panel and all of its ancestors up to (not including) the common ancestor of the new and old targets
+	OnCursorLeave = "gui.OnCursorLeave" // Cursor left the panel or a descendant
+	// Event sent to the cursor-focused IDispatcher if any, else sent to target panel's lowest subscribed ancestor
+	OnCursor = window.OnCursor // Cursor is over the panel
+
+	// Event sent to the new key-focused IDispatcher, specified on a call to gui.Manager().SetKeyFocus(core.IDispatcher)
+	OnFocus = "gui.OnFocus" // All keyboard events will be exclusively sent to the receiving IDispatcher
+	// Event sent to the previous key-focused IDispatcher when another panel is key-focused
+	OnFocusLost = "gui.OnFocusLost" // Keyboard events will stop being sent to the receiving IDispatcher
+
+	// Events sent to the key-focused IDispatcher
+	OnKeyDown   = window.OnKeyDown   // A key is pressed
+	OnKeyUp     = window.OnKeyUp     // A key is released
+	OnKeyRepeat = window.OnKeyRepeat // A key was pressed and is now automatically repeating
+	OnChar      = window.OnChar      // A unicode key is pressed
+)
+
+const (
+	OnResize     = "gui.OnResize"     // Panel size changed (no parameters)
+	OnEnable     = "gui.OnEnable"     // Panel enabled/disabled (no parameters)
+	OnClick      = "gui.OnClick"      // Widget clicked by mouse left button or via key press
+	OnChange     = "gui.OnChange"     // Value was changed. Emitted by List, DropDownList, CheckBox and Edit
+	OnRadioGroup = "gui.OnRadioGroup" // Radio button within a group changed state
 )

+ 16 - 2
gui/folder.go

@@ -70,6 +70,20 @@ func (f *Folder) Initialize(text string, width float32, contentPanel IPanel) {
 	f.Panel.Subscribe(OnCursorEnter, f.onCursor)
 	f.Panel.Subscribe(OnCursorLeave, f.onCursor)
 
+	f.Subscribe(OnMouseDownOut, func(s string, i interface{}) {
+		// Hide list when clicked out
+		if f.contentPanel.Visible() {
+			f.contentPanel.SetVisible(false)
+		}
+	})
+
+	f.contentPanel.Subscribe(OnCursorEnter, func(evname string, ev interface{}) {
+		f.Dispatch(OnCursorLeave, ev)
+	})
+	f.contentPanel.Subscribe(OnCursorLeave, func(evname string, ev interface{}) {
+		f.Dispatch(OnCursorEnter, ev)
+	})
+
 	f.alignRight = true
 	f.update()
 	f.recalc()
@@ -90,9 +104,9 @@ func (f *Folder) SetAlignRight(state bool) {
 	f.recalc()
 }
 
-// TotalHeight returns this folder total height
+// Height returns this folder total height
 // considering the contents panel, if visible.
-func (f *Folder) TotalHeight() float32 {
+func (f *Folder) Height() float32 {
 
 	height := f.Height()
 	if f.contentPanel.GetPanel().Visible() {

+ 3 - 7
gui/image_button.go

@@ -177,7 +177,6 @@ func (b *ImageButton) onCursor(evname string, ev interface{}) {
 		b.mouseOver = false
 		b.update()
 	}
-	b.root.StopPropagation(StopAll)
 }
 
 // onMouseEvent process subscribed mouse events
@@ -185,7 +184,7 @@ func (b *ImageButton) onMouse(evname string, ev interface{}) {
 
 	switch evname {
 	case OnMouseDown:
-		b.root.SetKeyFocus(b)
+		Manager().SetKeyFocus(b)
 		b.pressed = true
 		b.update()
 		b.Dispatch(OnClick, nil)
@@ -195,24 +194,21 @@ func (b *ImageButton) onMouse(evname string, ev interface{}) {
 	default:
 		return
 	}
-	b.root.StopPropagation(StopAll)
 }
 
 // onKey processes subscribed key events
 func (b *ImageButton) onKey(evname string, ev interface{}) {
 
 	kev := ev.(*window.KeyEvent)
-	if evname == OnKeyDown && kev.Keycode == window.KeyEnter {
+	if evname == OnKeyDown && kev.Key == window.KeyEnter {
 		b.pressed = true
 		b.update()
 		b.Dispatch(OnClick, nil)
-		b.root.StopPropagation(Stop3D)
 		return
 	}
-	if evname == OnKeyUp && kev.Keycode == window.KeyEnter {
+	if evname == OnKeyUp && kev.Key == window.KeyEnter {
 		b.pressed = false
 		b.update()
-		b.root.StopPropagation(Stop3D)
 		return
 	}
 	return

+ 5 - 10
gui/itemscroller.go

@@ -307,19 +307,15 @@ func (s *ItemScroller) onCursor(evname string, ev interface{}) {
 
 	switch evname {
 	case OnCursorEnter:
-		s.root.SetScrollFocus(s)
 		s.cursorOver = true
 		s.update()
 	case OnCursorLeave:
-		s.root.SetScrollFocus(nil)
 		s.cursorOver = false
 		s.update()
 	}
-	s.root.StopPropagation(Stop3D)
 }
 
-// onScroll receives subscriber mouse scroll events when this scroller has
-// the scroll focus (set by OnMouseEnter)
+// onScroll receives mouse scroll events
 func (s *ItemScroller) onScroll(evname string, ev interface{}) {
 
 	sev := ev.(*window.ScrollEvent)
@@ -328,7 +324,6 @@ func (s *ItemScroller) onScroll(evname string, ev interface{}) {
 	} else if sev.Yoffset < 0 {
 		s.ScrollDown()
 	}
-	s.root.StopPropagation(Stop3D)
 }
 
 // onResize receives resize events
@@ -351,7 +346,7 @@ func (s *ItemScroller) autoSize() {
 		if panel.Width() > width {
 			width = panel.Width()
 		}
-		height += panel.TotalHeight()
+		height += panel.Height()
 	}
 
 	// If auto maximum width enabled
@@ -388,7 +383,7 @@ func (s *ItemScroller) vRecalc() {
 	} else {
 		var posY float32
 		for _, item := range s.items[s.first:] {
-			posY += item.TotalHeight()
+			posY += item.Height()
 			if posY > s.height {
 				scroll = true
 				break
@@ -403,7 +398,7 @@ func (s *ItemScroller) vRecalc() {
 		for _, item := range s.items {
 			// TODO OPTIMIZATION
 			// Break when the view/content proportion becomes smaller than the minimum button size
-			totalHeight += item.TotalHeight()
+			totalHeight += item.Height()
 		}
 		s.vscroll.SetButtonSize(s.height * s.height / totalHeight)
 	}
@@ -433,7 +428,7 @@ func (s *ItemScroller) vRecalc() {
 		if s.adjustItem {
 			item.SetWidth(width)
 		}
-		posY += ipan.TotalHeight()
+		posY += ipan.Height()
 	}
 
 	// Set scroll bar value if recalc was not due by scroll event

+ 11 - 17
gui/list.go

@@ -81,7 +81,6 @@ func (li *List) initialize(vert bool, width, height float32) {
 	li.ItemScroller.initialize(vert, width, height)
 	li.ItemScroller.SetStyles(li.styles.Scroller)
 	li.ItemScroller.adjustItem = true
-	li.ItemScroller.Subscribe(OnMouseDown, li.onMouseEvent)
 	li.ItemScroller.Subscribe(OnKeyDown, li.onKeyEvent)
 	li.ItemScroller.Subscribe(OnKeyRepeat, li.onKeyEvent)
 
@@ -117,19 +116,20 @@ func (li *List) SetStyles(s *ListStyles) {
 }
 
 // Add add a list item at the end of the list
-func (li *List) Add(item IPanel) {
+func (li *List) Add(item IPanel) *ListItem {
 
-	li.InsertAt(len(li.items), item)
+	return li.InsertAt(len(li.items), item)
 }
 
 // InsertAt inserts a list item at the specified position
 // Returs true if the item was successfully inserted
-func (li *List) InsertAt(pos int, item IPanel) {
+func (li *List) InsertAt(pos int, item IPanel) *ListItem {
 
 	litem := newListItem(li, item)
 	li.ItemScroller.InsertAt(pos, litem)
 	litem.Panel.Subscribe(OnMouseDown, litem.onMouse)
 	litem.Panel.Subscribe(OnCursorEnter, litem.onCursor)
+	return litem
 }
 
 // RemoveAt removes the list item from the specified position
@@ -366,20 +366,13 @@ func (li *List) highlighted() (pos int) {
 	return -1
 }
 
-// onMouseEvent receives subscribed mouse events for the list
-func (li *List) onMouseEvent(evname string, ev interface{}) {
-
-	li.root.SetKeyFocus(li)
-	li.root.StopPropagation(StopAll)
-}
-
 // onKeyEvent receives subscribed key events for the list
 func (li *List) onKeyEvent(evname string, ev interface{}) {
 
 	kev := ev.(*window.KeyEvent)
 	// Dropdown mode
 	if li.dropdown {
-		switch kev.Keycode {
+		switch kev.Key {
 		case li.keyNext:
 			li.selNext(true, true)
 		case li.keyPrev:
@@ -389,13 +382,12 @@ func (li *List) onKeyEvent(evname string, ev interface{}) {
 		default:
 			return
 		}
-		li.root.StopPropagation(Stop3D)
 		return
 	}
 
 	// Listbox mode single selection
 	if li.single {
-		switch kev.Keycode {
+		switch kev.Key {
 		case li.keyNext:
 			li.selNext(true, true)
 		case li.keyPrev:
@@ -403,12 +395,11 @@ func (li *List) onKeyEvent(evname string, ev interface{}) {
 		default:
 			return
 		}
-		li.root.StopPropagation(Stop3D)
 		return
 	}
 
 	// Listbox mode multiple selection
-	switch kev.Keycode {
+	switch kev.Key {
 	case li.keyNext:
 		li.selNext(false, true)
 	case li.keyPrev:
@@ -422,13 +413,13 @@ func (li *List) onKeyEvent(evname string, ev interface{}) {
 	default:
 		return
 	}
-	li.root.StopPropagation(Stop3D)
 }
 
 // setSelection sets the selected state of the specified item
 // updating the visual appearance of the list if necessary
 func (li *List) setSelection(litem *ListItem, state bool, force bool, dispatch bool) {
 
+	Manager().SetKeyFocus(li)
 	// If already at this state, nothing to do
 	if litem.selected == state && !force {
 		return
@@ -488,6 +479,9 @@ func (litem *ListItem) onMouse(evname string, ev interface{}) {
 	} else {
 		litem.list.setSelection(litem, !litem.selected, true, true)
 	}
+	if litem.list.dropdown {
+		litem.list.SetVisible(false)
+	}
 }
 
 // onCursor receives subscribed cursor events over the list item

+ 11 - 26
gui/menu.go

@@ -142,7 +142,7 @@ func NewMenuBar() *Menu {
 
 	m := NewMenu()
 	m.bar = true
-	m.Panel.Subscribe(OnMouseOut, m.onMouse)
+	m.Panel.Subscribe(OnMouseDownOut, m.onMouse)
 	return m
 }
 
@@ -153,8 +153,6 @@ func NewMenu() *Menu {
 	m.Panel.Initialize(m, 0, 0)
 	m.styles = &StyleDefault().Menu
 	m.items = make([]*MenuItem, 0)
-	m.Panel.Subscribe(OnCursorEnter, m.onCursor)
-	m.Panel.Subscribe(OnCursor, m.onCursor)
 	m.Panel.Subscribe(OnKeyDown, m.onKey)
 	m.Panel.Subscribe(OnResize, m.onResize)
 	m.update()
@@ -213,22 +211,12 @@ func (m *Menu) RemoveItem(mi *MenuItem) {
 
 }
 
-// onCursor process subscribed cursor events
-func (m *Menu) onCursor(evname string, ev interface{}) {
-
-	switch evname {
-	case OnCursorEnter:
-		m.root.SetKeyFocus(m)
-	}
-	m.root.StopPropagation(StopAll)
-}
-
 // onKey process subscribed key events
 func (m *Menu) onKey(evname string, ev interface{}) {
 
 	sel := m.selectedPos()
 	kev := ev.(*window.KeyEvent)
-	switch kev.Keycode {
+	switch kev.Key {
 	// Select next enabled menu item
 	case window.KeyDown:
 		if sel < 0 {
@@ -244,7 +232,7 @@ func (m *Menu) onKey(evname string, ev interface{}) {
 			// Sets autoOpen and selects sub menu
 			m.autoOpen = true
 			mi.update()
-			m.root.SetKeyFocus(mi.submenu)
+			Manager().SetKeyFocus(mi.submenu)
 			mi.submenu.setSelectedPos(0)
 			return
 		}
@@ -281,7 +269,7 @@ func (m *Menu) onKey(evname string, ev interface{}) {
 			} else {
 				m.mitem.menu.setSelectedItem(m.mitem)
 			}
-			m.root.SetKeyFocus(m.mitem.menu)
+			Manager().SetKeyFocus(m.mitem.menu)
 			return
 		}
 
@@ -299,7 +287,7 @@ func (m *Menu) onKey(evname string, ev interface{}) {
 		}
 		// Enter into sub menu
 		if mi.submenu != nil {
-			m.root.SetKeyFocus(mi.submenu)
+			Manager().SetKeyFocus(mi.submenu)
 			mi.submenu.setSelectedPos(0)
 			return
 		}
@@ -308,7 +296,7 @@ func (m *Menu) onKey(evname string, ev interface{}) {
 			sel := m.mitem.menu.selectedPos()
 			next := m.mitem.menu.nextItem(sel)
 			m.mitem.menu.setSelectedPos(next)
-			m.root.SetKeyFocus(m.mitem.menu)
+			Manager().SetKeyFocus(m.mitem.menu)
 		}
 	// Enter -> Select menu option
 	case window.KeyEnter:
@@ -346,7 +334,7 @@ func (m *Menu) onMouse(evname string, ev interface{}) {
 
 	// Clear menu bar after some time, to give time for menu items
 	// to receive onMouse events.
-	m.Root().SetTimeout(1*time.Millisecond, nil, func(arg interface{}) {
+	Manager().SetTimeout(1*time.Millisecond, nil, func(arg interface{}) {
 		m.autoOpen = false
 		m.setSelectedPos(-1)
 	})
@@ -366,7 +354,7 @@ func (m *Menu) checkKey(kev *window.KeyEvent) *MenuItem {
 
 	for i := 0; i < len(m.items); i++ {
 		mi := m.items[i]
-		if mi.keyCode == kev.Keycode && mi.keyMods == kev.Mods {
+		if mi.keyCode == kev.Key && mi.keyMods == kev.Mods {
 			return mi
 		}
 		if mi.submenu != nil {
@@ -670,11 +658,10 @@ func (mi *MenuItem) SetSubmenu(smi *MenuItem) *MenuItem {
 }
 
 // SetEnabled sets the enabled state of this menu item
-func (mi *MenuItem) SetEnabled(enabled bool) *MenuItem {
+func (mi *MenuItem) SetEnabled(enabled bool) {
 
 	mi.disabled = !enabled
 	mi.update()
-	return mi
 }
 
 // SetId sets this menu item string id which can be used to identify
@@ -716,7 +703,6 @@ func (mi *MenuItem) onCursor(evname string, ev interface{}) {
 	case OnCursorEnter:
 		mi.menu.setSelectedItem(mi)
 	}
-	mi.root.StopPropagation(StopAll)
 }
 
 // onMouse processes subscribed mouse events over the menu item
@@ -729,7 +715,7 @@ func (mi *MenuItem) onMouse(evname string, ev interface{}) {
 			mi.menu.autoOpen = !mi.menu.autoOpen
 			if mi.submenu != nil && mi.submenu.Visible() {
 				mi.submenu.SetVisible(false)
-				mi.root.SetKeyFocus(mi.menu)
+				Manager().SetKeyFocus(mi.menu)
 			} else {
 				mi.update()
 			}
@@ -739,7 +725,6 @@ func (mi *MenuItem) onMouse(evname string, ev interface{}) {
 		}
 		mi.activate()
 	}
-	mi.root.StopPropagation(StopAll)
 }
 
 // activate activates this menu item dispatching OnClick events
@@ -750,7 +735,7 @@ func (mi *MenuItem) activate() {
 		rm.autoOpen = false
 	}
 	rm.setSelectedPos(-1)
-	mi.root.SetKeyFocus(rm)
+	Manager().SetKeyFocus(rm)
 	mi.dispatchAll(OnClick, mi)
 }
 

+ 14 - 26
gui/panel.go

@@ -5,8 +5,6 @@
 package gui
 
 import (
-	"math"
-
 	"github.com/g3n/engine/core"
 	"github.com/g3n/engine/geometry"
 	"github.com/g3n/engine/gls"
@@ -42,12 +40,14 @@ import (
 type IPanel interface {
 	graphic.IGraphic
 	GetPanel() *Panel
-	SetRoot(*Root)
-	Root() *Root
-	LostKeyFocus()
-	TotalHeight() float32
-	TotalWidth() float32
+	Width() float32
+	Height() float32
+	Enabled() bool
+	SetEnabled(bool)
 	SetLayout(ILayout)
+	InsideBorders(x, y float32) bool
+
+	// TODO these methods here should probably be defined in INode
 	SetPosition(x, y float32)
 	SetPositionX(x float32)
 	SetPositionY(y float32)
@@ -59,9 +59,6 @@ type IPanel interface {
 // It is the building block of most GUI widgets.
 type Panel struct {
 	*graphic.Graphic                    // Embedded graphic
-	root             *Root              // pointer to root container
-	width            float32            // external width in pixels
-	height           float32            // external height in pixels
 	mat              *material.Material // panel material
 
 	bounded   bool // Whether panel is bounded by its parent
@@ -708,7 +705,7 @@ func (p *Panel) setContentSize(width, height float32, dispatch bool) {
 // All unbounded panels and its children are closer than any of the bounded panels.
 func (p *Panel) setZ(z, zunb float32) (float32, float32) {
 
-	// TODO there's a problem here - two buttons wish labels one on top of the other have interlacing labels...
+	// TODO there's a problem here - two buttons with labels one on top of the other have interlacing labels...
 
 	// Bounded panel
 	if p.bounded {
@@ -855,20 +852,11 @@ func (p *Panel) resize(width, height float32, dispatch bool) {
 	// Adjust other area widths
 	padding.Width = p.paddingSizes.Left + p.content.Width + p.paddingSizes.Right
 	border.Width = p.borderSizes.Left + padding.Width + p.borderSizes.Right
-
-	// Adjusts content height
-	p.content.Height = height -
-		p.marginSizes.Top - p.marginSizes.Bottom -
-		p.borderSizes.Top - p.borderSizes.Bottom -
-		p.paddingSizes.Top - p.paddingSizes.Bottom
-	if p.content.Height < 0 {
-		p.content.Height = 0
-	}
 	// Adjust other area heights
 	padding.Height = p.paddingSizes.Top + p.content.Height + p.paddingSizes.Bottom
 	border.Height = p.borderSizes.Top + padding.Height + p.borderSizes.Bottom
 
-	// Sets area positions
+	// Set area positions
 	border.X = p.marginSizes.Left
 	border.Y = p.marginSizes.Top
 	padding.X = border.X + p.borderSizes.Left
@@ -876,25 +864,25 @@ func (p *Panel) resize(width, height float32, dispatch bool) {
 	p.content.X = padding.X + p.paddingSizes.Left
 	p.content.Y = padding.Y + p.paddingSizes.Top
 
-	// Sets final panel dimensions (may be different from requested dimensions)
+	// Set final panel dimensions (may be different from requested dimensions)
 	p.width = p.marginSizes.Left + border.Width + p.marginSizes.Right
 	p.height = p.marginSizes.Top + border.Height + p.marginSizes.Bottom
 
-	// Updates border uniform in texture coordinates (0,0 -> 1,1)
+	// Update border uniform in texture coordinates (0,0 -> 1,1)
 	p.udata.borders = math32.Vector4{
 		float32(border.X) / float32(p.width),
 		float32(border.Y) / float32(p.height),
 		float32(border.Width) / float32(p.width),
 		float32(border.Height) / float32(p.height),
 	}
-	// Updates padding uniform in texture coordinates (0,0 -> 1,1)
+	// Update padding uniform in texture coordinates (0,0 -> 1,1)
 	p.udata.paddings = math32.Vector4{
 		float32(padding.X) / float32(p.width),
 		float32(padding.Y) / float32(p.height),
 		float32(padding.Width) / float32(p.width),
 		float32(padding.Height) / float32(p.height),
 	}
-	// Updates content uniform in texture coordinates (0,0 -> 1,1)
+	// Update content uniform in texture coordinates (0,0 -> 1,1)
 	p.udata.content = math32.Vector4{
 		float32(p.content.X) / float32(p.width),
 		float32(p.content.Y) / float32(p.height),
@@ -942,7 +930,7 @@ func (p *Panel) RenderSetup(gl *gls.GLS, rinfo *core.RenderInfo) {
 func (p *Panel) SetModelMatrix(gl *gls.GLS, mm *math32.Matrix4) {
 
 	// Get scale of window (for HiDPI support)
-	sX64, sY64 := p.Root().Window().Scale()
+	sX64, sY64 := Manager().win.GetScale()
 	sX := float32(sX64)
 	sY := float32(sY64)
 

+ 0 - 452
gui/root.go

@@ -1,452 +0,0 @@
-// Copyright 2016 The G3N Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gui
-
-import (
-	"github.com/g3n/engine/core"
-	"github.com/g3n/engine/gls"
-	"github.com/g3n/engine/window"
-	"sort"
-)
-
-// Root is the container and dispatcher of panel events
-type Root struct {
-	Panel                            // embedded panel
-	core.TimerManager                // embedded TimerManager
-	gs                *gls.GLS       // OpenGL state
-	win               window.IWindow // Window
-	stopPropagation   int            // stop event propagation bitmask
-	keyFocus          IPanel         // current child panel with key focus
-	mouseFocus        IPanel         // current child panel with mouse focus
-	scrollFocus       IPanel         // current child panel with scroll focus
-	modalPanel        IPanel         // current modal panel
-	targets           []IPanel       // preallocated list of target panels
-}
-
-// Types of event propagation stopping.
-const (
-	StopGUI = 0x01             // Stop event propagation to GUI
-	Stop3D  = 0x02             // Stop event propagation to 3D
-	StopAll = StopGUI | Stop3D // Stop event propagation
-)
-
-// NewRoot creates and returns a pointer to a gui root panel for the specified window
-func NewRoot(gs *gls.GLS, win window.IWindow) *Root {
-
-	r := new(Root)
-	r.gs = gs
-	r.win = win
-	r.root = r
-	r.Panel.Initialize(0, 0)
-	r.TimerManager.Initialize()
-
-	// Set the size of the root panel based on the window framebuffer size
-	width, height := win.FramebufferSize()
-	r.SetSize(float32(width), float32(height))
-
-	// For optimization, set this root panel as not renderable as in most cases
-	// it is used only as a container
-	r.SetRenderable(false)
-
-	// Subscribe to window events
-	r.SubscribeWin()
-	r.targets = []IPanel{}
-	return r
-}
-
-// SubscribeWin subscribes this root panel to window events
-func (r *Root) SubscribeWin() {
-
-	r.win.Subscribe(window.OnKeyUp, r.onKey)
-	r.win.Subscribe(window.OnKeyDown, r.onKey)
-	r.win.Subscribe(window.OnKeyRepeat, r.onKey)
-	r.win.Subscribe(window.OnChar, r.onChar)
-	r.win.Subscribe(window.OnMouseUp, r.onMouse)
-	r.win.Subscribe(window.OnMouseDown, r.onMouse)
-	r.win.Subscribe(window.OnCursor, r.onCursor)
-	r.win.Subscribe(window.OnScroll, r.onScroll)
-	r.win.Subscribe(window.OnWindowSize, r.onWindowSize)
-	r.win.Subscribe(window.OnFrame, r.onFrame)
-}
-
-// Add adds the specified panel to the root container list of children
-// Overrides the Panel version because it needs to set the root panel field
-func (r *Root) Add(ipan IPanel) {
-
-	// Sets the root panel field of the child to be added
-	ipan.GetPanel().root = r
-	// Add this panel to the root panel children.
-	// This will also set the root panel for all the child children
-	// and the z coordinates of all the panel tree graph.
-	r.Panel.Add(ipan)
-}
-
-// Window returns the associated IWindow.
-func (r *Root) Window() window.IWindow {
-
-	return r.win
-}
-
-// SetModal sets the modal panel.
-// If there is a modal panel, only events for this panel are dispatched
-// To remove the modal panel call this function with a nil panel.
-func (r *Root) SetModal(ipan IPanel) {
-
-	r.modalPanel = ipan
-}
-
-// SetKeyFocus sets the panel which will receive all keyboard events
-// Passing nil will remove the focus (if any)
-func (r *Root) SetKeyFocus(ipan IPanel) {
-
-	if r.keyFocus != nil {
-		// If this panel is already in focus, nothing to do
-		if ipan != nil {
-			if r.keyFocus.GetPanel() == ipan.GetPanel() {
-				return
-			}
-		}
-		r.keyFocus.LostKeyFocus()
-	}
-	r.keyFocus = ipan
-}
-
-// ClearKeyFocus clears the key focus panel (if any) without
-// calling LostKeyFocus() for previous focused panel
-func (r *Root) ClearKeyFocus() {
-
-	r.keyFocus = nil
-}
-
-// SetMouseFocus sets the panel which will receive all mouse events
-// Passing nil will restore the default event processing
-func (r *Root) SetMouseFocus(ipan IPanel) {
-
-	r.mouseFocus = ipan
-}
-
-// SetScrollFocus sets the panel which will receive all scroll events
-// Passing nil will restore the default event processing
-func (r *Root) SetScrollFocus(ipan IPanel) {
-
-	r.scrollFocus = ipan
-}
-
-// HasKeyFocus checks if the specified panel has the key focus
-func (r *Root) HasKeyFocus(ipan IPanel) bool {
-
-	if r.keyFocus == nil {
-		return false
-	}
-	if r.keyFocus.GetPanel() == ipan.GetPanel() {
-		return true
-	}
-	return false
-}
-
-// HasMouseFocus checks if the specified panel has the mouse focus
-func (r *Root) HasMouseFocus(ipan IPanel) bool {
-
-	if r.mouseFocus == nil {
-		return false
-	}
-	if r.mouseFocus.GetPanel() == ipan.GetPanel() {
-		return true
-	}
-	return false
-}
-
-// StopPropagation stops the propagation of the current event
-// to outside the root panel (for example the 3D camera)
-func (r *Root) StopPropagation(events int) {
-
-	r.stopPropagation |= events
-}
-
-// SetCursorNormal sets the cursor over the associated window to the standard type.
-func (r *Root) SetCursorNormal() {
-
-	r.win.SetStandardCursor(window.ArrowCursor)
-}
-
-// SetCursorText sets the cursor over the associated window to the I-Beam type.
-func (r *Root) SetCursorText() {
-
-	r.win.SetStandardCursor(window.IBeamCursor)
-}
-
-// SetCursorCrosshair sets the cursor over the associated window to the crosshair type.
-func (r *Root) SetCursorCrosshair() {
-
-	r.win.SetStandardCursor(window.CrosshairCursor)
-}
-
-// SetCursorHand sets the cursor over the associated window to the hand type.
-func (r *Root) SetCursorHand() {
-
-	r.win.SetStandardCursor(window.HandCursor)
-}
-
-// SetCursorHResize sets the cursor over the associated window to the horizontal resize type.
-func (r *Root) SetCursorHResize() {
-
-	r.win.SetStandardCursor(window.HResizeCursor)
-}
-
-// SetCursorVResize sets the cursor over the associated window to the vertical resize type.
-func (r *Root) SetCursorVResize() {
-
-	r.win.SetStandardCursor(window.VResizeCursor)
-}
-
-// SetCursorDiag1 sets the cursor over the associated window to the diagonal (/) resize type.
-func (r *Root) SetCursorDiagResize1() {
-
-	r.win.SetStandardCursor(window.DiagResize1Cursor)
-}
-
-// SetCursorDiag2 sets the cursor over the associated window to the diagonal (\) resize type.
-func (r *Root) SetCursorDiagResize2() {
-
-	r.win.SetStandardCursor(window.DiagResize2Cursor)
-}
-
-// TODO allow setting a custom cursor
-
-// onKey is called when key events are received
-func (r *Root) onKey(evname string, ev interface{}) {
-
-	// If no panel has the key focus, nothing to do
-	if r.keyFocus == nil {
-		return
-	}
-	// Checks modal panel
-	if !r.canDispatch(r.keyFocus) {
-		return
-	}
-	// Dispatch window.KeyEvent to focused panel subscribers
-	r.stopPropagation = 0
-	r.keyFocus.GetPanel().Dispatch(evname, ev)
-	// If requested, stop propagation of event outside the root gui
-	if (r.stopPropagation & Stop3D) != 0 {
-		r.win.CancelDispatch()
-	}
-}
-
-// onChar is called when char events are received
-func (r *Root) onChar(evname string, ev interface{}) {
-
-	// If no panel has the key focus, nothing to do
-	if r.keyFocus == nil {
-		return
-	}
-	// Checks modal panel
-	if !r.canDispatch(r.keyFocus) {
-		return
-	}
-	// Dispatch window.CharEvent to focused panel subscribers
-	r.stopPropagation = 0
-	r.keyFocus.GetPanel().Dispatch(evname, ev)
-	// If requested, stopj propagation of event outside the root gui
-	if (r.stopPropagation & Stop3D) != 0 {
-		r.win.CancelDispatch()
-	}
-}
-
-// onMouse is called when mouse button events are received
-func (r *Root) onMouse(evname string, ev interface{}) {
-
-	mev := ev.(*window.MouseEvent)
-	r.sendPanels(mev.Xpos, mev.Ypos, evname, ev)
-}
-
-// onCursor is called when (mouse) cursor events are received
-func (r *Root) onCursor(evname string, ev interface{}) {
-
-	cev := ev.(*window.CursorEvent)
-	r.sendPanels(cev.Xpos, cev.Ypos, evname, ev)
-}
-
-// sendPanel sends a mouse or cursor event to focused panel or panels
-// which contain the specified screen position
-func (r *Root) sendPanels(x, y float32, evname string, ev interface{}) {
-
-	// Apply scale of window (for HiDPI support)
-	sX64, sY64 := r.Window().Scale()
-	x /= float32(sX64)
-	y /= float32(sY64)
-
-	// If there is panel with MouseFocus send only to this panel
-	if r.mouseFocus != nil {
-		// Checks modal panel
-		if !r.canDispatch(r.mouseFocus) {
-			return
-		}
-		r.mouseFocus.GetPanel().Dispatch(evname, ev)
-		if (r.stopPropagation & Stop3D) != 0 {
-			r.win.CancelDispatch()
-		}
-		return
-	}
-
-	// Clear list of panels which contains the mouse position
-	r.targets = r.targets[0:0]
-
-	// checkPanel checks recursively if the specified panel and
-	// any of its children contain the mouse position
-	var checkPanel func(ipan IPanel)
-	checkPanel = func(ipan IPanel) {
-		pan := ipan.GetPanel()
-		// If panel not visible or not enabled, ignore
-		if !pan.Visible() || !pan.Enabled() {
-			return
-		}
-		// Checks if this panel contains the mouse position
-		found := pan.InsideBorders(x, y)
-		if found {
-			r.targets = append(r.targets, ipan)
-		} else {
-			// If OnCursorEnter previously sent, sends OnCursorLeave with a nil event
-			if pan.cursorEnter {
-				pan.Dispatch(OnCursorLeave, nil)
-				pan.cursorEnter = false
-			}
-			// If mouse button was pressed, sends event informing mouse down outside of the panel
-			if evname == OnMouseDown {
-				pan.Dispatch(OnMouseOut, ev)
-			}
-		}
-		// Checks if any of its children also contains the position
-		for _, child := range pan.Children() {
-			ipan, ok := child.(IPanel)
-			if ok {
-				checkPanel(ipan)
-			}
-		}
-	}
-
-	// Checks all children of this root node
-	for _, iobj := range r.Node.Children() {
-		ipan, ok := iobj.(IPanel)
-		if !ok {
-			continue
-		}
-		checkPanel(ipan)
-	}
-
-	// No panels found
-	if len(r.targets) == 0 {
-		// If event is mouse click, removes the keyboard focus
-		if evname == OnMouseDown {
-			r.SetKeyFocus(nil)
-		}
-		return
-	}
-
-	// Sorts panels by absolute Z with the most foreground panels first
-	// and sends event to all panels or until a stop is requested
-	sort.Slice(r.targets, func(i, j int) bool {
-		iz := r.targets[i].GetPanel().Position().Z
-		jz := r.targets[j].GetPanel().Position().Z
-		return iz < jz
-	})
-
-	r.stopPropagation = 0
-
-	// Send events to panels
-	for _, ipan := range r.targets {
-		// Checks modal panel
-		if !r.canDispatch(ipan) {
-			continue
-		}
-		pan := ipan.GetPanel()
-		// Cursor position event
-		if evname == OnCursor {
-			pan.Dispatch(evname, ev)
-			if !pan.cursorEnter {
-				pan.Dispatch(OnCursorEnter, ev)
-				pan.cursorEnter = true
-			}
-			// Mouse button event
-		} else {
-			pan.Dispatch(evname, ev)
-		}
-		if (r.stopPropagation & StopGUI) != 0 {
-			break
-		}
-	}
-
-	// Stops propagation of event outside the root gui
-	if (r.stopPropagation & Stop3D) != 0 {
-		r.win.CancelDispatch()
-	}
-}
-
-// onScroll is called when scroll events are received and
-// is responsible to dispatch them to child panels.
-func (r *Root) onScroll(evname string, ev interface{}) {
-
-	// If no panel with the scroll focus, nothing to do
-	if r.scrollFocus == nil {
-		return
-	}
-	// Dispatch event to panel with scroll focus
-	r.scrollFocus.GetPanel().Dispatch(evname, ev)
-
-	// Stops propagation of event outside the root gui
-	if (r.stopPropagation & Stop3D) != 0 {
-		r.win.CancelDispatch()
-	}
-}
-
-// onSize is called when window size events are received
-func (r *Root) onWindowSize(evname string, ev interface{}) {
-
-	// Sends event only to immediate children
-	for _, ipan := range r.Children() {
-		ipan.(IPanel).GetPanel().Dispatch(evname, ev)
-	}
-}
-
-// onFrame is called when window finished swapping frame buffers
-func (r *Root) onFrame(evname string, ev interface{}) {
-
-	r.TimerManager.ProcessTimers()
-}
-
-// canDispatch returns if event can be dispatched to the specified panel
-// An event cannot be dispatched if there is a modal panel and the specified
-// panel is not the modal panel or any of its children.
-func (r *Root) canDispatch(ipan IPanel) bool {
-
-	if r.modalPanel == nil {
-		return true
-	}
-	if r.modalPanel == ipan {
-		return true
-	}
-
-	// Internal function to check panel children recursively
-	var checkChildren func(iparent IPanel) bool
-	checkChildren = func(iparent IPanel) bool {
-		parent := iparent.GetPanel()
-		for _, child := range parent.Children() {
-			if child == ipan {
-				return true
-			}
-			res := checkChildren(child.(IPanel))
-			if res {
-				return res
-			}
-		}
-		return false
-	}
-	return checkChildren(r.modalPanel)
-}
-
-//func (r *Root) applyStyleRecursively(s *Style) {
-//	// TODO
-//	// This should probably be in Panel ?
-//}

+ 2 - 5
gui/scrollbar.go

@@ -163,7 +163,6 @@ func (sb *ScrollBar) onMouse(evname string, ev interface{}) {
 		newX := math32.Clamp(posx-(sb.button.width/2), 0, sb.content.Width-sb.button.width)
 		sb.button.SetPositionX(newX)
 	}
-	sb.root.StopPropagation(StopAll)
 	sb.Dispatch(OnChange, nil)
 }
 
@@ -213,14 +212,13 @@ func (button *scrollBarButton) onMouse(evname string, ev interface{}) {
 		button.pressed = true
 		button.mouseX = e.Xpos
 		button.mouseY = e.Ypos
-		button.sb.root.SetMouseFocus(button)
+		Manager().SetCursorFocus(button)
 	case OnMouseUp:
 		button.pressed = false
-		button.sb.root.SetMouseFocus(nil)
+		Manager().SetCursorFocus(nil)
 	default:
 		return
 	}
-	button.sb.root.StopPropagation(StopAll)
 }
 
 // onCursor receives subscribed cursor events for the scroll bar button
@@ -242,5 +240,4 @@ func (button *scrollBarButton) onCursor(evname string, ev interface{}) {
 	button.mouseX = e.Xpos
 	button.mouseY = e.Ypos
 	button.sb.Dispatch(OnChange, nil)
-	button.sb.root.StopPropagation(StopAll)
 }

+ 18 - 57
gui/scroller.go

@@ -16,15 +16,14 @@ import (
 // The interlocking of the scrollbars (which happens when both scrollbars are visible) can be configured.
 // Whether each scrollbar overlaps the content can also be configured (useful for transparent UIs).
 type Scroller struct {
-	Panel                        // Embedded panel
-	mode          ScrollMode     // ScrollMode specifies which scroll directions are allowed
-	target        IPanel         // The IPanel that will be scrolled through
-	hscroll       *ScrollBar     // Horizontal scrollbar (may be nil)
-	vscroll       *ScrollBar     // Vertical scrollbar (may be nil)
-	style         *ScrollerStyle // The current style
-	corner        *Panel         // The optional corner panel (can be visible when scrollMode==Both, interlocking==None, corner=true)
-	cursorOver    bool           // Cursor is over the scroller
-	modKeyPressed bool           // Modifier key is pressed
+	Panel                     // Embedded panel
+	mode       ScrollMode     // ScrollMode specifies which scroll directions are allowed
+	target     IPanel         // The IPanel that will be scrolled through
+	hscroll    *ScrollBar     // Horizontal scrollbar (may be nil)
+	vscroll    *ScrollBar     // Vertical scrollbar (may be nil)
+	style      *ScrollerStyle // The current style
+	corner     *Panel         // The optional corner panel (can be visible when scrollMode==Both, interlocking==None, corner=true)
+	cursorOver bool           // Cursor is over the scroller
 }
 
 // ScrollMode specifies which scroll directions are allowed.
@@ -83,8 +82,8 @@ type ScrollerScrollbarStyle struct {
 // TODO these configuration variables could be made part of a global engine configuration object in the future
 // They should not be added to style since they are not style changes and not to the struct since they are global
 
-// ScrollModifierKey is the Key that changes the scrolling direction from vertical to horizontal
-const ScrollModifierKey = window.KeyLeftShift
+// ScrollModifierKey is the ModifierKey that changes the scrolling direction from vertical to horizontal when pressed
+const ScrollModifierKey = window.ModShift
 
 // NewScroller creates and returns a pointer to a new Scroller with the specified
 // target IPanel and ScrollMode.
@@ -104,12 +103,8 @@ func (s *Scroller) initialize(width, height float32, mode ScrollMode, target IPa
 	s.Panel.Add(s.target)
 	s.mode = mode
 
-	s.Subscribe(OnCursorEnter, s.onCursor)
-	s.Subscribe(OnCursorLeave, s.onCursor)
-	s.Subscribe(OnScroll, s.onScroll)
-	s.Subscribe(OnKeyDown, s.onKey)
-	s.Subscribe(OnKeyUp, s.onKey)
 	s.Subscribe(OnResize, s.onResize)
+	s.Subscribe(OnScroll, s.onScroll)
 
 	s.Update()
 }
@@ -278,22 +273,6 @@ func (s *Scroller) ScrollTo(x, y float32) {
 	// TODO
 }
 
-// onCursor receives subscribed cursor events over the panel
-func (s *Scroller) onCursor(evname string, ev interface{}) {
-
-	switch evname {
-	case OnCursorEnter:
-		s.root.SetScrollFocus(s)
-		s.root.SetKeyFocus(s)
-		s.cursorOver = true
-	case OnCursorLeave:
-		s.root.SetScrollFocus(nil)
-		s.root.SetKeyFocus(nil)
-		s.cursorOver = false
-	}
-	s.root.StopPropagation(Stop3D)
-}
-
 // onScroll receives mouse scroll events when this scroller has the scroll focus (set by OnMouseEnter)
 func (s *Scroller) onScroll(evname string, ev interface{}) {
 
@@ -307,7 +286,7 @@ func (s *Scroller) onScroll(evname string, ev interface{}) {
 	offsetY := sev.Yoffset * mult
 
 	// If modifier key is pressed (left shift by default) - then scroll in the horizontal direction
-	if s.modKeyPressed {
+	if sev.Mods&ScrollModifierKey > 0 {
 		if math32.Abs(offsetY) > math32.Abs(offsetX) {
 			offsetX = offsetY
 		}
@@ -331,24 +310,6 @@ func (s *Scroller) onScroll(evname string, ev interface{}) {
 	}
 
 	s.recalc()
-	s.root.StopPropagation(Stop3D)
-}
-
-// onKey receives key events
-func (s *Scroller) onKey(evname string, ev interface{}) {
-
-	key := ev.(*window.KeyEvent)
-	log.Error("Key %v", key)
-	if key.Keycode == ScrollModifierKey {
-		if evname == OnKeyDown {
-			s.modKeyPressed = true
-			log.Error("true")
-		} else if evname == OnKeyUp {
-			log.Error("false")
-			s.modKeyPressed = false
-		}
-	}
-	s.root.StopPropagation(Stop3D)
 }
 
 // onResize receives resize events
@@ -385,8 +346,8 @@ func (s *Scroller) setHorizontalScrollbarVisible() {
 func (s *Scroller) updateScrollbarsVisibility() {
 
 	// Obtain the size of the target panel
-	targetWidth := s.target.TotalWidth()
-	targetHeight := s.target.TotalHeight()
+	targetWidth := s.target.Width()
+	targetHeight := s.target.Height()
 
 	// If vertical scrolling is enabled and the vertical scrollbar should be visible
 	if (s.mode&ScrollVertical > 0) && (targetHeight > s.content.Height) {
@@ -458,8 +419,8 @@ func (s *Scroller) recalc() {
 	// The multipliers of the scrollbars' [0,1] values.
 	// After applied, they will give the correct target panel position.
 	// They can be thought of as the range of motion of the target panel in each axis
-	multHeight := s.target.TotalHeight() - s.content.Height
-	multWidth := s.target.TotalWidth() - s.content.Width
+	multHeight := s.target.Height() - s.content.Height
+	multWidth := s.target.Width() - s.content.Width
 
 	var targetX, targetY float32
 	var offsetX, offsetY float32
@@ -540,7 +501,7 @@ func (s *Scroller) recalcV() {
 
 	// Adjust the scrollbar button size to the correct proportion proportion according to the style
 	if s.style.VerticalScrollbar.AutoSizeButton {
-		s.vscroll.SetButtonSize(vscrollHeight * viewHeight / s.target.TotalHeight())
+		s.vscroll.SetButtonSize(vscrollHeight * viewHeight / s.target.Height())
 	}
 
 	// Update the position and height of the vertical scrollbar
@@ -579,7 +540,7 @@ func (s *Scroller) recalcH() {
 
 	// Adjust the scrollbar button size to the correct proportion proportion according to the style
 	if s.style.HorizontalScrollbar.AutoSizeButton {
-		s.hscroll.SetButtonSize(hscrollWidth * viewWidth / s.target.TotalWidth())
+		s.hscroll.SetButtonSize(hscrollWidth * viewWidth / s.target.Width())
 	}
 
 	// Update the position and width of the horizontal scrollbar

+ 13 - 19
gui/slider.go

@@ -164,6 +164,9 @@ func (s *Slider) setPos(pos float32) {
 func (s *Slider) onMouse(evname string, ev interface{}) {
 
 	mev := ev.(*window.MouseEvent)
+	if mev.Button != window.MouseButtonLeft {
+		return
+	}
 	switch evname {
 	case OnMouseDown:
 		s.pressed = true
@@ -172,36 +175,30 @@ func (s *Slider) onMouse(evname string, ev interface{}) {
 		} else {
 			s.posLast = mev.Ypos
 		}
-		s.root.SetMouseFocus(s)
-		s.root.SetKeyFocus(s)
+		Manager().SetKeyFocus(s)
+		Manager().SetCursorFocus(s)
 	case OnMouseUp:
 		s.pressed = false
-		if !s.cursorOver {
-			s.root.SetCursorNormal()
-		}
-		s.root.SetMouseFocus(nil)
+		Manager().SetCursorFocus(nil)
 	default:
 		return
 	}
-	s.root.StopPropagation(Stop3D)
 }
 
 // onCursor process subscribed cursor events
 func (s *Slider) onCursor(evname string, ev interface{}) {
 
 	if evname == OnCursorEnter {
-		s.root.SetScrollFocus(s)
+		s.cursorOver = true
 		if s.horiz {
-			s.root.SetCursorHResize()
+			window.Get().SetCursor(window.HResizeCursor)
 		} else {
-			s.root.SetCursorVResize()
+			window.Get().SetCursor(window.VResizeCursor)
 		}
-		s.cursorOver = true
 		s.update()
 	} else if evname == OnCursorLeave {
-		s.root.SetScrollFocus(nil)
-		s.root.SetCursorNormal()
 		s.cursorOver = false
+		window.Get().SetCursor(window.ArrowCursor)
 		s.update()
 	} else if evname == OnCursor {
 		if !s.pressed {
@@ -222,7 +219,6 @@ func (s *Slider) onCursor(evname string, ev interface{}) {
 		}
 		s.setPos(pos)
 	}
-	s.root.StopPropagation(Stop3D)
 }
 
 // onScroll process subscribed scroll events
@@ -232,7 +228,6 @@ func (s *Slider) onScroll(evname string, ev interface{}) {
 	v := s.pos
 	v += sev.Yoffset * 0.01
 	s.setPos(v)
-	s.root.StopPropagation(Stop3D)
 }
 
 // onKey process subscribed key events
@@ -242,7 +237,7 @@ func (s *Slider) onKey(evname string, ev interface{}) {
 	delta := float32(0.01)
 	// Horizontal slider
 	if s.horiz {
-		switch kev.Keycode {
+		switch kev.Key {
 		case window.KeyLeft:
 			s.setPos(s.pos - delta)
 		case window.KeyRight:
@@ -252,7 +247,7 @@ func (s *Slider) onKey(evname string, ev interface{}) {
 		}
 		// Vertical slider
 	} else {
-		switch kev.Keycode {
+		switch kev.Key {
 		case window.KeyDown:
 			s.setPos(s.pos - delta)
 		case window.KeyUp:
@@ -261,7 +256,6 @@ func (s *Slider) onKey(evname string, ev interface{}) {
 			return
 		}
 	}
-	s.root.StopPropagation(Stop3D)
 }
 
 // onResize process subscribed resize events
@@ -277,7 +271,7 @@ func (s *Slider) update() {
 		s.applyStyle(&s.styles.Disabled)
 		return
 	}
-	if s.cursorOver {
+	if s.cursorOver || s.pressed {
 		s.applyStyle(&s.styles.Over)
 		return
 	}

+ 9 - 9
gui/splitter.go

@@ -116,6 +116,9 @@ func (s *Splitter) onResize(evname string, ev interface{}) {
 func (s *Splitter) onMouse(evname string, ev interface{}) {
 
 	mev := ev.(*window.MouseEvent)
+	if mev.Button != window.MouseButtonLeft {
+		return
+	}
 	switch evname {
 	case OnMouseDown:
 		s.pressed = true
@@ -124,14 +127,12 @@ func (s *Splitter) onMouse(evname string, ev interface{}) {
 		} else {
 			s.posLast = mev.Ypos
 		}
-		s.root.SetMouseFocus(&s.spacer)
+		Manager().SetCursorFocus(&s.spacer)
 	case OnMouseUp:
 		s.pressed = false
-		s.root.SetCursorNormal()
-		s.root.SetMouseFocus(nil)
-	default:
+		window.Get().SetCursor(window.ArrowCursor)
+		Manager().SetCursorFocus(nil)
 	}
-	s.root.StopPropagation(Stop3D)
 }
 
 // onCursor receives subscribed cursor events over the spacer panel
@@ -139,14 +140,14 @@ func (s *Splitter) onCursor(evname string, ev interface{}) {
 
 	if evname == OnCursorEnter {
 		if s.horiz {
-			s.root.SetCursorHResize()
+			window.Get().SetCursor(window.HResizeCursor)
 		} else {
-			s.root.SetCursorVResize()
+			window.Get().SetCursor(window.VResizeCursor)
 		}
 		s.mouseOver = true
 		s.update()
 	} else if evname == OnCursorLeave {
-		s.root.SetCursorNormal()
+		window.Get().SetCursor(window.ArrowCursor)
 		s.mouseOver = false
 		s.update()
 	} else if evname == OnCursor {
@@ -168,7 +169,6 @@ func (s *Splitter) onCursor(evname string, ev interface{}) {
 		s.setSplit(pos)
 		s.recalc()
 	}
-	s.root.StopPropagation(Stop3D)
 }
 
 // setSplit sets the validated and clamped split position from the received value.

+ 3 - 16
gui/tabbar.go

@@ -71,7 +71,7 @@ func NewTabBar(width, height float32) *TabBar {
 
 	// Create list for contained tabs not visible
 	tb.list = NewVList(0, 0)
-	tb.list.Subscribe(OnMouseOut, func(evname string, ev interface{}) {
+	tb.list.Subscribe(OnMouseDownOut, func(evname string, ev interface{}) {
 		tb.list.SetVisible(false)
 	})
 	tb.list.Subscribe(OnChange, tb.onListChange)
@@ -250,7 +250,6 @@ func (tb *TabBar) onCursor(evname string, ev interface{}) {
 	default:
 		return
 	}
-	tb.root.StopPropagation(StopAll)
 }
 
 // onListButtonMouse process subscribed MouseButton events over the list button
@@ -264,7 +263,6 @@ func (tb *TabBar) onListButton(evname string, ev interface{}) {
 	default:
 		return
 	}
-	tb.root.StopPropagation(StopAll)
 }
 
 // onListChange process OnChange event from the tab list
@@ -441,24 +439,14 @@ func (tab *Tab) onCursor(evname string, ev interface{}) {
 	default:
 		return
 	}
-	tab.header.root.StopPropagation(StopAll)
 }
 
 // onMouse process subscribed mouse events over the tab header
 func (tab *Tab) onMouseHeader(evname string, ev interface{}) {
 
-	switch evname {
-	case OnMouseDown:
-		mev := ev.(*window.MouseEvent)
-		if mev.Button == window.MouseButtonLeft {
-			tab.tb.SetSelected(tab.tb.TabPosition(tab))
-		} else {
-			tab.header.Dispatch(OnRightClick, ev)
-		}
-	default:
-		return
+	if evname == OnMouseDown && ev.(*window.MouseEvent).Button == window.MouseButtonLeft {
+		tab.tb.SetSelected(tab.tb.TabPosition(tab))
 	}
-	tab.header.root.StopPropagation(StopAll)
 }
 
 // onMouseIcon process subscribed mouse events over the tab close icon
@@ -470,7 +458,6 @@ func (tab *Tab) onMouseIcon(evname string, ev interface{}) {
 	default:
 		return
 	}
-	tab.header.root.StopPropagation(StopAll)
 }
 
 // SetText sets the text of the tab header

+ 15 - 32
gui/table.go

@@ -282,8 +282,6 @@ func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
 	t.Panel.Add(&t.statusPanel)
 
 	// Subscribe to events
-	t.Panel.Subscribe(OnCursorEnter, t.onCursor)
-	t.Panel.Subscribe(OnCursorLeave, t.onCursor)
 	t.Panel.Subscribe(OnCursor, t.onCursorPos)
 	t.Panel.Subscribe(OnScroll, t.onScroll)
 	t.Panel.Subscribe(OnMouseUp, t.onMouse)
@@ -779,18 +777,6 @@ func (t *Table) removeRow(row int) {
 	trow.Dispose()
 }
 
-// onCursor process subscribed cursor events
-func (t *Table) onCursor(evname string, ev interface{}) {
-
-	switch evname {
-	case OnCursorEnter:
-		t.root.SetScrollFocus(t)
-	case OnCursorLeave:
-		t.root.SetScrollFocus(nil)
-	}
-	t.root.StopPropagation(Stop3D)
-}
-
 // onCursorPos process subscribed cursor position events
 func (t *Table) onCursorPos(evname string, ev interface{}) {
 
@@ -814,7 +800,7 @@ func (t *Table) onCursorPos(evname string, ev interface{}) {
 				found = true
 				t.resizeCol = ci
 				t.resizerX = c.xr
-				t.root.SetCursorHResize()
+				window.Get().SetCursor(window.HResizeCursor)
 			}
 			break
 		}
@@ -822,17 +808,16 @@ func (t *Table) onCursorPos(evname string, ev interface{}) {
 	// If column not found but previously was near a resizable column,
 	// resets the the window cursor.
 	if !found && t.resizeCol >= 0 {
-		t.root.SetCursorNormal()
+		window.Get().SetCursor(window.ArrowCursor)
 		t.resizeCol = -1
 	}
-	t.root.StopPropagation(Stop3D)
 }
 
 // onMouseEvent process subscribed mouse events
 func (t *Table) onMouse(evname string, ev interface{}) {
 
 	e := ev.(*window.MouseEvent)
-	t.root.SetKeyFocus(t)
+	Manager().SetKeyFocus(t)
 	switch evname {
 	case OnMouseDown:
 		// If over a resizable column border, shows the resizer panel
@@ -870,7 +855,7 @@ func (t *Table) onMouse(evname string, ev interface{}) {
 		if t.resizing {
 			t.resizing = false
 			t.resizerPanel.SetVisible(false)
-			t.root.SetCursorNormal()
+			window.Get().SetCursor(window.ArrowCursor)
 			// Calculates the new column width
 			cx, _ := t.ContentCoords(e.Xpos, e.Ypos)
 			c := t.header.cols[t.resizeCol]
@@ -880,26 +865,25 @@ func (t *Table) onMouse(evname string, ev interface{}) {
 	default:
 		return
 	}
-	t.root.StopPropagation(StopAll)
 }
 
 // onKeyEvent receives subscribed key events for this table
 func (t *Table) onKey(evname string, ev interface{}) {
 
 	kev := ev.(*window.KeyEvent)
-	if kev.Keycode == window.KeyUp && kev.Mods == 0 {
+	if kev.Key == window.KeyUp && kev.Mods == 0 {
 		t.selPrev()
-	} else if kev.Keycode == window.KeyDown && kev.Mods == 0 {
+	} else if kev.Key == window.KeyDown && kev.Mods == 0 {
 		t.selNext()
-	} else if kev.Keycode == window.KeyPageUp && kev.Mods == 0 {
+	} else if kev.Key == window.KeyPageUp && kev.Mods == 0 {
 		t.prevPage()
-	} else if kev.Keycode == window.KeyPageDown && kev.Mods == 0 {
+	} else if kev.Key == window.KeyPageDown && kev.Mods == 0 {
 		t.nextPage()
-	} else if kev.Keycode == window.KeyPageUp && kev.Mods == window.ModControl {
+	} else if kev.Key == window.KeyPageUp && kev.Mods == window.ModControl {
 		t.firstPage()
-	} else if kev.Keycode == window.KeyPageDown && kev.Mods == window.ModControl {
+	} else if kev.Key == window.KeyPageDown && kev.Mods == window.ModControl {
 		t.lastPage()
-	} else if kev.Keycode == window.KeyEnter && kev.Mods == window.ModControl {
+	} else if kev.Key == window.KeyEnter && kev.Mods == window.ModControl {
 		if t.selType == TableSelMultiRow {
 			t.toggleRowSel(t.rowCursor)
 		}
@@ -922,21 +906,20 @@ func (t *Table) onScroll(evname string, ev interface{}) {
 	} else if sev.Yoffset < 0 {
 		t.scrollDown(1)
 	}
-	t.root.StopPropagation(Stop3D)
 }
 
 // onRicon receives subscribed events for column header right icon
 func (t *Table) onRicon(evname string, c *tableColHeader) {
 
-	icon := tableSortedNoneIcon
+	ico := tableSortedNoneIcon
 	var asc bool
 	if c.sorted == tableSortedNone || c.sorted == tableSortedDesc {
 		c.sorted = tableSortedAsc
-		icon = tableSortedAscIcon
+		ico = tableSortedAscIcon
 		asc = false
 	} else {
 		c.sorted = tableSortedDesc
-		icon = tableSortedDescIcon
+		ico = tableSortedDescIcon
 		asc = true
 	}
 
@@ -947,7 +930,7 @@ func (t *Table) onRicon(evname string, c *tableColHeader) {
 		asString = false
 	}
 	t.SortColumn(c.id, asString, asc)
-	c.ricon.SetText(string(icon))
+	c.ricon.SetText(string(ico))
 }
 
 // findClick finds where in the table the specified mouse click event

+ 7 - 15
gui/tree.go

@@ -43,6 +43,7 @@ type TreeNode struct {
 	parNode  *TreeNode // Parent node
 	items    []IPanel  // List of node items
 	expanded bool      // Node expanded flag
+	litem    *ListItem // Reference to ListItem
 }
 
 // NewTree creates and returns a pointer to a new tree widget.
@@ -61,7 +62,6 @@ func (t *Tree) Initialize(width, height float32) {
 	t.SetStyles(&StyleDefault().Tree)
 	t.List.Subscribe(OnKeyDown, t.onKey)
 	t.List.Subscribe(OnKeyUp, t.onKey)
-	t.List.Subscribe(OnCursor, t.onCursor)
 }
 
 // SetStyles sets the tree styles overriding the default style.
@@ -92,7 +92,7 @@ func (t *Tree) InsertNodeAt(pos int, text string) *TreeNode {
 	n := newTreeNode(text, t, nil)
 	n.update()
 	n.recalc()
-	t.List.InsertAt(pos, n)
+	n.litem = t.List.InsertAt(pos, n)
 	return n
 }
 
@@ -103,7 +103,7 @@ func (t *Tree) AddNode(text string) *TreeNode {
 	n := newTreeNode(text, t, nil)
 	n.update()
 	n.recalc()
-	t.List.Add(n)
+	n.litem = t.List.Add(n)
 	return n
 }
 
@@ -162,13 +162,6 @@ func (t *Tree) FindChild(child IPanel) (*TreeNode, int) {
 	return nil, -1
 }
 
-// onCursor receives subscribed cursor events over the tree
-func (t *Tree) onCursor(evname string, ev interface{}) {
-
-	// Do not propagate any cursor events
-	t.root.StopPropagation(StopAll)
-}
-
 // onKey receives key down events for the embedded list
 func (t *Tree) onKey(evname string, ev interface{}) {
 
@@ -180,13 +173,12 @@ func (t *Tree) onKey(evname string, ev interface{}) {
 	// If item is not a tree node, dispatch event to item
 	node, ok := item.(*TreeNode)
 	if !ok {
-		item.SetRoot(t.root)
 		item.GetPanel().Dispatch(evname, ev)
 		return
 	}
 	// If not enter key pressed, ignore
 	kev := ev.(*window.KeyEvent)
-	if evname != OnKeyDown || kev.Keycode != window.KeyEnter {
+	if evname != OnKeyDown || kev.Key != window.KeyEnter {
 		return
 	}
 	// Toggles the expansion state of the node
@@ -343,9 +335,9 @@ func (n *TreeNode) onMouse(evname string, ev interface{}) {
 		n.update()
 		n.recalc()
 		n.updateItems()
-	default:
-		return
 	}
+
+	n.litem.onMouse(evname, ev)
 }
 
 // level returns the level of this node from the start of the tree
@@ -439,7 +431,7 @@ func (n *TreeNode) insertItems(pos int) int {
 		node, ok := ipanel.(*TreeNode)
 		if ok {
 			node.update()
-			n.tree.List.InsertAt(pos, ipanel)
+			node.litem = n.tree.List.InsertAt(pos, ipanel)
 			n.tree.List.SetItemPadLeftAt(pos, padLeft)
 			pos++
 			pos = node.insertItems(pos)

+ 4 - 7
gui/window.go

@@ -326,7 +326,7 @@ func newWindowTitle(win *Window, text string) *WindowTitle {
 	wt.closeButton = NewButton("")
 	wt.closeButton.SetIcon(icon.Close)
 	wt.closeButton.Subscribe(OnCursorEnter, func(s string, i interface{}) {
-		wt.win.root.SetCursorNormal()
+		window.Get().SetCursor(window.ArrowCursor)
 	})
 	wt.closeButton.Subscribe(OnClick, func(s string, i interface{}) {
 		wt.win.Parent().GetNode().Remove(wt.win)
@@ -367,25 +367,23 @@ func (wt *WindowTitle) onMouse(evname string, ev interface{}) {
 		wt.pressed = true
 		wt.mouseX = mev.Xpos
 		wt.mouseY = mev.Ypos
-		wt.win.root.SetMouseFocus(wt)
+		Manager().SetCursorFocus(wt)
 	case OnMouseUp:
 		wt.pressed = false
-		wt.win.root.SetMouseFocus(nil)
+		Manager().SetCursorFocus(nil)
 	default:
 		return
 	}
-	wt.win.root.StopPropagation(Stop3D)
 }
 
 // onCursor process subscribed cursor events over the window title.
 func (wt *WindowTitle) onCursor(evname string, ev interface{}) {
 
 	if evname == OnCursorLeave {
-		wt.win.root.SetCursorNormal()
+		window.Get().SetCursor(window.ArrowCursor)
 		wt.pressed = false
 	} else if evname == OnCursor {
 		if !wt.pressed {
-			wt.win.root.StopPropagation(Stop3D)
 			return
 		}
 		cev := ev.(*window.CursorEvent)
@@ -397,7 +395,6 @@ func (wt *WindowTitle) onCursor(evname string, ev interface{}) {
 		posY := wt.win.Position().Y - dy
 		wt.win.SetPosition(posX, posY)
 	}
-	wt.win.root.StopPropagation(Stop3D)
 }
 
 // applyStyle applies the specified WindowTitleStyle.