WIP: Broken UI - Reworking Frame Pack

This commit is contained in:
Noah 2019-04-19 13:51:27 -07:00
parent fb8f4b1029
commit 8fb579e66e
9 changed files with 309 additions and 116 deletions

9
lib/ui/constants.go Normal file
View File

@ -0,0 +1,9 @@
package ui
// Constants for the UI toolkit.
const (
// Pack.Fill values.
FillBoth = "both"
FillX = "x"
FillY = "y"
)

104
lib/ui/eg/layout/main.go Normal file
View File

@ -0,0 +1,104 @@
package main
import (
"time"
"git.kirsle.net/apps/doodle/lib/render"
"git.kirsle.net/apps/doodle/lib/render/sdl"
"git.kirsle.net/apps/doodle/lib/ui"
)
var TargetFPS = 1000 / 60
func main() {
engine := sdl.New("Test Layout GUI", 1024, 768)
if err := engine.Setup(); err != nil {
panic(err)
}
super := ui.NewSupervisor()
window := ui.NewWindow("Test Window")
window.Configure(ui.Config{
Width: 750,
Height: 400,
Background: render.Grey,
})
window.MoveTo(render.NewPoint(80, 80))
leftPanel := ui.NewFrame("Left Panel")
leftPanel.Configure(ui.Config{
// AutoResize: true,
Width: 200,
Background: render.SkyBlue,
BorderStyle: ui.BorderRaised,
BorderSize: 2,
})
window.Pack(leftPanel, ui.Pack{
Side: ui.Left,
Fill: ui.FillY,
Expand: true,
})
body := ui.NewFrame("Body Panel")
body.Configure(ui.Config{
Background: render.RGBA(255, 0, 0, 64),
BorderStyle: ui.BorderSunken,
BorderSize: 2,
})
window.Pack(body, ui.Pack{
Side: ui.Left,
Expand: true,
})
label1 := ui.NewLabel(ui.Label{
Text: "Hello world!",
Font: render.Text{
Size: 24,
Color: render.Red,
Stroke: render.Purple,
},
})
body.Pack(label1, ui.Pack{
Side: ui.Top,
})
window.Frame().SetBackground(render.Yellow)
window.Compute(engine)
// window.Present(engine, window.Point())
super.Add(window)
super.MainLoop(engine)
//
for true {
start := time.Now()
engine.Clear(render.White)
// poll for events
ev, err := engine.Poll()
if err != nil {
panic(err)
}
// escape key to close the window
if ev.EscapeKey.Now {
break
}
super.Loop(ev)
window.Compute(engine)
window.Present(engine, window.Point())
engine.Present()
// Delay to maintain the target frames per second.
var delay uint32
elapsed := time.Now().Sub(start)
tmp := elapsed / time.Millisecond
if TargetFPS-int(tmp) > 0 { // make sure it won't roll under
delay = uint32(TargetFPS - int(tmp))
}
engine.Delay(delay)
}
}

View File

