aboutsummaryrefslogtreecommitdiffstats
path: root/gui/widget
diff options
context:
space:
mode:
Diffstat (limited to 'gui/widget')
-rw-r--r--gui/widget/focus.go160
-rw-r--r--gui/widget/widget.go33
2 files changed, 121 insertions, 72 deletions
diff --git a/gui/widget/focus.go b/gui/widget/focus.go
index f1bc06e..d41b5b1 100644
--- a/gui/widget/focus.go
+++ b/gui/widget/focus.go
@@ -1,98 +1,136 @@
package widget
import (
+ "fmt"
"image"
- "sync"
)
-type FocusMaster struct {
- slaves [][]chan bool
- mu sync.Mutex
- p image.Point // coordinates of currently focused slave
-}
+type Direction int
+
+const (
+ UP Direction = iota
+ DOWN
+ LEFT
+ RIGHT
+)
type FocusSlave struct {
- Focus <-chan bool
- Mu *sync.Mutex
+ gain <-chan bool
+ lose <-chan Direction // forward to yield if widget accepts focus loss
+ yield chan<- Direction
+}
+
+type focusSlave struct {
+ gain chan bool
+ lose chan Direction
+}
+
+func (fs focusSlave) close() {
+ close(fs.gain)
+ close(fs.lose)
+}
+
+type FocusMaster struct {
+ slaves [][]focusSlave
+ yield chan Direction
+ focused image.Point // coordinates of focused slave
}
-func NewFocusMaster(rows []int) FocusMaster {
- f := FocusMaster{
- make([][]chan bool, len(rows)),
- sync.Mutex{},
- image.Point{},
+func NewFocusMaster(rows []int) *FocusMaster {
+ fm := &FocusMaster{
+ slaves: make([][]focusSlave, len(rows)),
+ yield: make(chan Direction),
+ focused: image.Point{0, 0},
}
- for i := range f.slaves {
- f.slaves[i] = make([]chan bool, rows[i])
- for j := range f.slaves[i] {
- f.slaves[i][j] = make(chan bool)
+ for y := range fm.slaves {
+ fm.slaves[y] = make([]focusSlave, rows[y])
+ for x := range fm.slaves[y] {
+ fm.slaves[y][x] = focusSlave{make(chan bool), make(chan Direction)}
}
}
- return f
+
+ go func() {
+ fm.slaves[0][0].gain <- true
+
+ for dir := range fm.yield {
+ fm.focused = fm.neighborPos(fm.focused, dir)
+ fm.slaves[fm.focused.Y][fm.focused.X].gain <- true
+ }
+ }()
+
+ return fm
}
-func (f *FocusMaster) Slave(y, x int) FocusSlave {
- return FocusSlave{f.slaves[y][x], &f.mu}
+func (fm FocusMaster) Slave(y, x int) FocusSlave {
+ return FocusSlave{
+ gain: fm.slaves[y][x].gain,
+ lose: fm.slaves[y][x].lose,
+ yield: fm.yield,
+ }
}
-func (f *FocusMaster) Close() {
- for i := range f.slaves {
- for j := range f.slaves[i] {
- close(f.slaves[i][j])
+func (fm FocusMaster) Close() {
+ for y := range fm.slaves {
+ for x := range fm.slaves[y] {
+ fm.slaves[y][x].close()
}
}
+ close(fm.yield)
}
-func (f *FocusMaster) Focus(focus bool) {
- f.slaves[f.p.Y][f.p.X] <- focus
+func (fm FocusMaster) Shift(dir Direction) {
+ fm.slaves[fm.focused.Y][fm.focused.X].lose <- dir
}
-func (f *FocusMaster) TryLeft() {
- if !f.mu.TryLock() {
- return
+func (fm FocusMaster) neighborPos(pos image.Point, dir Direction) image.Point {
+ switch dir {
+ case UP:
+ return fm.upNeighborPos(pos)
+ case DOWN:
+ return fm.downNeighborPos(pos)
+ case LEFT:
+ return fm.leftNeighborPos(pos)
+ case RIGHT:
+ return fm.rightNeighborPos(pos)
+ default:
+ panic(fmt.Sprintf("invalid Direction: %v", dir))
}
- defer f.mu.Unlock()
- f.Focus(false)
- if f.p.X <= 0 {
- f.p.X = len(f.slaves[f.p.Y]) - 1
+}
+
+func (fm FocusMaster) upNeighborPos(pos image.Point) image.Point {
+ if pos.Y <= 0 {
+ pos.Y = len(fm.slaves) - 1
} else {
- f.p.X--
+ pos.Y--
}
- f.Focus(true)
+ pos.X = min(pos.X, len(fm.slaves[pos.Y])-1)
+ return pos
}
-func (f *FocusMaster) TryRight() {
- if !f.mu.TryLock() {
- return
+func (fm FocusMaster) downNeighborPos(pos image.Point) image.Point {
+ if pos.Y >= len(fm.slaves)-1 {
+ pos.Y = 0
+ } else {
+ pos.Y++
}
- defer f.mu.Unlock()
- f.Focus(false)
- f.p.X = (f.p.X + 1) % len(f.slaves[f.p.Y])
- f.Focus(true)
+ pos.X = min(pos.X, len(fm.slaves[pos.Y])-1)
+ return pos
}
-func (f *FocusMaster) TryUp() {
- if !f.mu.TryLock() {
- return
- }
- defer f.mu.Unlock()
- f.Focus(false)
- if f.p.Y <= 0 {
- f.p.Y = len(f.slaves) - 1
+func (fm FocusMaster) leftNeighborPos(pos image.Point) image.Point {
+ if pos.X <= 0 {
+ pos.X = len(fm.slaves[pos.Y]) - 1
} else {
- f.p.Y--
+ pos.X--
}
- f.p.X = min(f.p.X, len(f.slaves[f.p.Y])-1)
- f.Focus(true)
+ return pos
}
-func (f *FocusMaster) TryDown() {
- if !f.mu.TryLock() {
- return
+func (fm FocusMaster) rightNeighborPos(pos image.Point) image.Point {
+ if pos.X >= len(fm.slaves[pos.Y])-1 {
+ pos.X = 0
+ } else {
+ pos.X++
}
- defer f.mu.Unlock()
- f.Focus(false)
- f.p.Y = (f.p.Y + 1) % len(f.slaves)
- f.p.X = min(f.p.X, len(f.slaves[f.p.Y])-1)
- f.Focus(true)
+ return pos
}
diff --git a/gui/widget/widget.go b/gui/widget/widget.go
index 8c80551..41fb621 100644
--- a/gui/widget/widget.go
+++ b/gui/widget/widget.go
@@ -46,9 +46,9 @@ func Input(val chan<- uint, r image.Rectangle, focus FocusSlave, env gui.Env, wg
defer close(env.Draw())
defer close(val)
- redraw := func(text []byte, haveFocus bool) func(draw.Image) image.Rectangle {
+ redraw := func(text []byte, focused bool) func(draw.Image) image.Rectangle {
return func(drw draw.Image) image.Rectangle {
- if haveFocus {
+ if focused {
drawText(text, drw, r, GREEN, FOCUS_COLOR)
} else {
drawText(text, drw, r, GREEN, WHITE)
@@ -57,14 +57,25 @@ func Input(val chan<- uint, r image.Rectangle, focus FocusSlave, env gui.Env, wg
}
}
text := []byte{'0'}
- haveFocus := false
+ focused := false
- env.Draw() <- redraw(text, haveFocus)
+ env.Draw() <- redraw(text, focused)
Loop:
for {
select {
- case haveFocus = <-focus.Focus:
- env.Draw() <- redraw(text, haveFocus)
+ case _, ok := <-focus.gain:
+ if !ok {
+ break Loop
+ }
+ focused = true
+ env.Draw() <- redraw(text, focused)
+ case dir, ok := <-focus.lose:
+ if !ok {
+ break Loop
+ }
+ focus.yield <- dir
+ focused = false
+ env.Draw() <- redraw(text, focused)
case event, ok := <-env.Events():
if !ok { // channel closed
break Loop
@@ -72,18 +83,18 @@ Loop:
switch event := event.(type) {
case win.WiFocus:
if event.Focused {
- env.Draw() <- redraw(text, haveFocus)
+ env.Draw() <- redraw(text, focused)
}
case win.KbType:
- if haveFocus && isDigit(event.Rune) {
+ if focused && isDigit(event.Rune) {
text = fmt.Appendf(text, "%c", event.Rune)
- env.Draw() <- redraw(text, haveFocus)
+ env.Draw() <- redraw(text, focused)
val <- atoi(text)
}
case win.KbDown:
- if haveFocus && event.Key == win.KeyBackspace && len(text) > 0 {
+ if focused && event.Key == win.KeyBackspace && len(text) > 0 {
text = text[:len(text)-1]
- env.Draw() <- redraw(text, haveFocus)
+ env.Draw() <- redraw(text, focused)
val <- atoi(text)
}
}