Browse Source

Require reference to INode when initializing a Node

Daniel Salvadori 6 years ago
parent
commit
3d7a9f0e74

+ 1 - 1
audio/listener.go

@@ -20,7 +20,7 @@ type Listener struct {
 func NewListener() *Listener {
 
 	l := new(Listener)
-	l.Node.Init()
+	l.Node.Init(l)
 	return l
 }
 

+ 1 - 1
audio/player.go

@@ -48,7 +48,7 @@ func NewPlayer(filename string) (*Player, error) {
 
 	// Creates player
 	p := new(Player)
-	p.Node.Init()
+	p.Node.Init(p)
 	p.af = af
 
 	// Generate buffers names

+ 1 - 1
camera/camera.go

@@ -33,7 +33,7 @@ type Camera struct {
 // Normally used by other camera types which embed this base camera.
 func (cam *Camera) Initialize() {
 
-	cam.Node.Init()
+	cam.Node.Init(cam)
 	cam.target.Set(0, 0, 0)
 	cam.up.Set(0, 1, 0)
 	cam.SetDirection(0, 0, -1)

+ 110 - 40
core/node.go

@@ -14,17 +14,35 @@ import (
 type INode interface {
 	IDispatcher
 	GetNode() *Node
+	GetINode() INode
+	Visible() bool
+	SetVisible(state bool)
+	Name() string
+	SetName(string)
+	Parent() INode
+	Children() []INode
+	IsAncestorOf(INode) bool
+	LowestCommonAncestor(INode) INode
 	UpdateMatrixWorld()
 	Raycast(*Raycaster, *[]Intersect)
 	BoundingBox() math32.Box3
 	Render(gs *gls.GLS)
 	Clone() INode
 	Dispose()
+	Position() math32.Vector3
+	Rotation() math32.Vector3
+	Scale() math32.Vector3
 }
 
+// Node events.
+const (
+	OnDescendant = "core.OnDescendant" // Dispatched when a descendent is added or removed
+)
+
 // Node represents an object in 3D space existing within a hierarchy.
 type Node struct {
 	Dispatcher                 // Embedded event dispatcher
+	inode          INode       // The INode associated with this Node
 	parent         INode       // Parent node
 	children       []INode     // Children nodes
 	name           string      // Optional node name
@@ -35,29 +53,32 @@ type Node struct {
 	userData       interface{} // Generic user data
 
 	// Spatial properties
-	position    math32.Vector3    // Node position in 3D space (relative to parent)
-	scale       math32.Vector3    // Node scale (relative to parent)
-	direction   math32.Vector3    // Initial direction (relative to parent)
-	rotation    math32.Vector3    // Node rotation specified in Euler angles (relative to parent)
-	quaternion  math32.Quaternion // Node rotation specified as a Quaternion (relative to parent)
-	matrix      math32.Matrix4    // Local transform matrix. Contains all position/rotation/scale information (relative to parent)
-	matrixWorld math32.Matrix4    // World transform matrix. Contains all absolute position/rotation/scale information (i.e. relative to very top parent, generally the scene)
+	position   math32.Vector3    // Node position in 3D space (relative to parent)
+	scale      math32.Vector3    // Node scale (relative to parent)
+	direction  math32.Vector3    // Initial direction (relative to parent)
+	rotation   math32.Vector3    // Node rotation specified in Euler angles (relative to parent)
+	quaternion math32.Quaternion // Node rotation specified as a Quaternion (relative to parent)
+
+	// Local transform matrix stores position/rotation/scale relative to parent
+	matrix math32.Matrix4
+	// World transform matrix stores position/rotation/scale relative to highest ancestor (generally the scene)
+	matrixWorld math32.Matrix4
 }
 
 // NewNode returns a pointer to a new Node.
 func NewNode() *Node {
 
 	n := new(Node)
-	n.Init()
+	n.Init(n)
 	return n
 }
 
 // Init initializes the node.
 // Normally called by other types which embed a Node.
-func (n *Node) Init() {
+func (n *Node) Init(inode INode) {
 
 	n.Dispatcher.Initialize()
-
+	n.inode = inode
 	n.children = make([]INode, 0)
 	n.visible = true
 
@@ -69,6 +90,19 @@ func (n *Node) Init() {
 	n.quaternion.Set(0, 0, 0, 1)
 	n.matrix.Identity()
 	n.matrixWorld.Identity()
+
+	// Subscribe to events
+	n.Subscribe(OnDescendant, func(evname string, ev interface{}) {
+		if n.parent != nil {
+			n.parent.Dispatch(evname, ev)
+		}
+	})
+}
+
+// GetINode returns the INode associated with this Node.
+func (n *Node) GetINode() INode {
+
+	return n.inode
 }
 
 // GetNode satisfies the INode interface
@@ -79,10 +113,10 @@ func (n *Node) GetNode() *Node {
 }
 
 // Raycast satisfies the INode interface.
-func (n *Node) Raycast(rc *Raycaster, intersects *[]Intersect) {
-}
+func (n *Node) Raycast(rc *Raycaster, intersects *[]Intersect) {}
 
 // BoundingBox satisfies the INode interface.
+// Computes union of own bounding box with those of all descendents.
 func (n *Node) BoundingBox() math32.Box3 {
 
 	bbox := math32.Box3{n.position, n.position}
@@ -94,12 +128,10 @@ func (n *Node) BoundingBox() math32.Box3 {
 }
 
 // Render satisfies the INode interface.
-func (n *Node) Render(gs *gls.GLS) {
-}
+func (n *Node) Render(gs *gls.GLS) {}
 
 // Dispose satisfies the INode interface.
-func (n *Node) Dispose() {
-}
+func (n *Node) Dispose() {}
 
 // Clone clones the Node and satisfies the INode interface.
 func (n *Node) Clone() INode {
@@ -137,12 +169,6 @@ func (n *Node) Clone() INode {
 	return clone
 }
 
-// SetParent sets the parent.
-func (n *Node) SetParent(iparent INode) {
-
-	n.parent = iparent
-}
-
 // Parent returns the parent.
 func (n *Node) Parent() INode {
 
@@ -283,44 +309,48 @@ func (n *Node) Children() []INode {
 // If the specified node had a parent, the specified node is removed from the original parent's list of children.
 func (n *Node) Add(ichild INode) *Node {
 
-	n.setParentOf(ichild)
+	setParent(n.GetINode(), ichild)
 	n.children = append(n.children, ichild)
+	n.Dispatch(OnDescendant, nil)
 	return n
 }
 
 // AddAt adds the specified node to the list of children at the specified index and sets its parent pointer.
 // If the specified node had a parent, the specified node is removed from the original parent's list of children.
-func (n *Node) AddAt(idx int, ichild INode) {
+func (n *Node) AddAt(idx int, ichild INode) *Node {
 
 	// Validate position
 	if idx < 0 || idx > len(n.children) {
 		panic("Node.AddAt: invalid position")
 	}
 
-	n.setParentOf(ichild)
+	setParent(n.GetINode(), ichild)
 
 	// Insert child in the specified position
 	n.children = append(n.children, nil)
 	copy(n.children[idx+1:], n.children[idx:])
 	n.children[idx] = ichild
+
+	n.Dispatch(OnDescendant, nil)
+
+	return n
 }
 
-// setParentOf is used by Add and AddAt.
+// setParent is used by Add and AddAt.
 // It verifies that the node is not being added to itself and sets the parent pointer of the specified node.
 // If the specified node had a parent, the specified node is removed from the original parent's list of children.
 // It does not add the specified node to the list of children.
-func (n *Node) setParentOf(ichild INode) {
+func setParent(parent INode, child INode) {
 
-	child := ichild.GetNode()
-	if n == child {
+	if parent.GetNode() == child.GetNode() {
 		panic("Node.{Add,AddAt}: object can't be added as a child of itself")
 	}
 	// If the specified node already has a parent,
 	// remove it from the original parent's list of children
-	if child.parent != nil {
-		child.parent.GetNode().Remove(ichild)
+	if child.Parent() != nil {
+		child.Parent().GetNode().Remove(child)
 	}
-	child.parent = n
+	child.GetNode().parent = parent
 }
 
 // ChildAt returns the child at the specified index.
@@ -343,6 +373,44 @@ func (n *Node) ChildIndex(ichild INode) int {
 	return -1
 }
 
+// IsAncestorOf returns whether this node is an ancestor of the specified node. Returns true if they are the same.
+func (n *Node) IsAncestorOf(desc INode) bool {
+
+	if desc == nil {
+		return false
+	}
+	if n == desc.GetNode() {
+		return true
+	}
+	for _, child := range n.Children() {
+		res := child.IsAncestorOf(desc)
+		if res {
+			return res
+		}
+	}
+	return false
+}
+
+// LowestCommonAncestor returns the common ancestor of this node and the specified node if any.
+func (n *Node) LowestCommonAncestor(other INode) INode {
+
+	if other == nil {
+		return nil
+	}
+	n1 := n.GetINode()
+	for n1 != nil {
+		n2 := other
+		for n2 != nil {
+			if n1 == n2 {
+				return n1
+			}
+			n2 = n2.Parent()
+		}
+		n1 = n1.Parent()
+	}
+	return nil
+}
+
 // Remove removes the specified INode from the list of children.
 // Returns true if found or false otherwise.
 func (n *Node) Remove(ichild INode) bool {
@@ -353,6 +421,7 @@ func (n *Node) Remove(ichild INode) bool {
 			n.children[len(n.children)-1] = nil
 			n.children = n.children[:len(n.children)-1]
 			ichild.GetNode().parent = nil
+			n.Dispatch(OnDescendant, nil)
 			return true
 		}
 	}
@@ -374,6 +443,8 @@ func (n *Node) RemoveAt(idx int) INode {
 	n.children[len(n.children)-1] = nil
 	n.children = n.children[:len(n.children)-1]
 
+	n.Dispatch(OnDescendant, nil)
+
 	return child
 }
 
@@ -459,19 +530,19 @@ func (n *Node) TranslateOnAxis(axis *math32.Vector3, dist float32) {
 // TranslateX translates the specified distance on the local X axis.
 func (n *Node) TranslateX(dist float32) {
 
-	n.TranslateOnAxis(&math32.Vector3{1,0,0}, dist)
+	n.TranslateOnAxis(&math32.Vector3{1, 0, 0}, dist)
 }
 
 // TranslateY translates the specified distance on the local Y axis.
 func (n *Node) TranslateY(dist float32) {
 
-	n.TranslateOnAxis(&math32.Vector3{0,1,0}, dist)
+	n.TranslateOnAxis(&math32.Vector3{0, 1, 0}, dist)
 }
 
 // TranslateZ translates the specified distance on the local Z axis.
 func (n *Node) TranslateZ(dist float32) {
 
-	n.TranslateOnAxis(&math32.Vector3{0,0,1}, dist)
+	n.TranslateOnAxis(&math32.Vector3{0, 0, 1}, dist)
 }
 
 // SetRotation sets the global rotation in Euler angles (radians).
@@ -554,19 +625,19 @@ func (n *Node) RotateOnAxis(axis *math32.Vector3, angle float32) {
 // RotateX rotates around the local X axis the specified angle in radians.
 func (n *Node) RotateX(x float32) {
 
-	n.RotateOnAxis(&math32.Vector3{1,0,0}, x)
+	n.RotateOnAxis(&math32.Vector3{1, 0, 0}, x)
 }
 
 // RotateY rotates around the local Y axis the specified angle in radians.
 func (n *Node) RotateY(y float32) {
 
-	n.RotateOnAxis(&math32.Vector3{0,1,0}, y)
+	n.RotateOnAxis(&math32.Vector3{0, 1, 0}, y)
 }
 
 // RotateZ rotates around the local Z axis the specified angle in radians.
 func (n *Node) RotateZ(z float32) {
 
-	n.RotateOnAxis(&math32.Vector3{0,0,1}, z)
+	n.RotateOnAxis(&math32.Vector3{0, 0, 1}, z)
 }
 
 // SetQuaternion sets the quaternion based on the specified quaternion unit multiples.
@@ -750,8 +821,7 @@ func (n *Node) UpdateMatrixWorld() {
 	if n.parent == nil {
 		n.matrixWorld = n.matrix
 	} else {
-		parent := n.parent.GetNode()
-		n.matrixWorld.MultiplyMatrices(&parent.matrixWorld, &n.matrix)
+		n.matrixWorld.MultiplyMatrices(&n.parent.GetNode().matrixWorld, &n.matrix)
 	}
 	// Update this Node children matrices
 	for _, ichild := range n.children {

+ 4 - 4
graphic/graphic.go

@@ -58,17 +58,17 @@ type IGraphic interface {
 // NewGraphic creates and returns a pointer to a new graphic object with
 // the specified geometry and OpenGL primitive.
 // The created graphic object, though, has not materials.
-func NewGraphic(igeom geometry.IGeometry, mode uint32) *Graphic {
+func NewGraphic(igr IGraphic, igeom geometry.IGeometry, mode uint32) *Graphic {
 
 	gr := new(Graphic)
-	return gr.Init(igeom, mode)
+	return gr.Init(igr, igeom, mode)
 }
 
 // Init initializes a Graphic type embedded in another type
 // with the specified geometry and OpenGL mode.
-func (gr *Graphic) Init(igeom geometry.IGeometry, mode uint32) *Graphic {
+func (gr *Graphic) Init(igr IGraphic, igeom geometry.IGeometry, mode uint32) *Graphic {
 
-	gr.Node.Init()
+	gr.Node.Init(igr)
 	gr.igeom = igeom
 	gr.mode = mode
 	gr.materials = make([]GraphicMaterial, 0)

+ 1 - 1
graphic/line_strip.go

@@ -22,7 +22,7 @@ type LineStrip struct {
 func NewLineStrip(igeom geometry.IGeometry, imat material.IMaterial) *LineStrip {
 
 	l := new(LineStrip)
-	l.Graphic.Init(igeom, gls.LINE_STRIP)
+	l.Graphic.Init(l, igeom, gls.LINE_STRIP)
 	l.AddMaterial(l, imat, 0, 0)
 	l.uniMVPm.Init("MVP")
 	return l

+ 1 - 1
graphic/lines.go

@@ -29,7 +29,7 @@ func NewLines(igeom geometry.IGeometry, imat material.IMaterial) *Lines {
 // Init initializes the Lines object and adds the specified material.
 func (l *Lines) Init(igeom geometry.IGeometry, imat material.IMaterial) {
 
-	l.Graphic.Init(igeom, gls.LINES)
+	l.Graphic.Init(l, igeom, gls.LINES)
 	l.AddMaterial(l, imat, 0, 0)
 	l.uniMVPm.Init("MVP")
 }

+ 2 - 2
graphic/mesh.go

@@ -15,7 +15,7 @@ import (
 // Mesh is a Graphic with uniforms for the model, view, projection, and normal matrices.
 type Mesh struct {
 	Graphic             // Embedded graphic
-	uniMm  gls.Uniform  // Model matrix uniform location cache
+	uniMm   gls.Uniform // Model matrix uniform location cache
 	uniMVm  gls.Uniform // Model view matrix uniform location cache
 	uniMVPm gls.Uniform // Model view projection matrix uniform cache
 	uniNm   gls.Uniform // Normal matrix uniform cache
@@ -34,7 +34,7 @@ func NewMesh(igeom geometry.IGeometry, imat material.IMaterial) *Mesh {
 // Init initializes the Mesh and its uniforms.
 func (m *Mesh) Init(igeom geometry.IGeometry, imat material.IMaterial) {
 
-	m.Graphic.Init(igeom, gls.TRIANGLES)
+	m.Graphic.Init(m, igeom, gls.TRIANGLES)
 
 	// Initialize uniforms
 	m.uniMm.Init("ModelMatrix")

+ 1 - 1
graphic/points.go

@@ -23,7 +23,7 @@ type Points struct {
 func NewPoints(igeom geometry.IGeometry, imat material.IMaterial) *Points {
 
 	p := new(Points)
-	p.Graphic.Init(igeom, gls.POINTS)
+	p.Graphic.Init(p, igeom, gls.POINTS)
 	if imat != nil {
 		p.AddMaterial(p, imat, 0, 0)
 	}

+ 1 - 1
graphic/skybox.go

@@ -34,7 +34,7 @@ func NewSkybox(data SkyboxData) (*Skybox, error) {
 	skybox := new(Skybox)
 
 	geom := geometry.NewCube(1)
-	skybox.Graphic.Init(geom, gls.TRIANGLES)
+	skybox.Graphic.Init(skybox, geom, gls.TRIANGLES)
 	skybox.Graphic.SetCullable(false)
 
 	for i := 0; i < 6; i++ {

+ 1 - 1
graphic/sprite.go

@@ -48,7 +48,7 @@ func NewSprite(width, height float32, imat material.IMaterial) *Sprite {
 			AddAttrib(gls.VertexTexcoord),
 	)
 
-	s.Graphic.Init(geom, gls.TRIANGLES)
+	s.Graphic.Init(s, geom, gls.TRIANGLES)
 	s.AddMaterial(s, imat, 0, 0)
 
 	s.uniMVPM.Init("MVP")

+ 2 - 2
gui/button.go

@@ -23,7 +23,7 @@ import (
 
 // Button represents a button GUI element
 type Button struct {
-	*Panel                  // Embedded Panel
+	Panel                   // Embedded Panel
 	Label     *Label        // Label panel
 	image     *Image        // pointer to button image (may be nil)
 	icon      *Label        // pointer to button icon (may be nil
@@ -52,7 +52,7 @@ func NewButton(text string) *Button {
 	b.styles = &StyleDefault().Button
 
 	// Initializes the button panel
-	b.Panel = NewPanel(0, 0)
+	b.Panel.Initialize(b, 0, 0)
 
 	// Subscribe to panel events
 	b.Subscribe(OnKeyDown, b.onKey)

+ 4 - 4
gui/chart.go

@@ -66,7 +66,7 @@ func NewChart(width, height float32) *Chart {
 // It is normally used to initialize a Chart embedded in a struct
 func (ch *Chart) Init(width float32, height float32) {
 
-	ch.Panel.Initialize(width, height)
+	ch.Panel.Initialize(ch, width, height)
 	ch.left = 40
 	ch.bottom = 20
 	ch.top = 10
@@ -479,7 +479,7 @@ func newChartScaleX(chart *Chart, lines int, color *math32.Color) *chartScaleX {
 	geom.AddVBO(gls.NewVBO(positions).AddAttrib(gls.VertexPosition))
 
 	// Initializes the panel graphic
-	gr := graphic.NewGraphic(geom, gls.LINES)
+	gr := graphic.NewGraphic(sx, geom, gls.LINES)
 	sx.mat.Init(color)
 	gr.AddMaterial(sx, &sx.mat, 0, 0)
 	sx.Panel.InitializeGraphic(chart.ContentWidth(), chart.ContentHeight(), gr)
@@ -564,7 +564,7 @@ func newChartScaleY(chart *Chart, lines int, color *math32.Color) *chartScaleY {
 	geom.AddVBO(gls.NewVBO(positions).AddAttrib(gls.VertexPosition))
 
 	// Initializes the panel with this graphic
-	gr := graphic.NewGraphic(geom, gls.LINES)
+	gr := graphic.NewGraphic(sy, geom, gls.LINES)
 	sy.mat.Init(color)
 	gr.AddMaterial(sy, &sy.mat, 0, 0)
 	sy.Panel.InitializeGraphic(chart.ContentWidth(), chart.ContentHeight(), gr)
@@ -634,7 +634,7 @@ func newGraph(chart *Chart, color *math32.Color, data []float32) *Graph {
 	geom.AddVBO(lg.vbo)
 
 	// Initializes the panel with this graphic
-	gr := graphic.NewGraphic(geom, gls.LINE_STRIP)
+	gr := graphic.NewGraphic(lg, geom, gls.LINE_STRIP)
 	lg.mat.Init(&lg.color)
 	gr.AddMaterial(lg, &lg.mat, 0, 0)
 	lg.Panel.InitializeGraphic(lg.chart.ContentWidth(), lg.chart.ContentHeight(), gr)

+ 1 - 1
gui/checkradio.go

@@ -75,7 +75,7 @@ func newCheckRadio(check bool, text string) *CheckRadio {
 	}
 
 	// Initialize panel
-	cb.Panel.Initialize(0, 0)
+	cb.Panel.Initialize(cb, 0, 0)
 
 	// Subscribe to events
 	cb.Panel.Subscribe(OnKeyDown, cb.onKey)

+ 1 - 1
gui/dropdown.go

@@ -41,7 +41,7 @@ func NewDropDown(width float32, item *ImageLabel) *DropDown {
 	dd.styles = &StyleDefault().DropDown
 	dd.litem = item
 
-	dd.Panel.Initialize(width, 0)
+	dd.Panel.Initialize(dd, width, 0)
 	dd.Panel.Subscribe(OnKeyDown, dd.onKeyEvent)
 	dd.Panel.Subscribe(OnMouseDown, dd.onMouse)
 	dd.Panel.Subscribe(OnCursorEnter, dd.onCursor)

+ 1 - 1
gui/folder.go

@@ -47,7 +47,7 @@ func NewFolder(text string, width float32, contentPanel IPanel) *Folder {
 // It is normally used when the folder is embedded in another object.
 func (f *Folder) Initialize(text string, width float32, contentPanel IPanel) {
 
-	f.Panel.Initialize(width, 0)
+	f.Panel.Initialize(f, width, 0)
 	f.styles = &StyleDefault().Folder
 
 	// Initialize label

+ 1 - 1
gui/image.go

@@ -39,7 +39,7 @@ func NewImageFromRGBA(rgba *image.RGBA) *Image {
 func NewImageFromTex(tex *texture.Texture2D) *Image {
 
 	i := new(Image)
-	i.Panel.Initialize(0, 0)
+	i.Panel.Initialize(i, 0, 0)
 	i.tex = tex
 	i.Panel.SetContentSize(float32(i.tex.Width()), float32(i.tex.Height()))
 	i.Material().AddTexture(i.tex)

+ 1 - 1
gui/imagelabel.go

@@ -39,7 +39,7 @@ func NewImageLabel(text string) *ImageLabel {
 	il := new(ImageLabel)
 
 	// Initializes the panel
-	il.Panel.Initialize(0, 0)
+	il.Panel.Initialize(il, 0, 0)
 	il.Panel.Subscribe(OnResize, func(evname string, ev interface{}) { il.recalc() })
 
 	// Initializes the label

+ 1 - 1
gui/itemscroller.go

@@ -290,7 +290,7 @@ func (s *ItemScroller) initialize(vert bool, width, height float32) {
 
 	s.vert = vert
 	s.autoButtonSize = true
-	s.Panel.Initialize(width, height)
+	s.Panel.Initialize(s, width, height)
 	s.styles = &StyleDefault().ItemScroller
 
 	s.Panel.Subscribe(OnCursorEnter, s.onCursor)

+ 1 - 1
gui/label.go

@@ -55,7 +55,7 @@ func NewLabelWithFont(msg string, font *text.Font) *Label {
 func (l *Label) initialize(msg string, font *text.Font) {
 
 	l.font = font
-	l.Panel.Initialize(0, 0)
+	l.Panel.Initialize(l, 0, 0)
 
 	// TODO: Remove this hack in an elegant way e.g. set the label style depending of if it's an icon or text label and have two defaults (one for icon labels one for text tabels)
 	if font != StyleDefault().FontIcon {

+ 1 - 1
gui/list.go

@@ -466,7 +466,7 @@ func (li *List) update() {
 func newListItem(list *List, item IPanel) *ListItem {
 
 	litem := new(ListItem)
-	litem.Panel.Initialize(0, 0)
+	litem.Panel.Initialize(litem, 0, 0)
 	litem.item = item
 	litem.list = list
 	litem.Panel.Add(item)

+ 2 - 2
gui/menu.go

@@ -150,7 +150,7 @@ func NewMenuBar() *Menu {
 func NewMenu() *Menu {
 
 	m := new(Menu)
-	m.Panel.Initialize(0, 0)
+	m.Panel.Initialize(m, 0, 0)
 	m.styles = &StyleDefault().Menu
 	m.items = make([]*MenuItem, 0)
 	m.Panel.Subscribe(OnCursorEnter, m.onCursor)
@@ -571,7 +571,7 @@ func (m *Menu) recalcBar(setSize bool) {
 func newMenuItem(text string, styles *MenuItemStyles) *MenuItem {
 
 	mi := new(MenuItem)
-	mi.Panel.Initialize(0, 0)
+	mi.Panel.Initialize(mi, 0, 0)
 	mi.styles = styles
 	if text != "" {
 		mi.label = NewLabel(text)

+ 127 - 152
gui/panel.go

@@ -63,26 +63,38 @@ type Panel struct {
 	width            float32            // external width in pixels
 	height           float32            // external height in pixels
 	mat              *material.Material // panel material
-	marginSizes      RectBounds         // external margin sizes in pixel coordinates
-	borderSizes      RectBounds         // border sizes in pixel coordinates
-	paddingSizes     RectBounds         // padding sizes in pixel coordinates
-	content          Rect               // current content rectangle in pixel coordinates
-	pospix           math32.Vector3     // absolute position in pixels
-	posclip          math32.Vector3     // position in clip (NDC) coordinates
-	wclip            float32            // width in clip coordinates
-	hclip            float32            // height in clip coordinates
-	xmin             float32            // minimum absolute x this panel can use
-	xmax             float32            // maximum absolute x this panel can use
-	ymin             float32            // minimum absolute y this panel can use
-	ymax             float32            // maximum absolute y this panel can use
-	bounded          bool               // panel is bounded by its parent
-	enabled          bool               // enable event processing
-	cursorEnter      bool               // mouse enter dispatched
-	layout           ILayout            // current layout for children
-	layoutParams     interface{}        // current layout parameters used by container panel
-	uniMatrix        gls.Uniform        // model matrix uniform location cache
-	uniPanel         gls.Uniform        // panel parameters uniform location cache
-	udata            struct {           // Combined uniform data 8 * vec4
+
+	bounded   bool // Whether panel is bounded by its parent
+	enabled   bool // Whether event should be processed for this panel
+	focusable bool // Whether panel can be focused on mouse down (so it receives key events)
+
+	layout       ILayout     // current layout for children
+	layoutParams interface{} // current layout parameters used by container panel
+
+	marginSizes  RectBounds // external margin sizes in pixel coordinates
+	borderSizes  RectBounds // border sizes in pixel coordinates
+	paddingSizes RectBounds // padding sizes in pixel coordinates
+	content      Rect       // current content rectangle in pixel coordinates
+
+	// Absolute screen position and external size in pixels
+	pospix math32.Vector3
+	width  float32
+	height float32
+
+	// Absolute screen position and size in clip (NDC) coordinates
+	posclip math32.Vector3
+	wclip   float32
+	hclip   float32
+
+	xmin float32 // minimum absolute x this panel can use
+	xmax float32 // maximum absolute x this panel can use
+	ymin float32 // minimum absolute y this panel can use
+	ymax float32 // maximum absolute y this panel can use
+
+	// Uniforms sent to shader
+	uniMatrix gls.Uniform // model matrix uniform location cache
+	uniPanel  gls.Uniform // panel parameters uniform location cache
+	udata     struct {    // Combined uniform data 8 * vec4
 		bounds        math32.Vector4 // panel bounds in texture coordinates
 		borders       math32.Vector4 // panel borders in texture coordinates
 		paddings      math32.Vector4 // panel paddings in texture coordinates
@@ -124,17 +136,17 @@ var panelQuadGeometry *geometry.Geometry
 func NewPanel(width, height float32) *Panel {
 
 	p := new(Panel)
-	p.Initialize(width, height)
+	p.Initialize(p, width, height)
 	return p
 }
 
 // Initialize initializes this panel and is normally used by other types which embed a panel.
-func (p *Panel) Initialize(width, height float32) {
+func (p *Panel) Initialize(ipan IPanel, width, height float32) { // TODO rename to Init
 
 	p.width = width
 	p.height = height
 
-	// If necessary, creates panel quad geometry
+	// If first time, create panel quad geometry
 	if panelQuadGeometry == nil {
 
 		// Builds array with vertex positions and texture coordinates
@@ -165,7 +177,7 @@ func (p *Panel) Initialize(width, height float32) {
 	p.mat.SetShaderUnique(true)
 
 	// Initialize graphic
-	p.Graphic = graphic.NewGraphic(panelQuadGeometry.Incref(), gls.TRIANGLES)
+	p.Graphic = graphic.NewGraphic(ipan, panelQuadGeometry.Incref(), gls.TRIANGLES)
 	p.AddMaterial(p, p.mat, 0, 0)
 
 	// Initialize uniforms location caches
@@ -177,6 +189,14 @@ func (p *Panel) Initialize(width, height float32) {
 	p.bounded = true
 	p.enabled = true
 	p.resize(width, height, true)
+
+	// Subscribe to OnDescendant to update Z-positions starting from "root" panels
+	p.Subscribe(core.OnDescendant, func(evname string, ev interface{}) {
+		if p.Parent() == nil {
+			// This is a "root" panel
+			p.setZ(0, deltaZunb)
+		}
+	})
 }
 
 // InitializeGraphic initializes this panel with a different graphic
@@ -204,46 +224,8 @@ func (p *Panel) GetPanel() *Panel {
 	return p
 }
 
-// SetRoot satisfies the IPanel interface.
-// Sets the pointer to the root panel for this panel and all its children.
-func (p *Panel) SetRoot(root *Root) {
-
-	p.root = root
-	for i := 0; i < len(p.Children()); i++ {
-		cpan := p.Children()[i].(IPanel).GetPanel()
-		cpan.SetRoot(root)
-	}
-}
-
-// Root satisfies the IPanel interface
-// Returns the pointer to the root panel for this panel's root.
-func (p *Panel) Root() *Root {
-
-	return p.root
-}
-
-// LostKeyFocus satisfies the IPanel interface and is called by gui root
-// container when the panel loses the key focus
-func (p *Panel) LostKeyFocus() {
-
-}
-
-// TotalHeight satisfies the IPanel interface and returns the total
-// height of this panel considering visible not bounded children
-func (p *Panel) TotalHeight() float32 {
-
-	return p.height
-}
-
-// TotalWidth satisfies the IPanel interface and returns the total
-// width of this panel considering visible not bounded children
-func (p *Panel) TotalWidth() float32 {
-
-	return p.width
-}
-
-// Material returns a pointer for this panel core.Material
-func (p *Panel) Material() *material.Material {
+// Material returns a pointer for this panel's Material
+func (p *Panel) Material() *material.Material { // TODO remove - allow for setting and getting a single texture
 
 	return p.mat
 }
@@ -520,19 +502,13 @@ func (p *Panel) Pospix() math32.Vector3 {
 }
 
 // Add adds a child panel to this one
+// TODO DOC This overrides Node because only IPanels can be children of an IPanel
 func (p *Panel) Add(ichild IPanel) *Panel {
 
 	p.Node.Add(ichild)
-	node := ichild.GetPanel()
-	node.SetParent(p)
-	if p.root != nil {
-		ichild.SetRoot(p.root)
-		p.root.setZ(0, deltaZunb)
-	}
 	if p.layout != nil {
 		p.layout.Recalc(p)
 	}
-	p.Dispatch(OnChild, nil)
 	return p
 }
 
@@ -544,7 +520,6 @@ func (p *Panel) Remove(ichild IPanel) bool {
 		if p.layout != nil {
 			p.layout.Recalc(p)
 		}
-		p.Dispatch(OnChild, nil)
 	}
 	return res
 }
@@ -566,14 +541,17 @@ func (p *Panel) SetBounded(bounded bool) {
 // the Engine before rendering the frame.
 func (p *Panel) UpdateMatrixWorld() {
 
-	// Panel has no parent should be the root panel
 	par := p.Parent()
 	if par == nil {
 		p.updateBounds(nil)
 		// Panel has parent
 	} else {
-		parpan := par.(*Panel)
-		p.updateBounds(parpan)
+		parpan, ok := par.(IPanel)
+		if ok {
+			p.updateBounds(parpan.GetPanel())
+		} else {
+			p.updateBounds(nil)
+		}
 	}
 	// Update this panel children
 	for _, ichild := range p.Children() {
@@ -680,9 +658,9 @@ func (p *Panel) ContentCoords(wx, wy float32) (float32, float32) {
 // NDC2Pix converts the specified NDC coordinates (-1,1) to relative pixel coordinates
 // for this panel content area.
 // 0,0      1,0        0,0       w,0
-// +--------+          +---------+
-// |        | -------> |         |
-// +--------+          +---------+
+//  +--------+          +---------+
+//  |        | -------> |         |
+//  +--------+          +---------+
 // 0,-1     1,-1       0,h       w,h
 func (p *Panel) NDC2Pix(nx, ny float32) (x, y float32) {
 
@@ -693,11 +671,11 @@ func (p *Panel) NDC2Pix(nx, ny float32) (x, y float32) {
 
 // Pix2NDC converts the specified relative pixel coordinates to NDC coordinates for this panel
 // content area
-// 0,0       w,0       0,0      1,0
-// +---------+         +---------+
-// |         | ------> |         |
-// +---------+         +---------+
-// 0,h       w,h       0,-1     1,-1
+// 0,0       w,0       0,0       1,0
+//  +---------+         +---------+
+//  |         | ------> |         |
+//  +---------+         +---------+
+// 0,h       w,h       0,-1      1,-1
 func (p *Panel) Pix2NDC(px, py float32) (nx, ny float32) {
 
 	w := p.ContentWidth()
@@ -730,6 +708,8 @@ 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...
+
 	// Bounded panel
 	if p.bounded {
 		p.SetPositionZ(z)
@@ -754,19 +734,13 @@ func (p *Panel) setZ(z, zunb float32) (float32, float32) {
 // bounds considering the bounds of its parent
 func (p *Panel) updateBounds(par *Panel) {
 
-	// If no parent, it is the root panel
+	// If this panel has no parent, its pixel position is its Position
 	if par == nil {
-		p.pospix = p.Position()
-		p.xmin = -math.MaxFloat32
-		p.ymin = -math.MaxFloat32
-		p.xmax = math.MaxFloat32
-		p.ymax = math.MaxFloat32
-		p.udata.bounds = math32.Vector4{0, 0, 1, 1}
-		return
-	}
-	// If this panel is bounded to its parent, its coordinates are relative
-	// to the parent internal content rectangle.
-	if p.bounded {
+		p.pospix.X = p.Position().X
+		p.pospix.Y = p.Position().Y
+		// If this panel is bounded to its parent, its coordinates are relative
+		// to the parent internal content rectangle.
+	} else if p.bounded {
 		p.pospix.X = p.Position().X + par.pospix.X + par.marginSizes.Left + par.borderSizes.Left + par.paddingSizes.Left
 		p.pospix.Y = p.Position().Y + par.pospix.Y + par.marginSizes.Top + par.borderSizes.Top + par.paddingSizes.Top
 		// Otherwise its coordinates are relative to the parent outer coordinates.
@@ -779,62 +753,59 @@ func (p *Panel) updateBounds(par *Panel) {
 	p.ymin = p.pospix.Y
 	p.xmax = p.pospix.X + p.width
 	p.ymax = p.pospix.Y + p.height
-	if p.bounded {
-		// Get the parent content area minimum and maximum absolute coordinates in pixels
-		pxmin := par.pospix.X + par.marginSizes.Left + par.borderSizes.Left + par.paddingSizes.Left
-		if pxmin < par.xmin {
-			pxmin = par.xmin
-		}
-		pymin := par.pospix.Y + par.marginSizes.Top + par.borderSizes.Top + par.paddingSizes.Top
-		if pymin < par.ymin {
-			pymin = par.ymin
-		}
-		pxmax := par.pospix.X + par.width - (par.marginSizes.Right + par.borderSizes.Right + par.paddingSizes.Right)
-		if pxmax > par.xmax {
-			pxmax = par.xmax
-		}
-		pymax := par.pospix.Y + par.height - (par.marginSizes.Bottom + par.borderSizes.Bottom + par.paddingSizes.Bottom)
-		if pymax > par.ymax {
-			pymax = par.ymax
-		}
-		// Update this panel minimum x and y coordinates.
-		if p.xmin < pxmin {
-			p.xmin = pxmin
-		}
-		if p.ymin < pymin {
-			p.ymin = pymin
-		}
-		// Update this panel maximum x and y coordinates.
-		if p.xmax > pxmax {
-			p.xmax = pxmax
-		}
-		if p.ymax > pymax {
-			p.ymax = pymax
-		}
+	// Set default bounds to be entire panel texture
+	p.udata.bounds = math32.Vector4{0, 0, 1, 1}
+	// If this panel has no parent or is unbounded then the default bounds are correct
+	if par == nil || !p.bounded {
+		return
+	}
+	// From here on panel has parent and is bounded by parent
+	// TODO why not use par.xmin, etc here ? shouldn't they be already updated?
+	// Get the parent content area minimum and maximum absolute coordinates in pixels
+	pxmin := par.pospix.X + par.marginSizes.Left + par.borderSizes.Left + par.paddingSizes.Left
+	if pxmin < par.xmin {
+		pxmin = par.xmin
+	}
+	pymin := par.pospix.Y + par.marginSizes.Top + par.borderSizes.Top + par.paddingSizes.Top
+	if pymin < par.ymin {
+		pymin = par.ymin
+	}
+	pxmax := par.pospix.X + par.width - (par.marginSizes.Right + par.borderSizes.Right + par.paddingSizes.Right)
+	if pxmax > par.xmax {
+		pxmax = par.xmax
+	}
+	pymax := par.pospix.Y + par.height - (par.marginSizes.Bottom + par.borderSizes.Bottom + par.paddingSizes.Bottom)
+	if pymax > par.ymax {
+		pymax = par.ymax
+	}
+	// Update this panel minimum x and y coordinates.
+	if p.xmin < pxmin {
+		p.xmin = pxmin
+	}
+	if p.ymin < pymin {
+		p.ymin = pymin
+	}
+	// Update this panel maximum x and y coordinates.
+	if p.xmax > pxmax {
+		p.xmax = pxmax
+	}
+	if p.ymax > pymax {
+		p.ymax = pymax
 	}
-	// Set default values for bounds in texture coordinates
-	xmintex := float32(0.0)
-	ymintex := float32(0.0)
-	xmaxtex := float32(1.0)
-	ymaxtex := float32(1.0)
 	// If this panel is bounded to its parent, calculates the bounds
 	// for clipping in texture coordinates
-	if p.bounded {
-		if p.pospix.X < p.xmin {
-			xmintex = (p.xmin - p.pospix.X) / p.width
-		}
-		if p.pospix.Y < p.ymin {
-			ymintex = (p.ymin - p.pospix.Y) / p.height
-		}
-		if p.pospix.X+p.width > p.xmax {
-			xmaxtex = (p.xmax - p.pospix.X) / p.width
-		}
-		if p.pospix.Y+p.height > p.ymax {
-			ymaxtex = (p.ymax - p.pospix.Y) / p.height
-		}
+	if p.pospix.X < p.xmin {
+		p.udata.bounds.X = (p.xmin - p.pospix.X) / p.width
+	}
+	if p.pospix.Y < p.ymin {
+		p.udata.bounds.Y = (p.ymin - p.pospix.Y) / p.height
+	}
+	if p.pospix.X+p.width > p.xmax {
+		p.udata.bounds.Z = (p.xmax - p.pospix.X) / p.width
+	}
+	if p.pospix.Y+p.height > p.ymax {
+		p.udata.bounds.W = (p.ymax - p.pospix.Y) / p.height
 	}
-	// Sets bounds uniform
-	p.udata.bounds = math32.Vector4{xmintex, ymintex, xmaxtex, ymaxtex}
 }
 
 // calcWidth calculates the panel external width in pixels
@@ -869,14 +840,18 @@ func (p *Panel) resize(width, height float32, dispatch bool) {
 
 	width = math32.Round(width)
 	height = math32.Round(height)
-	// Adjusts content width
-	p.content.Width = width -
-		p.marginSizes.Left - p.marginSizes.Right -
-		p.borderSizes.Left - p.borderSizes.Right -
-		p.paddingSizes.Left - p.paddingSizes.Right
+
+	// Adjust content width
+	p.content.Width = width - p.marginSizes.Left - p.marginSizes.Right - p.borderSizes.Left - p.borderSizes.Right - p.paddingSizes.Left - p.paddingSizes.Right
 	if p.content.Width < 0 {
 		p.content.Width = 0
 	}
+	// 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 widths
 	padding.Width = p.paddingSizes.Left + p.content.Width + p.paddingSizes.Right
 	border.Width = p.borderSizes.Left + padding.Width + p.borderSizes.Right

+ 2 - 2
gui/scrollbar.go

@@ -84,11 +84,11 @@ func (sb *ScrollBar) initialize(width, height float32, vertical bool) {
 
 	sb.styles = &StyleDefault().ScrollBar
 	sb.vertical = vertical
-	sb.Panel.Initialize(width, height)
+	sb.Panel.Initialize(sb, width, height)
 	sb.Panel.Subscribe(OnMouseDown, sb.onMouse)
 
 	// Initialize scrollbar button
-	sb.button.Panel.Initialize(0, 0)
+	sb.button.Panel.Initialize(&sb.button, 0, 0)
 	sb.button.Panel.Subscribe(OnMouseDown, sb.button.onMouse)
 	sb.button.Panel.Subscribe(OnMouseUp, sb.button.onMouse)
 	sb.button.Panel.Subscribe(OnCursor, sb.button.onCursor)

+ 1 - 1
gui/scroller.go

@@ -98,7 +98,7 @@ func NewScroller(width, height float32, mode ScrollMode, target IPanel) *Scrolle
 // initialize initializes this scroller and can be called by other types which embed a scroller
 func (s *Scroller) initialize(width, height float32, mode ScrollMode, target IPanel) {
 
-	s.Panel.Initialize(width, height)
+	s.Panel.Initialize(s, width, height)
 	s.style = &StyleDefault().Scroller
 	s.target = target
 	s.Panel.Add(s.target)

+ 2 - 2
gui/slider.go

@@ -71,7 +71,7 @@ func newSlider(horiz bool, width, height float32) *Slider {
 	s.scaleFactor = 1.0
 
 	// Initialize main panel
-	s.Panel.Initialize(width, height)
+	s.Panel.Initialize(s, width, height)
 	s.Panel.Subscribe(OnMouseDown, s.onMouse)
 	s.Panel.Subscribe(OnMouseUp, s.onMouse)
 	s.Panel.Subscribe(OnCursor, s.onCursor)
@@ -84,7 +84,7 @@ func newSlider(horiz bool, width, height float32) *Slider {
 	s.Panel.Subscribe(OnEnable, func(evname string, ev interface{}) { s.update() })
 
 	// Initialize slider panel
-	s.slider.Initialize(0, 0)
+	s.slider.Initialize(&s.slider, 0, 0)
 	s.Panel.Add(&s.slider)
 
 	s.recalc()

+ 4 - 4
gui/splitter.go

@@ -58,18 +58,18 @@ func newSplitter(horiz bool, width, height float32) *Splitter {
 	s := new(Splitter)
 	s.horiz = horiz
 	s.styles = &StyleDefault().Splitter
-	s.Panel.Initialize(width, height)
+	s.Panel.Initialize(s, width, height)
 
 	// Initialize left/top panel
-	s.P0.Initialize(0, 0)
+	s.P0.Initialize(&s.P0, 0, 0)
 	s.Panel.Add(&s.P0)
 
 	// Initialize right/bottom panel
-	s.P1.Initialize(0, 0)
+	s.P1.Initialize(&s.P1, 0, 0)
 	s.Panel.Add(&s.P1)
 
 	// Initialize spacer panel
-	s.spacer.Initialize(0, 0)
+	s.spacer.Initialize(&s.spacer, 0, 0)
 	s.Panel.Add(&s.spacer)
 
 	if horiz {

+ 4 - 4
gui/tabbar.go

@@ -60,13 +60,13 @@ func NewTabBar(width, height float32) *TabBar {
 
 	// Creates new TabBar
 	tb := new(TabBar)
-	tb.Initialize(width, height)
+	tb.Initialize(tb, width, height)
 	tb.styles = &StyleDefault().TabBar
 	tb.tabs = make([]*Tab, 0)
 	tb.selected = -1
 
 	// Creates separator panel (between the tab headers and content panel)
-	tb.separator.Initialize(0, 0)
+	tb.separator.Initialize(&tb.separator, 0, 0)
 	tb.Add(&tb.separator)
 
 	// Create list for contained tabs not visible
@@ -407,13 +407,13 @@ func newTab(text string, tb *TabBar, styles *TabStyles) *Tab {
 	tab.tb = tb
 	tab.styles = styles
 	// Setup the header panel
-	tab.header.Initialize(0, 0)
+	tab.header.Initialize(&tab.header, 0, 0)
 	tab.label = NewLabel(text)
 	tab.iconClose = NewIcon(styles.IconClose)
 	tab.header.Add(tab.label)
 	tab.header.Add(tab.iconClose)
 	// Creates the bottom panel
-	tab.bottom.Initialize(0, 0)
+	tab.bottom.Initialize(&tab.bottom, 0, 0)
 	tab.bottom.SetBounded(false)
 	tab.bottom.SetColor4(&tab.styles.Selected.BgColor)
 	tab.header.Add(&tab.bottom)

+ 8 - 8
gui/table.go

@@ -193,12 +193,12 @@ type tableCell struct {
 func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
 
 	t := new(Table)
-	t.Panel.Initialize(width, height)
+	t.Panel.Initialize(t, width, height)
 	t.styles = &StyleDefault().Table
 	t.rowCursor = -1
 
 	// Initialize table header
-	t.header.Initialize(0, 0)
+	t.header.Initialize(&t.header, 0, 0)
 	t.header.cmap = make(map[string]*tableColHeader)
 	t.header.cols = make([]*tableColHeader, 0)
 
@@ -215,7 +215,7 @@ func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
 		}
 		// Creates a column header
 		c := new(tableColHeader)
-		c.Initialize(0, 0)
+		c.Initialize(c, 0, 0)
 		t.applyHeaderStyle(&c.Panel, false)
 		c.label = NewLabel(cdesc.Header)
 		c.Add(c.label)
@@ -260,7 +260,7 @@ func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
 		t.header.Panel.Add(c)
 	}
 	// Creates last header
-	t.header.lastPan.Initialize(0, 0)
+	t.header.lastPan.Initialize(&t.header, 0, 0)
 	t.applyHeaderStyle(&t.header.lastPan, true)
 	t.header.Panel.Add(&t.header.lastPan)
 
@@ -268,13 +268,13 @@ func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
 	t.Panel.Add(&t.header)
 
 	// Creates resizer panel
-	t.resizerPanel.Initialize(t.styles.Resizer.Width, 0)
+	t.resizerPanel.Initialize(&t.resizerPanel, t.styles.Resizer.Width, 0)
 	t.resizerPanel.SetVisible(false)
 	t.applyResizerStyle()
 	t.Panel.Add(&t.resizerPanel)
 
 	// Creates status panel
-	t.statusPanel.Initialize(0, 0)
+	t.statusPanel.Initialize(&t.statusPanel, 0, 0)
 	t.statusPanel.SetVisible(false)
 	t.statusLabel = NewLabel("")
 	t.applyStatusStyle()
@@ -693,12 +693,12 @@ func (t *Table) insertRow(row int, values map[string]interface{}) {
 
 	// Creates tableRow panel
 	trow := new(tableRow)
-	trow.Initialize(0, 0)
+	trow.Initialize(trow, 0, 0)
 	trow.cells = make([]*tableCell, 0)
 	for ci := 0; ci < len(t.header.cols); ci++ {
 		// Creates tableRow cell panel
 		cell := new(tableCell)
-		cell.Initialize(0, 0)
+		cell.Initialize(cell, 0, 0)
 		cell.label.initialize("", StyleDefault().Font)
 		cell.Add(&cell.label)
 		trow.cells = append(trow.cells, cell)

+ 1 - 1
gui/tree.go

@@ -204,7 +204,7 @@ func (t *Tree) onKey(evname string, ev interface{}) {
 func newTreeNode(text string, tree *Tree, parNode *TreeNode) *TreeNode {
 
 	n := new(TreeNode)
-	n.Panel.Initialize(0, 0)
+	n.Panel.Initialize(n, 0, 0)
 
 	// Initialize node label
 	n.label.initialize(text, StyleDefault().Font)

+ 26 - 27
gui/window.go

@@ -5,9 +5,9 @@
 package gui
 
 import (
+	"github.com/g3n/engine/gui/assets/icon"
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/window"
-	"github.com/g3n/engine/gui/assets/icon"
 )
 
 /*********************************************
@@ -37,8 +37,8 @@ type Window struct {
 	title       *WindowTitle // internal optional title panel
 	client      Panel        // internal client panel
 	resizable   bool         // Specifies whether the window is resizable
-	drag        bool    // Whether the mouse buttons is pressed (i.e. when dragging)
-	dragPadding float32 // Extra width used to resize (in addition to border sizes)
+	drag        bool         // Whether the mouse buttons is pressed (i.e. when dragging)
+	dragPadding float32      // Extra width used to resize (in addition to border sizes)
 
 	// To keep track of which window borders the cursor is over
 	overTop    bool
@@ -72,7 +72,7 @@ func NewWindow(width, height float32) *Window {
 	w := new(Window)
 	w.styles = &StyleDefault().Window
 
-	w.Panel.Initialize(width, height)
+	w.Panel.Initialize(w, width, height)
 	w.Panel.Subscribe(OnMouseDown, w.onMouse)
 	w.Panel.Subscribe(OnMouseUp, w.onMouse)
 	w.Panel.Subscribe(OnCursor, w.onCursor)
@@ -80,7 +80,7 @@ func NewWindow(width, height float32) *Window {
 	w.Panel.Subscribe(OnCursorLeave, w.onCursor)
 	w.Panel.Subscribe(OnResize, func(evname string, ev interface{}) { w.recalc() })
 
-	w.client.Initialize(0, 0)
+	w.client.Initialize(&w.client, 0, 0)
 	w.Panel.Add(&w.client)
 
 	w.dragPadding = 5
@@ -107,6 +107,7 @@ func (w *Window) SetTitle(text string) {
 
 	if w.title == nil {
 		w.title = newWindowTitle(w, text)
+		w.title.Subscribe(OnCursor, w.onCursor)
 		w.Panel.Add(w.title)
 	} else {
 		w.title.label.SetText(text)
@@ -139,15 +140,14 @@ func (w *Window) onMouse(evname string, ev interface{}) {
 		// If the click happened inside the draggable area, then set drag to true
 		if w.overTop || w.overRight || w.overBottom || w.overLeft {
 			w.drag = true
-			w.root.SetMouseFocus(w)
+			Manager().SetCursorFocus(w)
 		}
 	case OnMouseUp:
 		w.drag = false
-		w.root.SetMouseFocus(nil)
+		Manager().SetCursorFocus(nil)
 	default:
 		return
 	}
-	w.root.StopPropagation(StopAll)
 }
 
 // onCursor process subscribed cursor events over the window
@@ -177,7 +177,7 @@ func (w *Window) onCursor(evname string, ev interface{}) {
 			if w.overRight {
 				delta := cev.Xpos - (w.pospix.X + w.width)
 				newWidth := w.Width() + delta
-				w.SetWidth(math32.Max(newWidth, w.title.label.Width() + w.title.closeButton.Width()))
+				w.SetWidth(math32.Max(newWidth, w.title.label.Width()+w.title.closeButton.Width()))
 			}
 			if w.overBottom {
 				delta := cev.Ypos - (w.pospix.Y + w.height)
@@ -203,50 +203,49 @@ func (w *Window) onCursor(evname string, ev interface{}) {
 			// Check if cursor is on the top of the window (border + drag margin)
 			if cy <= w.borderSizes.Top {
 				w.overTop = true
-				w.root.SetCursorVResize()
+				window.Get().SetCursor(window.VResizeCursor)
 			} else {
 				w.overTop = false
 			}
 			// Check if cursor is on the bottom of the window (border + drag margin)
-			if cy >= w.height-w.borderSizes.Bottom - w.dragPadding {
+			if cy >= w.height-w.borderSizes.Bottom-w.dragPadding {
 				w.overBottom = true
 			} else {
 				w.overBottom = false
 			}
 			// Check if cursor is on the left of the window (border + drag margin)
-			if cx <= w.borderSizes.Left + w.dragPadding {
+			if cx <= w.borderSizes.Left+w.dragPadding {
 				w.overLeft = true
-				w.root.SetCursorHResize()
+				window.Get().SetCursor(window.HResizeCursor)
 			} else {
 				w.overLeft = false
 			}
 			// Check if cursor is on the right of the window (border + drag margin)
-			if cx >= w.width-w.borderSizes.Right - w.dragPadding {
+			if cx >= w.width-w.borderSizes.Right-w.dragPadding {
 				w.overRight = true
-				w.root.SetCursorHResize()
+				window.Get().SetCursor(window.HResizeCursor)
 			} else {
 				w.overRight = false
 			}
 			// Update cursor image based on cursor position
 			if (w.overTop || w.overBottom) && !w.overRight && !w.overLeft {
-				w.root.SetCursorVResize()
+				window.Get().SetCursor(window.VResizeCursor)
 			} else if (w.overRight || w.overLeft) && !w.overTop && !w.overBottom {
-				w.root.SetCursorHResize()
+				window.Get().SetCursor(window.HResizeCursor)
 			} else if (w.overRight && w.overTop) || (w.overBottom && w.overLeft) {
-				w.root.SetCursorDiagResize1()
+				window.Get().SetCursor(window.DiagResize1Cursor)
 			} else if (w.overRight && w.overBottom) || (w.overTop && w.overLeft) {
-				w.root.SetCursorDiagResize2()
+				window.Get().SetCursor(window.DiagResize2Cursor)
 			}
 			// If cursor is not near the border of the window then reset the cursor
 			if !w.overTop && !w.overRight && !w.overBottom && !w.overLeft {
-				w.root.SetCursorNormal()
+				window.Get().SetCursor(window.ArrowCursor)
 			}
 		}
 	} else if evname == OnCursorLeave {
-		w.root.SetCursorNormal()
+		window.Get().SetCursor(window.ArrowCursor)
 		w.drag = false
 	}
-	w.root.StopPropagation(StopAll)
 }
 
 // update updates the window's visual state.
@@ -296,7 +295,7 @@ func (w *Window) recalc() {
 
 // WindowTitle represents the title bar of a Window
 type WindowTitle struct {
-	Panel              // Embedded panel
+	Panel                      // Embedded panel
 	win                *Window // Window to which this title belongs
 	label              Label   // Label for the title
 	pressed            bool    // Whether the left mouse button is pressed
@@ -304,14 +303,14 @@ type WindowTitle struct {
 	closeButtonVisible bool    // Whether the close button is present
 
 	// Last mouse coordinates
-	mouseX             float32
-	mouseY             float32
+	mouseX float32
+	mouseY float32
 }
 
 // WindowTitleStyle contains the styling for a window title.
 type WindowTitleStyle struct {
 	PanelStyle
-	FgColor     math32.Color4
+	FgColor math32.Color4
 }
 
 // newWindowTitle creates and returns a pointer to a window title panel.
@@ -320,7 +319,7 @@ func newWindowTitle(win *Window, text string) *WindowTitle {
 	wt := new(WindowTitle)
 	wt.win = win
 
-	wt.Panel.Initialize(0, 0)
+	wt.Panel.Initialize(wt, 0, 0)
 	wt.label.initialize(text, StyleDefault().Font)
 	wt.Panel.Add(&wt.label)
 

+ 1 - 1
light/ambient.go

@@ -23,7 +23,7 @@ type Ambient struct {
 func NewAmbient(color *math32.Color, intensity float32) *Ambient {
 
 	la := new(Ambient)
-	la.Node.Init()
+	la.Node.Init(la)
 	la.color = *color
 	la.intensity = intensity
 	la.uni.Init("AmbientLightColor")

+ 1 - 1
light/directional.go

@@ -27,7 +27,7 @@ type Directional struct {
 func NewDirectional(color *math32.Color, intensity float32) *Directional {
 
 	ld := new(Directional)
-	ld.Node.Init()
+	ld.Node.Init(ld)
 
 	ld.color = *color
 	ld.intensity = intensity

+ 1 - 1
light/point.go

@@ -29,7 +29,7 @@ type Point struct {
 func NewPoint(color *math32.Color, intensity float32) *Point {
 
 	lp := new(Point)
-	lp.Node.Init()
+	lp.Node.Init(lp)
 	lp.color = *color
 	lp.intensity = intensity
 

+ 1 - 1
light/spot.go

@@ -33,7 +33,7 @@ type Spot struct {
 func NewSpot(color *math32.Color, intensity float32) *Spot {
 
 	l := new(Spot)
-	l.Node.Init()
+	l.Node.Init(l)
 	l.color = *color
 	l.intensity = intensity
 	l.uni.Init("SpotLight")