@ -10,7 +10,7 @@ import (
type Frame struct { type Frame struct {
Name string Name string
BaseWidget BaseWidget
packs map[Anchor][]packedWidget packs map[Side][]packedWidget
widgets []Widget widgets []Widget
} }
@ -18,7 +18,7 @@ type Frame struct {
func NewFrame(name string) *Frame { func NewFrame(name string) *Frame {
w := &Frame{ w := &Frame{
Name: name, Name: name,
packs: map[Anchor][]packedWidget{}, packs: map[Side][]packedWidget{},
widgets: []Widget{}, widgets: []Widget{},
} }
w.IDFunc(func() string { w.IDFunc(func() string {
@ -32,7 +32,7 @@ func NewFrame(name string) *Frame {
// Setup ensures all the Frame's data is initialized and not null. // Setup ensures all the Frame's data is initialized and not null.
func (w *Frame) Setup() { func (w *Frame) Setup() {
if w.packs == nil { if w.packs == nil {
w.packs = map[Anchor][]packedWidget{} w.packs = map[Side][]packedWidget{}
} }
if w.widgets == nil { if w.widgets == nil {
w.widgets = []Widget{} w.widgets = []Widget{}

View File

@ -1,6 +1,8 @@
package ui package ui
import ( import (
"fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/apps/doodle/lib/render"
) )
@ -9,12 +11,13 @@ type Pack struct {
// Side of the parent to anchor the position to, like N, SE, W. Default // Side of the parent to anchor the position to, like N, SE, W. Default
// is Center. // is Center.
Anchor Anchor Anchor Anchor
Side Side
// If the widget is smaller than its allocated space, grow the widget // If the widget is smaller than its allocated space, grow the widget
// to fill its space in the Frame. // to fill its space in the Frame.
Fill bool Fill string // "x", "y", "both" or "" for none
FillX bool fillX bool
FillY bool fillY bool
Padding int32 // Equal padding on X and Y. Padding int32 // Equal padding on X and Y.
PadX int32 PadX int32
@ -30,8 +33,8 @@ func (w *Frame) Pack(child Widget, config ...Pack) {
} }
// Initialize the pack list for this anchor? // Initialize the pack list for this anchor?
if _, ok := w.packs[C.Anchor]; !ok { if _, ok := w.packs[C.Side]; !ok {
w.packs[C.Anchor] = []packedWidget{} w.packs[C.Side] = []packedWidget{}
} }
// Padding: if the user only provided Padding add it to both // Padding: if the user only provided Padding add it to both
@ -40,16 +43,14 @@ func (w *Frame) Pack(child Widget, config ...Pack) {
C.PadX += C.Padding C.PadX += C.Padding
C.PadY += C.Padding C.PadY += C.Padding
// Fill: true implies both directions. // Cache the full X and Y booleans.
if C.Fill { C.fillX = C.Fill == FillX || C.Fill == FillBoth
C.FillX = true C.fillY = C.Fill == FillY || C.Fill == FillBoth
C.FillY = true
}
// Adopt the child widget so it can access the Frame. // Adopt the child widget so it can access the Frame.
child.Adopt(w) child.Adopt(w)
w.packs[C.Anchor] = append(w.packs[C.Anchor], packedWidget{ w.packs[C.Side] = append(w.packs[C.Side], packedWidget{
widget: child, widget: child,
pack: C, pack: C,
}) })
@ -72,10 +73,10 @@ func (w *Frame) computePacked(e render.Engine) {
expanded = []packedWidget{} expanded = []packedWidget{}
) )
// Iterate through all anchored directions and compute how much space to // Iterate through all packed sides and compute how much space to
// reserve to contain all of their widgets. // reserve to contain all of their widgets.
for anchor := AnchorMin; anchor <= AnchorMax; anchor++ { for side := SideMin; side <= SideMax; side++ {
if _, ok := w.packs[anchor]; !ok { if _, ok := w.packs[side]; !ok {
continue continue
} }
@ -86,15 +87,15 @@ func (w *Frame) computePacked(e render.Engine) {
xDirection int32 = 1 xDirection int32 = 1
) )
if anchor.IsSouth() { // TODO: these need tuning if side == Bottom { // TODO: these need tuning
y = frameSize.H - w.BoxThickness(4) y = frameSize.H - w.BoxThickness(4)
yDirection = -1 * w.BoxThickness(4) // parent + child BoxThickness(1) = 2 yDirection = -1 * w.BoxThickness(4) // parent + child BoxThickness(1) = 2
} else if anchor == E { } else if side == Right {
x = frameSize.W - w.BoxThickness(4) x = frameSize.W - w.BoxThickness(4)
xDirection = -1 - w.BoxThickness(4) // - w.BoxThickness(2) xDirection = -1 - w.BoxThickness(4) // - w.BoxThickness(2)
} }
for _, packedWidget := range w.packs[anchor] { for _, packedWidget := range w.packs[side] {
child := packedWidget.widget child := packedWidget.widget
pack := packedWidget.pack pack := packedWidget.pack
@ -121,19 +122,17 @@ func (w *Frame) computePacked(e render.Engine) {
maxHeight = yStep + size.H + (pack.PadY * 2) maxHeight = yStep + size.H + (pack.PadY * 2)
} }
if anchor.IsSouth() { if side == Bottom {
y -= size.H - pack.PadY y -= size.H - pack.PadY
} } else if side == Right {
if anchor.IsEast() {
x -= size.W - pack.PadX x -= size.W - pack.PadX
} }
child.MoveTo(render.NewPoint(x, y)) child.MoveTo(render.NewPoint(x, y))
if anchor.IsNorth() { if side == Top {
y += size.H + pack.PadY y += size.H + pack.PadY
} } else if side == Left {
if anchor == W {
x += size.W + pack.PadX x += size.W + pack.PadX
} }
@ -147,6 +146,13 @@ func (w *Frame) computePacked(e render.Engine) {
// If we have extra space in the Frame and any expanding widgets, let the // If we have extra space in the Frame and any expanding widgets, let the
// expanding widgets grow and share the remaining space. // expanding widgets grow and share the remaining space.
computedSize := render.NewRect(maxWidth, maxHeight) computedSize := render.NewRect(maxWidth, maxHeight)
if w.fixedWidth > 0 {
computedSize.W = w.fixedWidth
}
if w.fixedHeight > 0 {
computedSize.H = w.fixedHeight
}
if len(expanded) > 0 && !frameSize.IsZero() && frameSize.Bigger(computedSize) { if len(expanded) > 0 && !frameSize.IsZero() && frameSize.Bigger(computedSize) {
// Divy up the size available. // Divy up the size available.
growBy := render.Rect{ growBy := render.Rect{
@ -154,7 +160,8 @@ func (w *Frame) computePacked(e render.Engine) {
H: ((frameSize.H - computedSize.H) / int32(len(expanded))), // - w.BoxThickness(2), H: ((frameSize.H - computedSize.H) / int32(len(expanded))), // - w.BoxThickness(2),
} }
for _, pw := range expanded { for _, pw := range expanded {
pw.widget.ResizeBy(growBy) fmt.Printf("expand %s by %s (comp size %s)\n", pw.widget.ID(), growBy, computedSize)
pw.widget.ResizeAuto(growBy)
pw.widget.Compute(e) pw.widget.Compute(e)
} }
} }
@ -172,7 +179,7 @@ func (w *Frame) computePacked(e render.Engine) {
} }
} }
// Rescan all the widgets in this anchor to re-center them // Rescan all the widgets in this side to re-center them
// in their space. // in their space.
innerFrameSize := render.NewRect( innerFrameSize := render.NewRect(
frameSize.W-w.BoxThickness(2), frameSize.W-w.BoxThickness(2),
@ -189,8 +196,8 @@ func (w *Frame) computePacked(e render.Engine) {
moved bool moved bool
) )
if pack.Anchor.IsNorth() || pack.Anchor.IsSouth() { if pack.Side == Top || pack.Side == Bottom {
if pack.FillX && resize.W < innerFrameSize.W { if pack.fillX && resize.W < innerFrameSize.W {
resize.W = innerFrameSize.W - w.BoxThickness(2) resize.W = innerFrameSize.W - w.BoxThickness(2)
resized = true resized = true
} }
@ -205,8 +212,8 @@ func (w *Frame) computePacked(e render.Engine) {
moved = true moved = true
} }
} else if pack.Anchor.IsWest() || pack.Anchor.IsEast() { } else if pack.Side == Left || pack.Side == Right {
if pack.FillY && resize.H < innerFrameSize.H { if pack.fillY && resize.H < innerFrameSize.H {
resize.H = innerFrameSize.H - w.BoxThickness(2) // BoxThickness(2) for parent + child resize.H = innerFrameSize.H - w.BoxThickness(2) // BoxThickness(2) for parent + child
// point.Y -= (w.BoxThickness(4) + child.BoxThickness(2)) // point.Y -= (w.BoxThickness(4) + child.BoxThickness(2))
moved = true moved = true
@ -225,11 +232,12 @@ func (w *Frame) computePacked(e render.Engine) {
moved = true moved = true
} }
} else { } else {
panic("unsupported pack.Anchor") panic("unsupported pack.Side")
} }
if resized && size != resize { if resized && size != resize {
child.Resize(resize) fmt.Printf("fill: resize %s to %s\n", child.ID(), resize)
child.ResizeAuto(resize)
child.Compute(e) child.Compute(e)
} }
if moved { if moved {
@ -238,7 +246,7 @@ func (w *Frame) computePacked(e render.Engine) {
} }
// if !w.FixedSize() { // if !w.FixedSize() {
w.Resize(render.NewRect( w.ResizeAuto(render.NewRect(
frameSize.W-w.BoxThickness(2), frameSize.W-w.BoxThickness(2),
frameSize.H-w.BoxThickness(2), frameSize.H-w.BoxThickness(2),
)) ))
@ -248,7 +256,10 @@ func (w *Frame) computePacked(e render.Engine) {
// Anchor is a cardinal direction. // Anchor is a cardinal direction.
type Anchor uint8 type Anchor uint8
// Anchor values. // Side of a parent widget to pack children against.
type Side uint8
// Anchor and Side constants.
const ( const (
Center Anchor = iota Center Anchor = iota
N N
@ -259,12 +270,20 @@ const (
SW SW
W W
NW NW
Top Side = iota
Left
Right
Bottom
) )
// Range of Anchor values. // Range of Anchor and Side values.
const ( const (
AnchorMin = Center AnchorMin = Center
AnchorMax = NW AnchorMax = NW
SideMin = Top
SideMax = Bottom
) )
// IsNorth returns if the anchor is N, NE or NW. // IsNorth returns if the anchor is N, NE or NW.

View File

@ -91,7 +91,7 @@ func (w *Label) Compute(e render.Engine) {
) )
if !w.FixedSize() { if !w.FixedSize() {
w.resizeAuto(render.Rect{ w.ResizeAuto(render.Rect{
W: maxRect.W + (padX * 2), W: maxRect.W + (padX * 2),
H: maxRect.H + (padY * 2), H: maxRect.H + (padY * 2),
}) })

View File

@ -3,6 +3,7 @@ package ui
import ( import (
"errors" "errors"
"sync" "sync"
"time"
"git.kirsle.net/apps/doodle/lib/events" "git.kirsle.net/apps/doodle/lib/events"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/apps/doodle/lib/render"
@ -29,12 +30,13 @@ const (
// interaction events such as mouse hovers and clicks in their general // interaction events such as mouse hovers and clicks in their general
// vicinity. // vicinity.
type Supervisor struct { type Supervisor struct {
lock sync.RWMutex lock sync.RWMutex
serial int // ID number of each widget added in order targetFPS int
widgets map[int]WidgetSlot // map of widget ID to WidgetSlot serial int // ID number of each widget added in order
hovering map[int]interface{} // map of widgets under the cursor widgets map[int]WidgetSlot // map of widget ID to WidgetSlot
clicked map[int]interface{} // map of widgets being clicked hovering map[int]interface{} // map of widgets under the cursor
dd *DragDrop clicked map[int]interface{} // map of widgets being clicked
dd *DragDrop
} }
// WidgetSlot holds a widget with a unique ID number in a sorted list. // WidgetSlot holds a widget with a unique ID number in a sorted list.
@ -46,10 +48,11 @@ type WidgetSlot struct {
// NewSupervisor creates a supervisor. // NewSupervisor creates a supervisor.
func NewSupervisor() *Supervisor { func NewSupervisor() *Supervisor {
return &Supervisor{ return &Supervisor{
widgets: map[int]WidgetSlot{}, targetFPS: 1000 / 60,
hovering: map[int]interface{}{}, widgets: map[int]WidgetSlot{},
clicked: map[int]interface{}{}, hovering: map[int]interface{}{},
dd: NewDragDrop(), clicked: map[int]interface{}{},
dd: NewDragDrop(),
} }
} }
@ -75,6 +78,43 @@ var (
ErrStopPropagation = errors.New("stop all event propagation") ErrStopPropagation = errors.New("stop all event propagation")
) )
// MainLoop starts the UI main loop, for UI-only applications.
func (s *Supervisor) MainLoop(e render.Engine) error {
for true {
start := time.Now()
e.Clear(render.Green)
// Poll for events.
ev, err := e.Poll()
if err != nil {
return err
}
// TODO: escape key to exit the main loop
if ev.EscapeKey.Now {
return nil
}
s.Loop(ev)
// Render the widgets under our care.
s.Present(e)
// Commit the pixels to screen.
e.Present()
// Delay to maintain the target FPS.
var delay uint32
elapsed := time.Now().Sub(start)
tmp := elapsed / time.Millisecond
if s.targetFPS-int(tmp) > 0 {
delay = uint32(s.targetFPS - int(tmp))
}
e.Delay(delay)
}
return nil
}
// Loop to check events and pass them to managed widgets. // Loop to check events and pass them to managed widgets.
// //
// Useful errors returned by this may be: // Useful errors returned by this may be:
@ -211,12 +251,15 @@ func (s *Supervisor) Present(e render.Engine) {
} }
// Add a widget to be supervised. // Add a widget to be supervised.
func (s *Supervisor) Add(w Widget) { func (s *Supervisor) Add(w ...Widget) {
s.lock.Lock() s.lock.Lock()
s.widgets[s.serial] = WidgetSlot{
id: s.serial, for _, child := range w {
widget: w, s.widgets[s.serial] = WidgetSlot{
id: s.serial,
widget: child,
}
s.serial++
} }
s.serial++
s.lock.Unlock() s.lock.Unlock()
} }

View File

@ -1,6 +1,8 @@
package ui package ui
import ( import (
"fmt"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/apps/doodle/lib/render"
"git.kirsle.net/apps/doodle/lib/ui/theme" "git.kirsle.net/apps/doodle/lib/ui/theme"
) )
@ -24,11 +26,13 @@ type Widget interface {
Point() render.Point Point() render.Point
MoveTo(render.Point) MoveTo(render.Point)
MoveBy(render.Point) MoveBy(render.Point)
Size() render.Rect // Return the Width and Height of the widget. Size() render.Rect // Return the Width and Height of the widget.
FixedSize() bool // Return whether the size is fixed (true) or automatic (false) FixedSize() bool // Return whether the size is fixed (true) or automatic (false)
BoxSize() render.Rect // Return the full size including the border and outline. FixedSizes() (int32, int32) // Return whether the W and H are fixed in size
BoxSize() render.Rect // Return the full size including the border and outline.
Resize(render.Rect) Resize(render.Rect)
ResizeBy(render.Rect) ResizeBy(render.Rect)
ResizeAuto(render.Rect)
Rect() render.Rect // Return the full absolute rect combining the Size() and Point() Rect() render.Rect // Return the full absolute rect combining the Size() and Point()
Handle(Event, func(render.Point)) Handle(Event, func(render.Point))
@ -107,6 +111,8 @@ type BaseWidget struct {
hidden bool hidden bool
width int32 width int32
height int32 height int32
fixedWidth int32 // values manually configured by the user,
fixedHeight int32 // and do not change
point render.Point point render.Point
margin int32 margin int32
background render.Color background render.Color
@ -152,9 +158,11 @@ func (w *BaseWidget) Configure(c Config) {
w.fixedSize = !c.AutoResize w.fixedSize = !c.AutoResize
if c.Width != 0 { if c.Width != 0 {
w.width = c.Width w.width = c.Width
w.fixedWidth = w.width
} }
if c.Height != 0 { if c.Height != 0 {
w.height = c.Height w.height = c.Height
w.fixedHeight = w.height
} }
} }
@ -235,6 +243,12 @@ func (w *BaseWidget) FixedSize() bool {
return w.fixedSize return w.fixedSize
} }
// FixedSizes returns whether the widget's Width or Height were manually set
// to hard-coded values, respectively.
func (w *BaseWidget) FixedSizes() (int32, int32) {
return w.fixedWidth, w.fixedHeight
}
// Resize sets the size of the widget to the .W and .H attributes of a rect. // Resize sets the size of the widget to the .W and .H attributes of a rect.
func (w *BaseWidget) Resize(v render.Rect) { func (w *BaseWidget) Resize(v render.Rect) {
w.fixedSize = true w.fixedSize = true
@ -249,10 +263,17 @@ func (w *BaseWidget) ResizeBy(v render.Rect) {
w.height += v.H w.height += v.H
} }
// resizeAuto sets the size of the widget but doesn't set the fixedSize flag. // ResizeAuto sets the size of the widget but doesn't set the fixedSize flag.
func (w *BaseWidget) resizeAuto(v render.Rect) { func (w *BaseWidget) ResizeAuto(v render.Rect) {
w.width = v.W if w.fixedWidth == 0 {
w.height = v.H w.width = v.W
}
if w.fixedHeight == 0 {
w.height = v.H
}
fmt.Printf("ResizeAuto(%s): fixed size=%d,%d new size=%s\n",
w.ID(), w.fixedWidth, w.fixedHeight, w.Size(),
)
} }
// BoxThickness returns the full sum of the padding, border and outline. // BoxThickness returns the full sum of the padding, border and outline.

View File

@ -50,8 +50,8 @@ func NewWindow(title string) *Window {
Background: render.Blue, Background: render.Blue,
}) })
w.body.Pack(titleBar, Pack{ w.body.Pack(titleBar, Pack{
Anchor: N, Side: Top,
Fill: true, Fill: FillX,
}) })
w.titleBar = titleBar w.titleBar = titleBar
@ -61,8 +61,8 @@ func NewWindow(title string) *Window {
Background: render.Grey, Background: render.Grey,
}) })
w.body.Pack(content, Pack{ w.body.Pack(content, Pack{
Anchor: N, Side: Top,
Fill: true, Fill: FillBoth,
}) })
w.content = content w.content = content
@ -81,6 +81,11 @@ func (w *Window) TitleBar() *Label {
return w.titleBar return w.titleBar
} }
// Frame returns the content frame of the window.
func (w *Window) Frame() *Frame {
return w.content
}
// Configure the widget. Color and style changes are passed down to the inner // Configure the widget. Color and style changes are passed down to the inner
// content frame of the window. // content frame of the window.
func (w *Window) Configure(C Config) { func (w *Window) Configure(C Config) {
@ -100,7 +105,23 @@ func (w *Window) ConfigureTitle(C Config) {
// Compute the window. // Compute the window.
func (w *Window) Compute(e render.Engine) { func (w *Window) Compute(e render.Engine) {
var size = w.Size()
w.body.Compute(e) w.body.Compute(e)
// Assign a manual Height to the title bar using its naturally computed
// height, but leave the Width empty so the frame packer can stretch it
// horizontally.
w.titleBar.Configure(Config{
Height: w.titleBar.Size().H,
})
// Shrink down the content frame to leave room for the title bar.
w.content.Resize(render.Rect{
W: size.W - w.BoxThickness(2) - w.titleBar.BoxThickness(2),
H: size.H - w.titleBar.Size().H - w.BoxThickness(4) -
((w.titleBar.Font.Padding + w.titleBar.Font.PadY) * 2),
})
} }
// Present the window. // Present the window.

View File

@ -16,7 +16,7 @@ type GUITestScene struct {
// Private widgets. // Private widgets.
Frame *ui.Frame Frame *ui.Frame
Window *ui.Frame Window *ui.Window
} }
// Name of the scene. // Name of the scene.
@ -28,8 +28,7 @@ func (s *GUITestScene) Name() string {
func (s *GUITestScene) Setup(d *Doodle) error { func (s *GUITestScene) Setup(d *Doodle) error {
s.Supervisor = ui.NewSupervisor() s.Supervisor = ui.NewSupervisor()
window := ui.NewFrame("window") window := ui.NewWindow("Widget Toolkit")
s.Window = window
window.Configure(ui.Config{ window.Configure(ui.Config{
Width: 750, Width: 750,
Height: 450, Height: 450,
@ -37,33 +36,7 @@ func (s *GUITestScene) Setup(d *Doodle) error {
BorderStyle: ui.BorderRaised, BorderStyle: ui.BorderRaised,
BorderSize: 2, BorderSize: 2,
}) })
s.Window = window
// Title Bar
titleBar := ui.NewLabel(ui.Label{
Text: "Widget Toolkit",
Font: render.Text{
Size: 12,
Color: render.White,
Stroke: render.DarkBlue,
},
})
titleBar.Configure(ui.Config{
Background: render.Blue,
})
window.Pack(titleBar, ui.Pack{
Anchor: ui.N,
Fill: true,
})
// Window Body
body := ui.NewFrame("Window Body")
body.Configure(ui.Config{
Background: render.Yellow,
})
window.Pack(body, ui.Pack{
Anchor: ui.N,
Expand: true,
})
// Left Frame // Left Frame
leftFrame := ui.NewFrame("Left Frame") leftFrame := ui.NewFrame("Left Frame")
@ -73,9 +46,9 @@ func (s *GUITestScene) Setup(d *Doodle) error {
BorderSize: 4, BorderSize: 4,
Width: 100, Width: 100,
}) })
body.Pack(leftFrame, ui.Pack{ window.Pack(leftFrame, ui.Pack{
Anchor: ui.W, Side: ui.Left,
FillY: true, FillY: true,
}) })
// Some left frame buttons. // Some left frame buttons.
@ -89,9 +62,9 @@ func (s *GUITestScene) Setup(d *Doodle) error {
}) })
s.Supervisor.Add(btn) s.Supervisor.Add(btn)
leftFrame.Pack(btn, ui.Pack{ leftFrame.Pack(btn, ui.Pack{
Anchor: ui.N, Side: ui.Top,
FillX: true, FillX: true,
PadY: 2, PadY: 2,
}) })
} }
@ -101,8 +74,8 @@ func (s *GUITestScene) Setup(d *Doodle) error {
Background: render.White, Background: render.White,
BorderSize: 0, BorderSize: 0,
}) })
body.Pack(frame, ui.Pack{ window.Pack(frame, ui.Pack{
Anchor: ui.W, Side: ui.Left,
Expand: true, Expand: true,
Fill: true, Fill: true,
}) })
@ -115,9 +88,9 @@ func (s *GUITestScene) Setup(d *Doodle) error {
BorderSize: 2, BorderSize: 2,
Width: 80, Width: 80,
}) })
body.Pack(rightFrame, ui.Pack{ window.Pack(rightFrame, ui.Pack{
Anchor: ui.W, Side: ui.Right,
Fill: true, Fill: true,
}) })
// A grid of buttons. // A grid of buttons.
@ -139,7 +112,7 @@ func (s *GUITestScene) Setup(d *Doodle) error {
d.Flash("%s clicked", btn) d.Flash("%s clicked", btn)
}) })
rowFrame.Pack(btn, ui.Pack{ rowFrame.Pack(btn, ui.Pack{
Anchor: ui.W, Side: ui.Left,
Expand: true, Expand: true,
FillX: true, FillX: true,
}) })
@ -147,8 +120,8 @@ func (s *GUITestScene) Setup(d *Doodle) error {
})(row, col, rowFrame) })(row, col, rowFrame)
} }
rightFrame.Pack(rowFrame, ui.Pack{ rightFrame.Pack(rowFrame, ui.Pack{
Anchor: ui.N, Side: ui.Top,
Fill: true, Fill: true,
}) })
} }
@ -160,7 +133,8 @@ func (s *GUITestScene) Setup(d *Doodle) error {
Color: render.Black, Color: render.Black,
}, },
}), ui.Pack{ }), ui.Pack{
Anchor: ui.NW, Side: ui.Top,
Anchor: ui.W,
Padding: 2, Padding: 2,
}) })
@ -172,7 +146,8 @@ func (s *GUITestScene) Setup(d *Doodle) error {
}), }),
) )
frame.Pack(cb, ui.Pack{ frame.Pack(cb, ui.Pack{
Anchor: ui.NW, Side: ui.Top,
Anchor: ui.W,
Padding: 4, Padding: 4,
}) })
cb.Supervise(s.Supervisor) cb.Supervise(s.Supervisor)
@ -184,7 +159,8 @@ func (s *GUITestScene) Setup(d *Doodle) error {
Color: render.Red, Color: render.Red,
}, },
}), ui.Pack{ }), ui.Pack{
Anchor: ui.SE, Side: ui.Bottom,
Anchor: ui.E,
Padding: 8, Padding: 8,
}) })
frame.Pack(ui.NewLabel(ui.Label{ frame.Pack(ui.NewLabel(ui.Label{
@ -194,7 +170,8 @@ func (s *GUITestScene) Setup(d *Doodle) error {
Color: render.Blue, Color: render.Blue,
}, },
}), ui.Pack{ }), ui.Pack{
Anchor: ui.SE, Side: ui.Bottom,
Anchor: ui.E,
Padding: 8, Padding: 8,
}) })
@ -204,7 +181,7 @@ func (s *GUITestScene) Setup(d *Doodle) error {
Background: render.Grey, Background: render.Grey,
}) })
window.Pack(btnFrame, ui.Pack{ window.Pack(btnFrame, ui.Pack{
Anchor: ui.N, Side: ui.Top,
}) })
button1 := ui.NewButton("Button1", ui.NewLabel(ui.Label{ button1 := ui.NewButton("Button1", ui.NewLabel(ui.Label{
@ -228,13 +205,12 @@ func (s *GUITestScene) Setup(d *Doodle) error {
}) })
}) })
var align = ui.W
btnFrame.Pack(button1, ui.Pack{ btnFrame.Pack(button1, ui.Pack{
Anchor: align, Side: ui.Left,
Padding: 20, Padding: 20,
}) })
btnFrame.Pack(button2, ui.Pack{ btnFrame.Pack(button2, ui.Pack{
Anchor: align, Side: ui.Left,
Padding: 20, Padding: 20,
}) })