aerc/lib/ui/tab.go
Markus Ongyerth 2f5c1db63c refactor lib/ui/tab to ensure staying in bounds
Fix a few potential out of bounds by placing proper checks, which should
be relevant if all tabs are removed for some reason.

Also avoid iterating all tabs in the invalidate handler, since we are
only interested in whether it's the selected tab either way
2018-06-13 07:00:12 -04:00

131 lines
2.8 KiB
Go

package ui
import (
"github.com/gdamore/tcell"
)
type Tabs struct {
Tabs []*Tab
TabStrip *TabStrip
TabContent *TabContent
Selected int
onInvalidateStrip func(d Drawable)
onInvalidateContent func(d Drawable)
}
type Tab struct {
Content Drawable
Name string
invalid bool
}
type TabStrip Tabs
type TabContent Tabs
func NewTabs() *Tabs {
tabs := &Tabs{}
tabs.TabStrip = (*TabStrip)(tabs)
tabs.TabContent = (*TabContent)(tabs)
return tabs
}
func (tabs *Tabs) Add(content Drawable, name string) {
tabs.Tabs = append(tabs.Tabs, &Tab{
Content: content,
Name: name,
})
tabs.TabStrip.Invalidate()
content.OnInvalidate(tabs.invalidateChild)
}
func (tabs *Tabs) invalidateChild(d Drawable) {
if tabs.Selected >= len(tabs.Tabs) {
return
}
if tabs.Tabs[tabs.Selected].Content == d {
if tabs.onInvalidateContent != nil {
tabs.onInvalidateContent(tabs.TabContent)
}
}
}
func (tabs *Tabs) Remove(content Drawable) {
for i, tab := range tabs.Tabs {
if tab.Content == content {
tabs.Tabs = append(tabs.Tabs[:i], tabs.Tabs[i+1:]...)
break
}
}
/* Force the selected index into the existing range */
if tabs.Selected >= len(tabs.Tabs) {
tabs.Select(len(tabs.Tabs) - 1)
}
tabs.TabStrip.Invalidate()
}
func (tabs *Tabs) Select(index int) {
if tabs.Selected >= len(tabs.Tabs) {
panic("Tried to set tab index to a non-existing element")
}
if tabs.Selected != index {
tabs.Selected = index
tabs.TabStrip.Invalidate()
tabs.TabContent.Invalidate()
}
}
// TODO: Color repository
func (strip *TabStrip) Draw(ctx *Context) {
x := 0
for i, tab := range strip.Tabs {
style := tcell.StyleDefault.
Background(tcell.ColorWhite).
Foreground(tcell.ColorBlack)
if strip.Selected == i {
style = tcell.StyleDefault.
Background(tcell.ColorDefault).
Foreground(tcell.ColorDefault)
}
x += ctx.Printf(x, 0, style, " %s ", tab.Name)
}
style := tcell.StyleDefault.
Background(tcell.ColorWhite).
Foreground(tcell.ColorBlack)
ctx.Fill(x, 0, ctx.Width()-x, 1, ' ', style)
}
func (strip *TabStrip) Invalidate() {
if strip.onInvalidateStrip != nil {
strip.onInvalidateStrip(strip)
}
}
func (strip *TabStrip) OnInvalidate(onInvalidate func(d Drawable)) {
strip.onInvalidateStrip = onInvalidate
}
func (content *TabContent) Draw(ctx *Context) {
if content.Selected >= len(content.Tabs) {
width := ctx.Width()
height := ctx.Height()
ctx.Fill(0, 0, width, height, ' ', tcell.StyleDefault)
}
tab := content.Tabs[content.Selected]
tab.Content.Draw(ctx)
}
func (content *TabContent) Invalidate() {
if content.onInvalidateContent != nil {
content.onInvalidateContent(content)
}
tab := content.Tabs[content.Selected]
tab.Content.Invalidate()
}
func (content *TabContent) OnInvalidate(onInvalidate func(d Drawable)) {
content.onInvalidateContent = onInvalidate
}