diff options
author | Drew DeVault <sir@cmpwn.com> | 2018-02-17 16:35:36 -0500 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2018-02-17 16:35:36 -0500 |
commit | 60b351b78c930110716b0c9db2227e13704f826d (patch) | |
tree | 3d5d6f69fe416cfd4032c7d672168c965999ec4d /ui | |
parent | 1892d73161a006182d7ef467e2bfc03c11587cb6 (diff) | |
download | aerc-60b351b78c930110716b0c9db2227e13704f826d.tar.gz |
Polish up grid and add new rendering loop
Diffstat (limited to 'ui')
-rw-r--r-- | ui/account.go.old (renamed from ui/account.go) | 0 | ||||
-rw-r--r-- | ui/context.go | 3 | ||||
-rw-r--r-- | ui/drawable.go | 2 | ||||
-rw-r--r-- | ui/grid.go | 70 | ||||
-rw-r--r-- | ui/helpers.go | 41 | ||||
-rw-r--r-- | ui/types.go | 71 | ||||
-rw-r--r-- | ui/ui.go | 91 |
7 files changed, 87 insertions, 191 deletions
diff --git a/ui/account.go b/ui/account.go.old index 393a47a..393a47a 100644 --- a/ui/account.go +++ b/ui/account.go.old diff --git a/ui/context.go b/ui/context.go index 9f2e2fe..e7d9ebe 100644 --- a/ui/context.go +++ b/ui/context.go @@ -22,8 +22,7 @@ func (ctx *Context) Height() int { return ctx.height } -func NewContext() *Context { - width, height := termbox.Size() +func NewContext(width, height int) *Context { return &Context{0, 0, width, height} } diff --git a/ui/drawable.go b/ui/drawable.go index a61c020..ef09451 100644 --- a/ui/drawable.go +++ b/ui/drawable.go @@ -5,4 +5,6 @@ type Drawable interface { Draw(ctx *Context) // Specifies a function to call when this cell needs to be redrawn OnInvalidate(callback func(d Drawable)) + // Invalidates the drawable + Invalidate() } diff --git a/ui/grid.go b/ui/grid.go index 2183a55..2091fc5 100644 --- a/ui/grid.go +++ b/ui/grid.go @@ -1,6 +1,9 @@ package ui -import "fmt" +import ( + "fmt" + "math" +) type Grid struct { Rows []DimSpec @@ -42,6 +45,22 @@ type GridCell struct { invalid bool } +func NewGrid() *Grid { + return &Grid{invalid: true} +} + +func (cell *GridCell) At(row, col int) *GridCell { + cell.Row = row + cell.Column = col + return cell +} + +func (cell *GridCell) Span(rows, cols int) *GridCell { + cell.RowSpan = rows + cell.ColSpan = cols + return cell +} + func (grid *Grid) Draw(ctx *Context) { invalid := grid.invalid if invalid { @@ -51,17 +70,17 @@ func (grid *Grid) Draw(ctx *Context) { if !cell.invalid && !invalid { continue } - rows := grid.rowLayout[cell.Row:cell.RowSpan] - cols := grid.columnLayout[cell.Column:cell.ColSpan] + rows := grid.rowLayout[cell.Row : cell.Row+cell.RowSpan] + cols := grid.columnLayout[cell.Column : 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 + width += col.Size + } + for _, row := range rows { + height += row.Size } subctx := ctx.Subcontext(x, y, width, height) cell.Content.Draw(subctx) @@ -74,10 +93,12 @@ func (grid *Grid) reflow(ctx *Context) { flow := func(specs *[]DimSpec, layouts *[]dimLayout, extent int) { exact := 0 weight := 0 + nweights := 0 for _, dim := range *specs { if dim.Strategy == SIZE_EXACT { exact += dim.Size } else if dim.Strategy == SIZE_WEIGHT { + nweights += 1 weight += dim.Size } } @@ -87,30 +108,49 @@ func (grid *Grid) reflow(ctx *Context) { 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) + size := float64(dim.Size) / float64(weight) + size *= float64(extent - exact) + layout.Size = int(math.Floor(size)) } + offset += layout.Size *layouts = append(*layouts, layout) } } - flow(&grid.Rows, &grid.rowLayout, ctx.Width()) - flow(&grid.Columns, &grid.columnLayout, ctx.Height()) + flow(&grid.Rows, &grid.rowLayout, ctx.Height()) + flow(&grid.Columns, &grid.columnLayout, ctx.Width()) grid.invalid = false } -func (grid *Grid) InvalidateLayout() { +func (grid *Grid) invalidateLayout() { grid.invalid = true + if grid.onInvalidate != nil { + grid.onInvalidate(grid) + } +} + +func (grid *Grid) Invalidate() { + grid.invalidateLayout() + for _, cell := range grid.Cells { + cell.Content.Invalidate() + } } func (grid *Grid) OnInvalidate(onInvalidate func(d Drawable)) { grid.onInvalidate = onInvalidate } -func (grid *Grid) AddChild(cell *GridCell) { +func (grid *Grid) AddChild(content Drawable) *GridCell { + cell := &GridCell{ + RowSpan: 1, + ColSpan: 1, + Content: content, + invalid: true, + } grid.Cells = append(grid.Cells, cell) cell.Content.OnInvalidate(grid.cellInvalidated) cell.invalid = true - grid.InvalidateLayout() + grid.invalidateLayout() + return cell } func (grid *Grid) RemoveChild(cell *GridCell) { @@ -120,7 +160,7 @@ func (grid *Grid) RemoveChild(cell *GridCell) { break } } - grid.InvalidateLayout() + grid.invalidateLayout() } func (grid *Grid) cellInvalidated(drawable Drawable) { diff --git a/ui/helpers.go b/ui/helpers.go deleted file mode 100644 index f2b2adf..0000000 --- a/ui/helpers.go +++ /dev/null @@ -1,41 +0,0 @@ -package ui - -import ( - "fmt" - - tb "github.com/nsf/termbox-go" -) - -func TPrintf(geo *Geometry, ref tb.Cell, format string, a ...interface{}) { - str := fmt.Sprintf(format, a...) - _geo := *geo - newline := func() { - // TODO: Abort when out of room? - geo.Col = _geo.Col - geo.Row++ - } - for _, ch := range str { - switch ch { - case '\n': - newline() - case '\r': - geo.Col = _geo.Col - default: - tb.SetCell(geo.Col, geo.Row, ch, ref.Fg, ref.Bg) - geo.Col++ - if geo.Col == _geo.Col+geo.Width { - newline() - } - } - } -} - -func TFill(geo Geometry, ref tb.Cell) { - _geo := geo - for ; geo.Row < geo.Height; geo.Row++ { - for ; geo.Col < geo.Width; geo.Col++ { - tb.SetCell(geo.Col, geo.Row, ref.Ch, ref.Fg, ref.Bg) - } - geo.Col = _geo.Col - } -} diff --git a/ui/types.go b/ui/types.go deleted file mode 100644 index 5437642..0000000 --- a/ui/types.go +++ /dev/null @@ -1,71 +0,0 @@ -package ui - -import ( - tb "github.com/nsf/termbox-go" - - "git.sr.ht/~sircmpwn/aerc2/config" - "git.sr.ht/~sircmpwn/aerc2/worker/types" -) - -const ( - Valid = 0 - InvalidateTabList = 1 << iota - InvalidateTabView - InvalidateStatusBar -) - -const ( - InvalidateAll = InvalidateTabList | - InvalidateTabView | - InvalidateStatusBar -) - -type Geometry struct { - Row int - Col int - Width int - Height int -} - -type AercTab interface { - Name() string - Render(at Geometry) - SetParent(parent *UIState) -} - -type WorkerListener interface { - GetChannel() chan types.WorkerMessage - HandleMessage(msg types.WorkerMessage) -} - -type wrappedMessage struct { - msg types.WorkerMessage - listener WorkerListener -} - -type UIState struct { - Config *config.AercConfig - Exit bool - InvalidPanes uint - - Panes struct { - TabList Geometry - TabView Geometry - Sidebar Geometry - StatusBar Geometry - } - - Tabs []AercTab - SelectedTab int - - Prompt struct { - Prompt *string - Text *string - Index int - Scroll int - } - - tbEvents chan tb.Event - // Aggregate channel for all worker messages - workerEvents chan wrappedMessage -} diff --git a/ui/ui.go b/ui/ui.go index db31696..d1d2ca3 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -6,17 +6,27 @@ import ( "git.sr.ht/~sircmpwn/aerc2/config" ) -func Initialize(conf *config.AercConfig) (*UIState, error) { - state := UIState{ - Config: conf, - InvalidPanes: InvalidateAll, +type UI struct { + Exit bool + Content Drawable + ctx *Context - tbEvents: make(chan tb.Event, 10), - workerEvents: make(chan wrappedMessage), - } + tbEvents chan tb.Event + invalidations chan interface{} +} + +func Initialize(conf *config.AercConfig, content Drawable) (*UI, error) { if err := tb.Init(); err != nil { return nil, err } + width, height := tb.Size() + state := UI{ + Content: content, + ctx: NewContext(width, height), + + tbEvents: make(chan tb.Event, 10), + invalidations: make(chan interface{}), + } tb.SetInputMode(tb.InputEsc | tb.InputMouse) tb.SetOutputMode(tb.Output256) go (func() { @@ -24,50 +34,18 @@ func Initialize(conf *config.AercConfig) (*UIState, error) { state.tbEvents <- tb.PollEvent() } })() + go (func() { state.invalidations <- nil })() + content.OnInvalidate(func(_ Drawable) { + go (func() { state.invalidations <- nil })() + }) return &state, nil } -func (state *UIState) Close() { +func (state *UI) Close() { tb.Close() } -func (state *UIState) AddTab(tab AercTab) { - tab.SetParent(state) - state.Tabs = append(state.Tabs, tab) - if listener, ok := tab.(WorkerListener); ok { - go (func() { - for msg := range listener.GetChannel() { - state.workerEvents <- wrappedMessage{ - msg: msg, - listener: listener, - } - } - })() - } -} - -func (state *UIState) Invalidate(what uint) { - state.InvalidPanes |= what -} - -func (state *UIState) InvalidateFrom(tab AercTab) { - if state.Tabs[state.SelectedTab] == tab { - state.Invalidate(InvalidateTabView) - } -} - -func (state *UIState) calcGeometries() { - width, height := tb.Size() - // TODO: more - state.Panes.TabView = Geometry{ - Row: 0, - Col: 0, - Width: width, - Height: height, - } -} - -func (state *UIState) Tick() bool { +func (state *UI) Tick() bool { select { case event := <-state.tbEvents: switch event.Type { @@ -76,26 +54,15 @@ func (state *UIState) Tick() bool { state.Exit = true } case tb.EventResize: - state.Invalidate(InvalidateAll) - } - case msg := <-state.workerEvents: - msg.listener.HandleMessage(msg.msg) - default: - // no-op - break - } - if state.InvalidPanes != 0 { - invalid := state.InvalidPanes - state.InvalidPanes = 0 - if invalid&InvalidateAll == InvalidateAll { tb.Clear(tb.ColorDefault, tb.ColorDefault) - state.calcGeometries() - } - if invalid&InvalidateTabView != 0 { - tab := state.Tabs[state.SelectedTab] - tab.Render(state.Panes.TabView) + state.ctx = NewContext(event.Width, event.Height) + state.Content.Invalidate() } + case <-state.invalidations: + state.Content.Draw(state.ctx) tb.Flush() + default: + return false } return true } |