Add grid rendering, probably

This commit is contained in:
Drew DeVault 2018-02-17 15:21:22 -05:00
parent 8c8c21f3ff
commit 1892d73161
2 changed files with 76 additions and 9 deletions

View File

@ -2,7 +2,7 @@ package ui
type Drawable interface { type Drawable interface {
// Called when this renderable should draw itself // Called when this renderable should draw itself
Draw(ctx Context) Draw(ctx *Context)
// Specifies a function to call when this cell needs to be redrawn // Specifies a function to call when this cell needs to be redrawn
OnInvalidate(callback func(d Drawable)) OnInvalidate(callback func(d Drawable))
} }

View File

@ -4,9 +4,12 @@ import "fmt"
type Grid struct { type Grid struct {
Rows []DimSpec Rows []DimSpec
rowLayout []dimLayout
Columns []DimSpec Columns []DimSpec
columnLayout []dimLayout
Cells []*GridCell Cells []*GridCell
onInvalidate func(d Drawable) onInvalidate func(d Drawable)
invalid bool
} }
const ( const (
@ -17,24 +20,86 @@ const (
// Specifies the layout of a single row or column // Specifies the layout of a single row or column
type DimSpec struct { type DimSpec struct {
// One of SIZE_EXACT or SIZE_WEIGHT // One of SIZE_EXACT or SIZE_WEIGHT
Strategy uint Strategy int
// If Strategy = SIZE_EXACT, this is the number of cells this dim shall // If Strategy = SIZE_EXACT, this is the number of cells this dim shall
// occupy. If SIZE_WEIGHT, the space left after all exact dims are measured // occupy. If SIZE_WEIGHT, the space left after all exact dims are measured
// is distributed amonst the remaining dims weighted by this value. // is distributed amonst the remaining dims weighted by this value.
Size *uint Size int
}
// Used to cache layout of each row/column
type dimLayout struct {
Offset int
Size int
} }
type GridCell struct { type GridCell struct {
Row uint Row int
Column uint Column int
RowSpan uint RowSpan int
ColSpan uint ColSpan int
Content Drawable Content Drawable
invalid bool invalid bool
} }
func (grid *Grid) Draw(ctx Context) { func (grid *Grid) Draw(ctx *Context) {
// TODO invalid := grid.invalid
if invalid {
grid.reflow(ctx)
}
for _, cell := range grid.Cells {
if !cell.invalid && !invalid {
continue
}
rows := grid.rowLayout[cell.Row:cell.RowSpan]
cols := grid.columnLayout[cell.Column:cell.ColSpan]
x := cols[0].Offset
y := rows[0].Offset
width := 0
height := 0
for _, row := range rows {
width += row.Size
}
for _, col := range cols {
height += col.Size
}
subctx := ctx.Subcontext(x, y, width, height)
cell.Content.Draw(subctx)
}
}
func (grid *Grid) reflow(ctx *Context) {
grid.rowLayout = nil
grid.columnLayout = nil
flow := func(specs *[]DimSpec, layouts *[]dimLayout, extent int) {
exact := 0
weight := 0
for _, dim := range *specs {
if dim.Strategy == SIZE_EXACT {
exact += dim.Size
} else if dim.Strategy == SIZE_WEIGHT {
weight += dim.Size
}
}
offset := 0
for _, dim := range *specs {
layout := dimLayout{Offset: offset}
if dim.Strategy == SIZE_EXACT {
layout.Size = dim.Size
} else if dim.Strategy == SIZE_WEIGHT {
size := float64(dim.Size) / float64(weight) * float64(extent)
layout.Size = int(size)
}
*layouts = append(*layouts, layout)
}
}
flow(&grid.Rows, &grid.rowLayout, ctx.Width())
flow(&grid.Columns, &grid.columnLayout, ctx.Height())
grid.invalid = false
}
func (grid *Grid) InvalidateLayout() {
grid.invalid = true
} }
func (grid *Grid) OnInvalidate(onInvalidate func(d Drawable)) { func (grid *Grid) OnInvalidate(onInvalidate func(d Drawable)) {
@ -45,6 +110,7 @@ func (grid *Grid) AddChild(cell *GridCell) {
grid.Cells = append(grid.Cells, cell) grid.Cells = append(grid.Cells, cell)
cell.Content.OnInvalidate(grid.cellInvalidated) cell.Content.OnInvalidate(grid.cellInvalidated)
cell.invalid = true cell.invalid = true
grid.InvalidateLayout()
} }
func (grid *Grid) RemoveChild(cell *GridCell) { func (grid *Grid) RemoveChild(cell *GridCell) {
@ -54,6 +120,7 @@ func (grid *Grid) RemoveChild(cell *GridCell) {
break break
} }
} }
grid.InvalidateLayout()
} }
func (grid *Grid) cellInvalidated(drawable Drawable) { func (grid *Grid) cellInvalidated(drawable Drawable) {