2014-02-13 16:04:57 -06:00
// 13 february 2014
2014-02-19 10:41:10 -06:00
package ui
2014-02-13 16:04:57 -06:00
import (
"fmt"
"sync"
)
// Orientation defines the orientation of controls in a Stack.
2014-02-24 09:30:14 -06:00
type Orientation bool
2014-02-13 16:04:57 -06:00
const (
2014-02-24 09:30:14 -06:00
Horizontal Orientation = false
Vertical Orientation = true
2014-02-13 16:04:57 -06:00
)
2014-02-24 09:29:15 -06:00
// A Stack stacks controls horizontally or vertically within the Stack's parent.
// A horizontal Stack gives all controls the same height and their preferred widths.
// A vertical Stack gives all controls the same width and their preferred heights.
// Some controls may be marked as "stretchy": when the Window they are in changes size, stretchy controls resize to take up the remaining space after non-stretchy controls are laid out. If multiple controls are marked stretchy, they are alloted equal distribution of the remaining space.
2014-02-13 16:04:57 -06:00
type Stack struct {
lock sync . Mutex
created bool
orientation Orientation
2014-02-14 09:58:16 -06:00
controls [ ] Control
2014-02-24 09:29:15 -06:00
stretchy [ ] bool
2014-02-24 09:42:58 -06:00
width , height [ ] int // caches to avoid reallocating these each time
2014-02-13 16:04:57 -06:00
}
// NewStack creates a new Stack with the specified orientation.
2014-02-14 09:58:16 -06:00
func NewStack ( o Orientation , controls ... Control ) * Stack {
2014-02-13 16:04:57 -06:00
return & Stack {
orientation : o ,
2014-02-14 09:58:16 -06:00
controls : controls ,
2014-02-24 09:29:15 -06:00
stretchy : make ( [ ] bool , len ( controls ) ) ,
width : make ( [ ] int , len ( controls ) ) ,
height : make ( [ ] int , len ( controls ) ) ,
2014-02-13 16:04:57 -06:00
}
}
2014-02-24 09:29:15 -06:00
// SetStretchy marks a control in a Stack as stretchy.
func ( s * Stack ) SetStretchy ( index int ) {
s . lock . Lock ( )
defer s . lock . Unlock ( )
s [ index ] = true // TODO explicitly check for index out of bounds?
}
2014-02-14 10:12:08 -06:00
func ( s * Stack ) make ( window * sysData ) error {
2014-02-15 14:38:41 -06:00
for i , c := range s . controls {
2014-02-14 10:12:08 -06:00
err := c . make ( window )
2014-02-13 16:04:57 -06:00
if err != nil {
2014-02-24 09:29:15 -06:00
return fmt . Errorf ( "error adding control %d to Stack: %v" , i , err )
2014-02-13 16:04:57 -06:00
}
}
2014-02-14 19:41:36 -06:00
s . created = true
2014-02-13 16:04:57 -06:00
return nil
}
func ( s * Stack ) setRect ( x int , y int , width int , height int ) error {
var dx , dy int
2014-02-24 09:42:58 -06:00
var stretchywid , stretchyht int
2014-02-13 16:04:57 -06:00
2014-02-14 09:58:16 -06:00
if len ( s . controls ) == 0 { // do nothing if there's nothing to do
2014-02-13 16:04:57 -06:00
return nil
}
2014-02-24 09:42:58 -06:00
// 1) get height and width of non-stretchy controls; figure out how much space is alloted to stretchy controls
stretchywid = width
stretchyht = height
nStretchy := 0
for i , c := range s . controls {
if s . stretchy [ i ] {
nStretchy ++
continue
}
w , h , err := c . preferredSize ( )
if err != nil {
return fmt . Errorf ( "error getting preferred size of control %d in Stack.setRect(): %v" , i , err )
}
if s . orientation == Horizontal { // all controls have same height
s . width [ i ] = w
s . height [ i ] = height
stretchywid -= w
} else { // all controls have same width
s . width [ i ] = width
s . height [ i ] = h
stretchyht -= h
}
2014-02-13 16:04:57 -06:00
}
2014-02-24 09:42:58 -06:00
// 2) figure out size of stretchy controls
if nStretchy != 0 {
if s . orientation == Horizontal { // split rest of width
stretchywid /= nStretchy
} else { // split rest of height
stretchyht /= nStretchy
}
}
for i , c := range controls {
if ! s . stretchy [ i ] {
continue
}
c . width [ i ] = stretchywid
c . height [ i ] = stretchyht
}
// 3) now actually place controls
2014-02-15 14:38:41 -06:00
for i , c := range s . controls {
2014-02-24 09:42:58 -06:00
err := c . setRect ( x , y , s . width [ i ] , s . height [ i ] )
2014-02-13 16:04:57 -06:00
if err != nil {
2014-02-24 09:42:58 -06:00
return fmt . Errorf ( "error setting size of control %d in Stack.setRect(): %v" , i , err )
}
if s . orientation == Horizontal {
x += s . width [ i ]
} else {
y += s . height [ i ]
2014-02-13 16:04:57 -06:00
}
}
return nil
}