ted/ui/layout.go

137 lines
3.8 KiB
Go

// Standard layout components.
package ui
// Instance of a component.
type componentInstance struct {
component UiComponent
x, y int // X and Y offset of this component
height int // Height allocated to the component
width int // Width allocated to the component
}
// A layout component.
type LinearLayout struct {
// The set of components that this layout component contains
subcomponents []*componentInstance
}
// Adds a component to the end of the component list managed by this layout.
func (l *LinearLayout) Append(component UiComponent) {
l.subcomponents = append(l.subcomponents, &componentInstance{component, 0, 0, 0, 0})
}
// A vertical layout component.
type VertLinearLayout struct {
LinearLayout
maxHeight int
}
// Request from the manager for the component to draw itself. This is given a drawable context.
func (vl *VertLinearLayout) Redraw(context *DrawContext) {
for _, ci := range vl.LinearLayout.subcomponents {
subContext := context.NewSubContext(ci.x, ci.y, ci.width, ci.height)
ci.component.Redraw(subContext)
}
}
// Remeasures the components currently managed within this layout.
func (vl *VertLinearLayout) Remeasure(w, h int) (int, int) {
posy := 0
// TODO: At the moment, this simply takes the minimum and maximum dimensions requested
// by the components. This needs to be extended to allow for "special" components which
// take up dynamic space.
for _, ci := range vl.LinearLayout.subcomponents {
_, rh := ci.component.Remeasure(w, h - posy)
// All components within this layer are given the full width of the screen.
ci.x = 0
ci.width = w
ci.y = posy
ci.height = rh
// Put the next component directly below the previous one
posy += rh
}
return w, posy
}
// A layout component which defers calls to the nested component. If no control is defined,
// will not draw itself. Used for switching controls dynamically.
type ProxyLayout struct {
Component UiComponent
}
func (pl *ProxyLayout) Remeasure(w, h int) (int, int) {
if pl.Component != nil {
return pl.Component.Remeasure(w, h)
} else {
return 0, 0
}
}
func (pl *ProxyLayout) Redraw(context *DrawContext) {
if pl.Component != nil {
pl.Component.Redraw(context)
}
}
// A relative layout component. This has a "client" component, bordered by a north,
// south, east and west component. The N,S,E,W components will be provided with the full dimensions
// whereas the client component will be provided with the remaining size. Each one of the components
// can be null.
type RelativeLayout struct {
North, South, East, West, Client UiComponent
// Measured client borders
ct, cb, cl, cr, ch, cw int
// North/south heights and east/west widths
nh, sh int
ew, ww int
}
func (rl *RelativeLayout) Remeasure(w, h int) (int, int) {
if rl.North != nil {
_, rl.nh = rl.North.Remeasure(w, h)
rl.ct = rl.nh
} else {
rl.ct = 0
}
if rl.South != nil {
_, rl.sh = rl.South.Remeasure(w, h - rl.nh)
rl.cb = h - rl.sh
} else {
rl.cb = h
}
// TODO: East and west
rl.cl = 0
rl.cr = w
rl.ch = h - rl.nh - rl.sh
rl.cw = w
return w, h
}
func (vl *RelativeLayout) Redraw(context *DrawContext) {
if vl.North != nil {
vl.North.Redraw(context.NewSubContext(0, 0, context.W, vl.nh))
}
if vl.South != nil {
vl.South.Redraw(context.NewSubContext(0, vl.cb, context.W, vl.sh))
}
if vl.Client != nil {
vl.Client.Redraw(context.NewSubContext(vl.cl, vl.ct, vl.cw, vl.ch))
}
}