aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2019-01-13 15:10:47 -0500
committerDrew DeVault <sir@cmpwn.com>2019-01-13 15:10:47 -0500
commit3157897c1a20e5638feaf56e753b7886bc4ba267 (patch)
treed47b8ea1654a5627811df1e5ad5c5cfd172358fc
parent755aa9af731c6faac64cbe3ad77324fb2850a7ae (diff)
Add abstract list, update dirlist accordingly
-rw-r--r--lib/ui/grid.go1
-rw-r--r--lib/ui/list.go110
-rw-r--r--widgets/directories.go71
3 files changed, 156 insertions, 26 deletions
diff --git a/lib/ui/grid.go b/lib/ui/grid.go
index 3c375ee..5fe6ab2 100644
--- a/lib/ui/grid.go
+++ b/lib/ui/grid.go
@@ -5,6 +5,7 @@ import (
"math"
)
+// A container which arranges its children on a grid.
type Grid struct {
rows []GridSpec
rowLayout []gridLayout
diff --git a/lib/ui/list.go b/lib/ui/list.go
new file mode 100644
index 0000000..58f304f
--- /dev/null
+++ b/lib/ui/list.go
@@ -0,0 +1,110 @@
+package ui
+
+import (
+ "fmt"
+)
+
+// A container which arranges its children in a list.
+type List struct {
+ Items []*ListItem
+ itemHeight int
+ onInvalidate func(d Drawable)
+ selected int
+}
+
+type ListItem struct {
+ Content Drawable
+ invalid bool
+}
+
+type SelectableDrawable interface {
+ Drawable
+ DrawWithSelected(ctx *Context, selected bool)
+}
+
+func NewList() *List {
+ return &List{itemHeight: 1, selected: -1}
+}
+
+func (list *List) OnInvalidate(onInvalidate func(d Drawable)) {
+ list.onInvalidate = onInvalidate
+}
+
+func (list *List) Invalidate() {
+ for _, item := range list.Items {
+ item.Content.Invalidate()
+ }
+ if list.onInvalidate != nil {
+ list.onInvalidate(list)
+ }
+}
+
+func (list *List) Draw(ctx *Context) {
+ for i, item := range list.Items {
+ if !item.invalid {
+ continue
+ }
+ subctx := ctx.Subcontext(0, i, ctx.Width(), list.itemHeight)
+ if content, ok := item.Content.(SelectableDrawable); ok {
+ content.DrawWithSelected(subctx, i == list.selected)
+ } else {
+ item.Content.Draw(subctx)
+ }
+ }
+}
+
+func (list *List) Add(child Drawable) {
+ list.Items = append(list.Items, &ListItem{Content: child, invalid: true})
+ child.OnInvalidate(list.childInvalidated)
+ list.Invalidate()
+}
+
+func (list *List) Remove(child Drawable) {
+ for i, item := range list.Items {
+ if item.Content == child {
+ list.Items = append(list.Items[:i], list.Items[i+1:]...)
+ child.OnInvalidate(nil)
+ list.Invalidate()
+ return
+ }
+ }
+ panic(fmt.Errorf("Attempted to remove unknown child"))
+}
+
+func (list *List) Set(items []Drawable) {
+ for _, item := range list.Items {
+ item.Content.OnInvalidate(nil)
+ }
+ list.Items = make([]*ListItem, len(items))
+ for i, item := range items {
+ list.Items[i] = &ListItem{Content: item, invalid: true}
+ item.OnInvalidate(list.childInvalidated)
+ }
+ list.Invalidate()
+}
+
+func (list *List) Select(index int) {
+ if index >= len(list.Items) || index < 0 {
+ panic(fmt.Errorf("Attempted to select unknown child"))
+ }
+ list.selected = index
+ list.Invalidate()
+}
+
+func (list *List) ItemHeight(height int) {
+ list.itemHeight = height
+ list.Invalidate()
+}
+
+func (list *List) childInvalidated(child Drawable) {
+ for _, item := range list.Items {
+ if item.Content == child {
+ item.invalid = true
+ if list.onInvalidate != nil {
+ list.onInvalidate(list)
+ }
+ return
+ }
+ }
+ panic(fmt.Errorf("Attempted to invalidate unknown child"))
+}
diff --git a/widgets/directories.go b/widgets/directories.go
index 16b0e5a..ff2f6f5 100644
--- a/widgets/directories.go
+++ b/widgets/directories.go
@@ -3,6 +3,7 @@ package widgets
import (
"log"
"sort"
+ "strings"
"github.com/gdamore/tcell"
@@ -13,7 +14,7 @@ import (
type DirectoryList struct {
conf *config.AccountConfig
- dirs []string
+ dirs *ui.List
logger *log.Logger
onInvalidate func(d ui.Drawable)
worker *types.Worker
@@ -22,50 +23,68 @@ type DirectoryList struct {
func NewDirectoryList(conf *config.AccountConfig,
logger *log.Logger, worker *types.Worker) *DirectoryList {
- return &DirectoryList{conf: conf, logger: logger, worker: worker}
+ return &DirectoryList{
+ conf: conf,
+ dirs: ui.NewList(),
+ logger: logger,
+ worker: worker,
+ }
}
func (dirlist *DirectoryList) UpdateList() {
- var dirs []string
+ var dirs []ui.Drawable
dirlist.worker.PostAction(
&types.ListDirectories{}, func(msg types.WorkerMessage) {
switch msg := msg.(type) {
case *types.Directory:
- dirs = append(dirs, msg.Name)
+ if len(dirlist.conf.Folders) > 1 {
+ idx := sort.SearchStrings(dirlist.conf.Folders, msg.Name)
+ if idx == len(dirlist.conf.Folders) ||
+ dirlist.conf.Folders[idx] != msg.Name {
+ break
+ }
+ }
+ dirs = append(dirs, directoryEntry(msg.Name))
case *types.Done:
- sort.Strings(dirs)
- dirlist.dirs = dirs
- dirlist.Invalidate()
+ sort.Slice(dirs, func(_a, _b int) bool {
+ a, _ := dirs[_a].(directoryEntry)
+ b, _ := dirs[_b].(directoryEntry)
+ return strings.Compare(string(a), string(b)) > 0
+ })
+ dirlist.dirs.Set(dirs)
}
})
}
func (dirlist *DirectoryList) OnInvalidate(onInvalidate func(d ui.Drawable)) {
- dirlist.onInvalidate = onInvalidate
+ dirlist.dirs.OnInvalidate(func(_ ui.Drawable) {
+ onInvalidate(dirlist)
+ })
}
func (dirlist *DirectoryList) Invalidate() {
- if dirlist.onInvalidate != nil {
- dirlist.onInvalidate(dirlist)
- }
+ dirlist.dirs.Invalidate()
}
func (dirlist *DirectoryList) Draw(ctx *ui.Context) {
+ dirlist.dirs.Draw(ctx)
+}
+
+type directoryEntry string
+
+func (d directoryEntry) OnInvalidate(_ func(_ ui.Drawable)) {
+}
+
+func (d directoryEntry) Invalidate() {
+}
+
+func (d directoryEntry) Draw(ctx *ui.Context) {
+ d.DrawWithSelected(ctx, false)
+}
+
+func (d directoryEntry) DrawWithSelected(ctx *ui.Context, selected bool) {
+ // TODO: distinguish the selected item
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
- row := 0
- for _, name := range dirlist.dirs {
- if row >= ctx.Height() {
- break
- }
- if len(dirlist.conf.Folders) > 1 {
- idx := sort.SearchStrings(dirlist.conf.Folders, name)
- if idx == len(dirlist.conf.Folders) ||
- dirlist.conf.Folders[idx] != name {
- continue
- }
- }
- ctx.Printf(0, row, tcell.StyleDefault, "%s", name)
- row++
- }
+ ctx.Printf(0, 0, tcell.StyleDefault, "%s", d)
}