aerc/widgets/dirlist.go
Ben Burwell cce7cb4808 Factor UI models out of the worker message package
Before, the information needed to display different parts of the UI was
tightly coupled to the specific messages being sent back and forth to
the backend worker. Separating out a models package allows us to be more
specific about exactly what a backend is able to and required to
provide for the UI.
2019-07-08 16:06:23 -04:00

178 lines
3.9 KiB
Go

package widgets
import (
"log"
"sort"
"github.com/gdamore/tcell"
"git.sr.ht/~sircmpwn/aerc/config"
"git.sr.ht/~sircmpwn/aerc/lib"
"git.sr.ht/~sircmpwn/aerc/lib/ui"
"git.sr.ht/~sircmpwn/aerc/worker/types"
)
type DirectoryList struct {
ui.Invalidatable
acctConf *config.AccountConfig
uiConf *config.UIConfig
store *lib.DirStore
dirs []string
logger *log.Logger
selecting string
selected string
spinner *Spinner
worker *types.Worker
}
func NewDirectoryList(acctConf *config.AccountConfig, uiConf *config.UIConfig,
logger *log.Logger, worker *types.Worker) *DirectoryList {
dirlist := &DirectoryList{
acctConf: acctConf,
uiConf: uiConf,
logger: logger,
spinner: NewSpinner(),
store: lib.NewDirStore(),
worker: worker,
}
dirlist.spinner.OnInvalidate(func(_ ui.Drawable) {
dirlist.Invalidate()
})
dirlist.spinner.Start()
return dirlist
}
func (dirlist *DirectoryList) List() []string {
return dirlist.store.List()
}
func (dirlist *DirectoryList) UpdateList(done func(dirs []string)) {
// TODO: move this logic into dirstore
var dirs []string
dirlist.worker.PostAction(
&types.ListDirectories{}, func(msg types.WorkerMessage) {
switch msg := msg.(type) {
case *types.Directory:
dirs = append(dirs, msg.Dir.Name)
case *types.Done:
sort.Strings(dirs)
dirlist.store.Update(dirs)
dirlist.filterDirsByFoldersConfig()
dirlist.spinner.Stop()
dirlist.Invalidate()
if done != nil {
done(dirs)
}
}
})
}
func (dirlist *DirectoryList) Select(name string) {
dirlist.selecting = name
dirlist.worker.PostAction(&types.OpenDirectory{Directory: name},
func(msg types.WorkerMessage) {
switch msg.(type) {
case *types.Error:
dirlist.selecting = ""
case *types.Done:
dirlist.selected = dirlist.selecting
dirlist.filterDirsByFoldersConfig()
hasSelected := false
for _, d := range dirlist.dirs {
if d == dirlist.selected {
hasSelected = true
break
}
}
if !hasSelected && dirlist.selected != "" {
dirlist.dirs = append(dirlist.dirs, dirlist.selected)
}
sort.Strings(dirlist.dirs)
}
dirlist.Invalidate()
})
dirlist.Invalidate()
}
func (dirlist *DirectoryList) Selected() string {
return dirlist.selected
}
func (dirlist *DirectoryList) Invalidate() {
dirlist.DoInvalidate(dirlist)
}
func (dirlist *DirectoryList) Draw(ctx *ui.Context) {
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
if dirlist.spinner.IsRunning() {
dirlist.spinner.Draw(ctx)
return
}
if len(dirlist.dirs) == 0 {
style := tcell.StyleDefault
ctx.Printf(0, 0, style, dirlist.uiConf.EmptyDirlist)
return
}
row := 0
for _, name := range dirlist.dirs {
if row >= ctx.Height() {
break
}
style := tcell.StyleDefault
if name == dirlist.selected {
style = style.Reverse(true)
}
ctx.Fill(0, row, ctx.Width(), 1, ' ', style)
ctx.Printf(0, row, style, "%s", name)
row++
}
}
func (dirlist *DirectoryList) nextPrev(delta int) {
curIdx := sort.SearchStrings(dirlist.dirs, dirlist.selected)
if curIdx == len(dirlist.dirs) {
return
}
newIdx := curIdx + delta
ndirs := len(dirlist.dirs)
if newIdx < 0 {
newIdx = ndirs - 1
} else if newIdx >= ndirs {
newIdx = 0
}
dirlist.Select(dirlist.dirs[newIdx])
}
func (dirlist *DirectoryList) Next() {
dirlist.nextPrev(1)
}
func (dirlist *DirectoryList) Prev() {
dirlist.nextPrev(-1)
}
// filterDirsByFoldersConfig sets dirlist.dirs to the filtered subset of the
// dirstore, based on the AccountConfig.Folders option
func (dirlist *DirectoryList) filterDirsByFoldersConfig() {
dirlist.dirs = dirlist.store.List()
// config option defaults to show all if unset
if len(dirlist.acctConf.Folders) == 0 {
return
}
var filtered []string
for _, folder := range dirlist.dirs {
for _, cfgfolder := range dirlist.acctConf.Folders {
if folder == cfgfolder {
filtered = append(filtered, folder)
break
}
}
}
dirlist.dirs = filtered
}