diff --git a/buffer.go b/buffer.go index 6be54b1..e8f3ab5 100644 --- a/buffer.go +++ b/buffer.go @@ -32,8 +32,8 @@ type Buffer struct { minX, minY int } -func NewBuffer() Buffer { - return Buffer{cells: make(map[Coord]Cell)} +func NewBuffer() *Buffer { + return &Buffer{cells: make(map[Coord]Cell)} } func (b *Buffer) Width() int { return b.maxX - b.minX } diff --git a/timemap.go b/timemap.go new file mode 100644 index 0000000..1a8a16b --- /dev/null +++ b/timemap.go @@ -0,0 +1,91 @@ +/* +Copyright © Brian Buller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package widgets + +import ( + "time" + + "github.com/gdamore/tcell" +) + +// TODO: Support recurring jobs +type TimeMap struct { + Events map[time.Time]func(*tcell.EventTime) bool + Jobs map[time.Duration]func(*tcell.EventTime) bool +} + +func BlankTimeMap() *TimeMap { + return &TimeMap{ + Events: make(map[time.Time]func(*tcell.EventTime) bool), + Jobs: make(map[time.Duration]func(*tcell.EventTime) bool), + } +} + +func NewEventMap(m map[time.Time]func(*tcell.EventTime) bool) TimeMap { + return TimeMap{ + Events: m, + Jobs: make(map[time.Duration]func(*tcell.EventTime) bool), + } +} + +func NewJobMap(m map[time.Duration]func(*tcell.EventTime) bool) TimeMap { + return TimeMap{ + Events: make(map[time.Time]func(*tcell.EventTime) bool), + Jobs: m, + } +} + +func (m *TimeMap) Merge(tm TimeMap) { + for t, v := range tm.Events { + m.Events[t] = v + } + for j, v := range tm.Jobs { + m.Jobs[j] = v + } +} + +func (m *TimeMap) Add(e time.Time, do func(*tcell.EventTime) bool) { m.Events[e] = do } +func (m *TimeMap) Remove(e time.Time) { delete(m.Events, e) } + +func (m *TimeMap) AddAll(all map[time.Time]func(*tcell.EventTime) bool) { + for t, v := range all { + m.Add(t, v) + } +} + +func (m *TimeMap) AddJob(d time.Duration, do func(*tcell.EventTime) bool) { m.Jobs[d] = do } +func (m *TimeMap) RemoveJob(d time.Duration) { delete(m.Jobs, d) } +func (m *TimeMap) AddJobs(all map[time.Duration]func(*tcell.EventTime) bool) { + for k, v := range all { + m.AddJob(k, v) + } +} + +func (m *TimeMap) Handle(ev *tcell.EventTime) bool { + var ranSomething bool + for t, do := range m.Events { + if ev.When() == t { + ranSomething = ranSomething || do(ev) + } + } + return ranSomething +} diff --git a/wdgt_asciiarttext.go b/wdgt_asciiarttext.go index 1dc81eb..2af2bb7 100644 --- a/wdgt_asciiarttext.go +++ b/wdgt_asciiarttext.go @@ -36,7 +36,7 @@ type ArtWidget struct { x, y int w, h int - buffer Buffer + buffer *Buffer active bool visible bool diff --git a/wdgt_buffer.go b/wdgt_buffer.go new file mode 100644 index 0000000..b77e50a --- /dev/null +++ b/wdgt_buffer.go @@ -0,0 +1,110 @@ +/* +Copyright © Brian Buller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package widgets + +import ( + "github.com/gdamore/tcell" +) + +// The Buffer widget is a widget that is manually setup by the user +type BufferWidget struct { + id string + style tcell.Style + + x, y int + w, h int + visible bool + active bool + focusable bool + + buffer *Buffer + + keyMap KeyMap + + timeMap *TimeMap +} + +var _ Widget = (*BufferWidget)(nil) + +func NewBufferWidget(id string, s tcell.Style) *BufferWidget { + ret := &BufferWidget{} + ret.Init(id, s) + return ret +} + +func (w *BufferWidget) Init(id string, s tcell.Style) { + w.id = id + w.style = s + w.visible = true + w.timeMap = BlankTimeMap() + w.keyMap = BlankKeyMap() + w.buffer = NewBuffer() +} + +func (w *BufferWidget) Id() string { return w.id } +func (w *BufferWidget) HandleResize(ev *tcell.EventResize) { +} + +func (w *BufferWidget) SetKeyMap(km KeyMap, def bool) { w.keyMap = km } +func (w *BufferWidget) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *BufferWidget) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + +func (w *BufferWidget) HandleKey(ev *tcell.EventKey) bool { + if !w.active { + return false + } + return w.keyMap.Handle(ev) +} +func (w *BufferWidget) HandleTime(ev *tcell.EventTime) { w.timeMap.Handle(ev) } +func (w *BufferWidget) Draw(screen tcell.Screen) { w.buffer.Draw(w.x, w.y, screen) } +func (w *BufferWidget) Active() bool { return w.active } +func (w *BufferWidget) SetActive(a bool) { w.active = a } +func (w *BufferWidget) Visible() bool { return w.visible } +func (w *BufferWidget) SetVisible(a bool) { w.visible = a } +func (w *BufferWidget) Focusable() bool { return w.focusable } +func (w *BufferWidget) SetFocusable(b bool) { w.focusable = b } +func (w *BufferWidget) SetX(x int) { w.x = x } +func (w *BufferWidget) SetY(y int) { w.y = y } +func (w *BufferWidget) GetX() int { return w.x } +func (w *BufferWidget) GetY() int { return w.y } +func (w *BufferWidget) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *BufferWidget) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *BufferWidget) GetW() int { return w.buffer.Width() } +func (w *BufferWidget) GetH() int { return w.buffer.Height() } +func (w *BufferWidget) SetW(wd int) { w.w = wd } +func (w *BufferWidget) SetH(h int) { w.h = h } +func (w *BufferWidget) SetSize(c Coord) { w.w, w.h = c.X, c.Y } + +func (w *BufferWidget) WantW() int { return w.buffer.Width() } +func (w *BufferWidget) WantH() int { return w.buffer.Height() } + +func (w *BufferWidget) MinW() int { return w.buffer.Width() } +func (w *BufferWidget) MinH() int { return w.buffer.Height() } + +func (w *BufferWidget) GetBuffer() *Buffer { return w.buffer } diff --git a/wdgt_cli.go b/wdgt_cli.go index 5887e7e..c72aeef 100644 --- a/wdgt_cli.go +++ b/wdgt_cli.go @@ -291,20 +291,20 @@ func (w *Cli) initKeyMap() { return true }, tcell.KeyPgUp: func(ev *tcell.EventKey) bool { - if w.historyPosition < len(w.log)-w.h-2 { - w.historyPosition += (w.h - 2) - if w.historyPosition > len(w.log) { - w.historyPosition = len(w.log) + if w.logPosition < len(w.log)-w.h-2 { + w.logPosition += (w.h - 2) + if w.logPosition > len(w.log) { + w.logPosition = len(w.log) } return true } return false }, tcell.KeyPgDn: func(ev *tcell.EventKey) bool { - if w.historyPosition > 0 { - w.historyPosition -= (w.h - 2) - if w.historyPosition < 0 { - w.historyPosition = 0 + if w.logPosition > 0 { + w.logPosition -= (w.h - 2) + if w.logPosition < 0 { + w.logPosition = 0 } return true } diff --git a/wdgt_searcher.go b/wdgt_searcher.go index f6a36c7..2ce8e2c 100644 --- a/wdgt_searcher.go +++ b/wdgt_searcher.go @@ -35,7 +35,7 @@ type Searcher struct { x, y int w, h int - buffer Buffer + buffer *Buffer active bool visible bool diff --git a/wdgt_spinner.go b/wdgt_spinner.go index 4fe5355..4504960 100644 --- a/wdgt_spinner.go +++ b/wdgt_spinner.go @@ -41,6 +41,12 @@ type Spinner struct { var _ Widget = (*Spinner)(nil) +var ( + SPINNER_DOTS = []rune{'⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'} + SPINNER_DIRS = []rune{'←', '↖', '↑', '↗', '→', '↘', '↓', '↙'} + SPINNER_FILL = []rune{'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█', '▇', '▆', '▅', '▄', '▃', '▁'} +) + func NewSpinner(id string, style tcell.Style) *Spinner { ret := &Spinner{id: id} ret.Init(id, style) @@ -48,7 +54,7 @@ func NewSpinner(id string, style tcell.Style) *Spinner { } func (w *Spinner) Init(id string, st tcell.Style) { - w.frames = []rune{'⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'} + w.frames = SPINNER_DOTS w.tickInterval = time.Second w.keyMap = BlankKeyMap() } @@ -66,10 +72,7 @@ func (w *Spinner) RemoveFromKeyMap(km KeyMap) { } func (w *Spinner) HandleKey(ev *tcell.EventKey) bool { return false } func (w *Spinner) HandleTime(ev *tcell.EventTime) { - for w.lastTick.Before(ev.When()) { - w.lastTick = w.lastTick.Add(w.tickInterval) - w.currentFrame = (w.currentFrame + 1) % len(w.frames) - } + w.currentFrame = (w.currentFrame + 1) % len(w.frames) } func (w *Spinner) Draw(screen tcell.Screen) {