summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/ui/borders.go9
-rw-r--r--lib/ui/grid.go14
-rw-r--r--lib/ui/invalidatable.go24
-rw-r--r--lib/ui/text.go22
-rw-r--r--widgets/account.go19
-rw-r--r--widgets/dirlist.go10
-rw-r--r--widgets/exline.go9
-rw-r--r--widgets/msglist.go24
-rw-r--r--widgets/msgviewer.go21
-rw-r--r--widgets/spinner.go10
-rw-r--r--widgets/status.go11
-rw-r--r--widgets/terminal.go36
12 files changed, 82 insertions, 127 deletions
diff --git a/lib/ui/borders.go b/lib/ui/borders.go
index 9b7860e..cffd3ca 100644
--- a/lib/ui/borders.go
+++ b/lib/ui/borders.go
@@ -12,6 +12,7 @@ const (
 )
 
 type Bordered struct {
+	Invalidatable
 	borders      uint
 	content      Drawable
 	onInvalidate func(d Drawable)
@@ -35,13 +36,7 @@ func (bordered *Bordered) Children() []Drawable {
 }
 
 func (bordered *Bordered) Invalidate() {
-	if bordered.onInvalidate != nil {
-		bordered.onInvalidate(bordered)
-	}
-}
-
-func (bordered *Bordered) OnInvalidate(onInvalidate func(d Drawable)) {
-	bordered.onInvalidate = onInvalidate
+	bordered.DoInvalidate(bordered)
 }
 
 func (bordered *Bordered) Draw(ctx *Context) {
diff --git a/lib/ui/grid.go b/lib/ui/grid.go
index 87b94bd..3ac43a4 100644
--- a/lib/ui/grid.go
+++ b/lib/ui/grid.go
@@ -6,12 +6,12 @@ import (
 )
 
 type Grid struct {
+	Invalidatable
 	rows         []GridSpec
 	rowLayout    []gridLayout
 	columns      []GridSpec
 	columnLayout []gridLayout
 	cells        []*GridCell
-	onInvalidate func(d Drawable)
 	invalid      bool
 }
 
@@ -141,9 +141,7 @@ func (grid *Grid) reflow(ctx *Context) {
 
 func (grid *Grid) invalidateLayout() {
 	grid.invalid = true
-	if grid.onInvalidate != nil {
-		grid.onInvalidate(grid)
-	}
+	grid.DoInvalidate(grid)
 }
 
 func (grid *Grid) Invalidate() {
@@ -153,10 +151,6 @@ func (grid *Grid) Invalidate() {
 	}
 }
 
-func (grid *Grid) OnInvalidate(onInvalidate func(d Drawable)) {
-	grid.onInvalidate = onInvalidate
-}
-
 func (grid *Grid) AddChild(content Drawable) *GridCell {
 	cell := &GridCell{
 		RowSpan: 1,
@@ -193,7 +187,5 @@ func (grid *Grid) cellInvalidated(drawable Drawable) {
 		panic(fmt.Errorf("Attempted to invalidate unknown cell"))
 	}
 	cell.invalid = true
-	if grid.onInvalidate != nil {
-		grid.onInvalidate(grid)
-	}
+	grid.DoInvalidate(grid)
 }
diff --git a/lib/ui/invalidatable.go b/lib/ui/invalidatable.go
new file mode 100644
index 0000000..9275712
--- /dev/null
+++ b/lib/ui/invalidatable.go
@@ -0,0 +1,24 @@
+package ui
+
+import (
+	"sync/atomic"
+)
+
+type Invalidatable struct {
+	onInvalidate atomic.Value
+}
+
+func (i *Invalidatable) OnInvalidate(f func(d Drawable)) {
+	i.onInvalidate.Store(f)
+}
+
+func (i *Invalidatable) DoInvalidate(d Drawable) {
+	v := i.onInvalidate.Load()
+	if v == nil {
+		return
+	}
+	f := v.(func(d Drawable))
+	if f != nil {
+		f(d)
+	}
+}
diff --git a/lib/ui/text.go b/lib/ui/text.go
index 761673c..8aea8eb 100644
--- a/lib/ui/text.go
+++ b/lib/ui/text.go
@@ -12,13 +12,13 @@ const (
 )
 
 type Text struct {
-	text         string
-	strategy     uint
-	fg           tcell.Color
-	bg           tcell.Color
-	bold         bool
-	reverse      bool
-	onInvalidate func(d Drawable)
+	Invalidatable
+	text     string
+	strategy uint
+	fg       tcell.Color
+	bg       tcell.Color
+	bold     bool
+	reverse  bool
 }
 
 func NewText(text string) *Text {
@@ -80,12 +80,6 @@ func (t *Text) Draw(ctx *Context) {
 	ctx.Printf(x, 0, style, t.text)
 }
 
-func (t *Text) OnInvalidate(onInvalidate func(d Drawable)) {
-	t.onInvalidate = onInvalidate
-}
-
 func (t *Text) Invalidate() {
-	if t.onInvalidate != nil {
-		t.onInvalidate(t)
-	}
+	t.DoInvalidate(t)
 }
diff --git a/widgets/account.go b/widgets/account.go
index f8abdc9..a8cd9ad 100644
--- a/widgets/account.go
+++ b/widgets/account.go
@@ -14,16 +14,15 @@ import (
 )
 
 type AccountView struct {
-	acct         *config.AccountConfig
-	conf         *config.AercConfig
-	dirlist      *DirectoryList
-	grid         *ui.Grid
-	host         TabHost
-	logger       *log.Logger
-	onInvalidate func(d ui.Drawable)
-	msglist      *MessageList
-	msgStores    map[string]*lib.MessageStore
-	worker       *types.Worker
+	acct      *config.AccountConfig
+	conf      *config.AercConfig
+	dirlist   *DirectoryList
+	grid      *ui.Grid
+	host      TabHost
+	logger    *log.Logger
+	msglist   *MessageList
+	msgStores map[string]*lib.MessageStore
+	worker    *types.Worker
 }
 
 func NewAccountView(conf *config.AercConfig, acct *config.AccountConfig,
diff --git a/widgets/dirlist.go b/widgets/dirlist.go
index eb79bc4..374d142 100644
--- a/widgets/dirlist.go
+++ b/widgets/dirlist.go
@@ -12,10 +12,10 @@ import (
 )
 
 type DirectoryList struct {
+	ui.Invalidatable
 	conf         *config.AccountConfig
 	dirs         []string
 	logger       *log.Logger
-	onInvalidate func(d ui.Drawable)
 	selecting    string
 	selected     string
 	spinner      *Spinner
@@ -77,14 +77,8 @@ func (dirlist *DirectoryList) Selected() string {
 	return dirlist.selected
 }
 
-func (dirlist *DirectoryList) OnInvalidate(onInvalidate func(d ui.Drawable)) {
-	dirlist.onInvalidate = onInvalidate
-}
-
 func (dirlist *DirectoryList) Invalidate() {
-	if dirlist.onInvalidate != nil {
-		dirlist.onInvalidate(dirlist)
-	}
+	dirlist.DoInvalidate(dirlist)
 }
 
 func (dirlist *DirectoryList) Draw(ctx *ui.Context) {
diff --git a/widgets/exline.go b/widgets/exline.go
index 5c9f065..8b18736 100644
--- a/widgets/exline.go
+++ b/widgets/exline.go
@@ -12,6 +12,7 @@ import (
 // TODO: scrolling
 
 type ExLine struct {
+	ui.Invalidatable
 	command []rune
 	commit  func(cmd string)
 	ctx     *ui.Context
@@ -33,14 +34,8 @@ func NewExLine(commit func(cmd string), cancel func()) *ExLine {
 	}
 }
 
-func (ex *ExLine) OnInvalidate(onInvalidate func(d ui.Drawable)) {
-	ex.onInvalidate = onInvalidate
-}
-
 func (ex *ExLine) Invalidate() {
-	if ex.onInvalidate != nil {
-		ex.onInvalidate(ex)
-	}
+	ex.DoInvalidate(ex)
 }
 
 func (ex *ExLine) Draw(ctx *ui.Context) {
diff --git a/widgets/msglist.go b/widgets/msglist.go
index ea9b245..c4b5d82 100644
--- a/widgets/msglist.go
+++ b/widgets/msglist.go
@@ -12,14 +12,14 @@ import (
 )
 
 type MessageList struct {
-	conf         *config.AercConfig
-	logger       *log.Logger
-	height       int
-	onInvalidate func(d ui.Drawable)
-	scroll       int
-	selected     int
-	spinner      *Spinner
-	store        *lib.MessageStore
+	ui.Invalidatable
+	conf     *config.AercConfig
+	logger   *log.Logger
+	height   int
+	scroll   int
+	selected int
+	spinner  *Spinner
+	store    *lib.MessageStore
 }
 
 // TODO: fish in config
@@ -37,14 +37,8 @@ func NewMessageList(logger *log.Logger) *MessageList {
 	return ml
 }
 
-func (ml *MessageList) OnInvalidate(onInvalidate func(d ui.Drawable)) {
-	ml.onInvalidate = onInvalidate
-}
-
 func (ml *MessageList) Invalidate() {
-	if ml.onInvalidate != nil {
-		ml.onInvalidate(ml)
-	}
+	ml.DoInvalidate(ml)
 }
 
 func (ml *MessageList) Draw(ctx *ui.Context) {
diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go
index 711879b..ba99911 100644
--- a/widgets/msgviewer.go
+++ b/widgets/msgviewer.go
@@ -252,8 +252,7 @@ func (mv *MessageViewer) Focus(focus bool) {
 }
 
 type HeaderView struct {
-	onInvalidate func(d ui.Drawable)
-
+	ui.Invalidatable
 	Name  string
 	Value string
 }
@@ -281,17 +280,11 @@ func (hv *HeaderView) Draw(ctx *ui.Context) {
 }
 
 func (hv *HeaderView) Invalidate() {
-	if hv.onInvalidate != nil {
-		hv.onInvalidate(hv)
-	}
-}
-
-func (hv *HeaderView) OnInvalidate(fn func(d ui.Drawable)) {
-	hv.onInvalidate = fn
+	hv.DoInvalidate(hv)
 }
 
 type MultipartView struct {
-	onInvalidate func(d ui.Drawable)
+	ui.Invalidatable
 }
 
 func (mpv *MultipartView) Draw(ctx *ui.Context) {
@@ -303,11 +296,5 @@ func (mpv *MultipartView) Draw(ctx *ui.Context) {
 }
 
 func (mpv *MultipartView) Invalidate() {
-	if mpv.onInvalidate != nil {
-		mpv.onInvalidate(mpv)
-	}
-}
-
-func (mpv *MultipartView) OnInvalidate(fn func(d ui.Drawable)) {
-	mpv.onInvalidate = fn
+	mpv.DoInvalidate(mpv)
 }
diff --git a/widgets/spinner.go b/widgets/spinner.go
index 0ab3e13..bb7dbe8 100644
--- a/widgets/spinner.go
+++ b/widgets/spinner.go
@@ -23,8 +23,8 @@ var (
 )
 
 type Spinner struct {
+	ui.Invalidatable
 	frame        int64 // access via atomic
-	onInvalidate func(d ui.Drawable)
 	stop         chan struct{}
 }
 
@@ -84,12 +84,6 @@ func (s *Spinner) Draw(ctx *ui.Context) {
 	ctx.Printf(col, 0, tcell.StyleDefault, "%s", frames[cur])
 }
 
-func (s *Spinner) OnInvalidate(onInvalidate func(d ui.Drawable)) {
-	s.onInvalidate = onInvalidate
-}
-
 func (s *Spinner) Invalidate() {
-	if s.onInvalidate != nil {
-		s.onInvalidate(s)
-	}
+	s.DoInvalidate(s)
 }
diff --git a/widgets/status.go b/widgets/status.go
index 3536760..344454a 100644
--- a/widgets/status.go
+++ b/widgets/status.go
@@ -9,10 +9,9 @@ import (
 )
 
 type StatusLine struct {
+	ui.Invalidatable
 	stack    []*StatusMessage
 	fallback StatusMessage
-
-	onInvalidate func(d ui.Drawable)
 }
 
 type StatusMessage struct {
@@ -31,14 +30,8 @@ func NewStatusLine() *StatusLine {
 	}
 }
 
-func (status *StatusLine) OnInvalidate(onInvalidate func(d ui.Drawable)) {
-	status.onInvalidate = onInvalidate
-}
-
 func (status *StatusLine) Invalidate() {
-	if status.onInvalidate != nil {
-		status.onInvalidate(status)
-	}
+	status.DoInvalidate(status)
 }
 
 func (status *StatusLine) Draw(ctx *ui.Context) {
diff --git a/widgets/terminal.go b/widgets/terminal.go
index 84f80e3..63c105f 100644
--- a/widgets/terminal.go
+++ b/widgets/terminal.go
@@ -88,20 +88,20 @@ func init() {
 }
 
 type Terminal struct {
-	closed       bool
-	cmd          *exec.Cmd
-	colors       map[tcell.Color]tcell.Color
-	ctx          *ui.Context
-	cursorPos    vterm.Pos
-	cursorShown  bool
-	damage       []vterm.Rect
-	destroyed    bool
-	err          error
-	focus        bool
-	onInvalidate func(d ui.Drawable)
-	pty          *os.File
-	start        chan interface{}
-	vterm        *vterm.VTerm
+	ui.Invalidatable
+	closed      bool
+	cmd         *exec.Cmd
+	colors      map[tcell.Color]tcell.Color
+	ctx         *ui.Context
+	cursorPos   vterm.Pos
+	cursorShown bool
+	damage      []vterm.Rect
+	destroyed   bool
+	err         error
+	focus       bool
+	pty         *os.File
+	start       chan interface{}
+	vterm       *vterm.VTerm
 
 	OnClose func(err error)
 	OnStart func()
@@ -225,10 +225,6 @@ func (term *Terminal) Destroy() {
 	term.destroyed = true
 }
 
-func (term *Terminal) OnInvalidate(cb func(d ui.Drawable)) {
-	term.onInvalidate = cb
-}
-
 func (term *Terminal) Invalidate() {
 	if term.vterm != nil {
 		width, height := term.vterm.Size()
@@ -239,9 +235,7 @@ func (term *Terminal) Invalidate() {
 }
 
 func (term *Terminal) invalidate() {
-	if term.onInvalidate != nil {
-		term.onInvalidate(term)
-	}
+	term.DoInvalidate(term)
 }
 
 func (term *Terminal) Draw(ctx *ui.Context) {