Merge pull request #179 from mum4k/draw-metadata

Provide metadata to widgets when drawing.
This commit is contained in:
Jakub Sobon 2019-04-03 23:26:27 -04:00 committed by GitHub
commit 9543219b48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 297 additions and 127 deletions

View File

@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Changed
- Widgets now get information whether their container is focused when Draw is
executed.
#### Breaking API changes
- The widgetapi.Widget.Draw method now accepts a second argument which provides
widgets with additional metadata. This affects all implemented widgets.
## [0.8.0] - 30-Mar-2019
### Added

View File

@ -907,9 +907,12 @@ func TestNew(t *testing.T) {
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
testdraw.MustBorder(cvs, image.Rect(0, 0, 10, 10))
testdraw.MustText(cvs, "(10,10)", image.Point{1, 1})
testcanvas.MustApply(cvs, ft)
fakewidget.MustDraw(
ft,
cvs,
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
return ft
},
},
@ -1033,11 +1036,13 @@ func TestKeyboard(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 0, 20, 20)),
&widgetapi.Meta{},
widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused},
)
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(20, 0, 40, 10)),
&widgetapi.Meta{},
widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused},
)
@ -1045,6 +1050,7 @@ func TestKeyboard(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(20, 10, 40, 20)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused},
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
)
@ -1089,6 +1095,7 @@ func TestKeyboard(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 0, 20, 20)),
&widgetapi.Meta{},
widgetapi.Options{WantKeyboard: widgetapi.KeyScopeGlobal},
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
)
@ -1097,6 +1104,7 @@ func TestKeyboard(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(20, 0, 40, 10)),
&widgetapi.Meta{},
widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused},
)
@ -1104,6 +1112,7 @@ func TestKeyboard(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(20, 10, 40, 20)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused},
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
)
@ -1128,6 +1137,7 @@ func TestKeyboard(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
return ft
@ -1152,6 +1162,7 @@ func TestKeyboard(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
return ft
@ -1249,6 +1260,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
return ft
@ -1301,11 +1313,13 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 0, 25, 20)),
&widgetapi.Meta{},
widgetapi.Options{},
)
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(25, 10, 50, 20)),
&widgetapi.Meta{},
widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget},
&terminalapi.Keyboard{},
)
@ -1314,6 +1328,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(25, 0, 50, 10)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget},
&terminalapi.Mouse{Position: image.Point{24, 9}, Button: mouse.ButtonLeft},
&terminalapi.Mouse{Position: image.Point{24, 9}, Button: mouse.ButtonRelease},
@ -1381,11 +1396,13 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 0, 25, 20)),
&widgetapi.Meta{},
widgetapi.Options{},
)
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(25, 10, 50, 20)),
&widgetapi.Meta{},
widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget},
&terminalapi.Keyboard{},
)
@ -1394,6 +1411,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(26, 1, 49, 9)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget},
&terminalapi.Mouse{Position: image.Point{22, 7}, Button: mouse.ButtonLeft},
&terminalapi.Mouse{Position: image.Point{22, 7}, Button: mouse.ButtonRelease},
@ -1419,6 +1437,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
return ft
@ -1453,6 +1472,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(1, 1, 19, 19)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
return ft
@ -1487,6 +1507,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(1, 1, 20, 19)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget},
&terminalapi.Mouse{Position: image.Point{-1, -1}, Button: mouse.ButtonLeft},
)
@ -1522,6 +1543,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(1, 1, 20, 19)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget},
&terminalapi.Mouse{Position: image.Point{-1, -1}, Button: mouse.ButtonLeft},
)
@ -1553,6 +1575,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 5, 20, 15)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
return ft
@ -1583,6 +1606,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 5, 20, 15)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget},
&terminalapi.Mouse{Position: image.Point{-1, -1}, Button: mouse.ButtonLeft},
)
@ -1614,6 +1638,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 5, 20, 15)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget},
&terminalapi.Mouse{Position: image.Point{-1, -1}, Button: mouse.ButtonLeft},
)
@ -1650,6 +1675,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 10, 20, 20)),
&widgetapi.Meta{},
widgetapi.Options{},
)
return ft
@ -1685,6 +1711,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 10, 20, 20)),
&widgetapi.Meta{},
widgetapi.Options{},
)
return ft
@ -1720,6 +1747,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 10, 20, 20)),
&widgetapi.Meta{},
widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget},
&terminalapi.Mouse{Position: image.Point{-1, -1}, Button: mouse.ButtonLeft},
)
@ -1751,6 +1779,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 5, 20, 15)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget},
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft},
)
@ -1782,6 +1811,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(6, 0, 24, 20)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget},
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft},
)
@ -1807,6 +1837,7 @@ func TestMouse(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
return ft
@ -2009,7 +2040,7 @@ func TestUpdate(t *testing.T) {
cvs := testcanvas.MustNew(ft.Area())
wAr := image.Rect(10, 0, 20, 10)
wCvs := testcanvas.MustNew(wAr)
fakewidget.MustDraw(ft, wCvs, widgetapi.Options{})
fakewidget.MustDraw(ft, wCvs, &widgetapi.Meta{}, widgetapi.Options{})
testcanvas.MustCopyTo(wCvs, cvs)
testcanvas.MustApply(cvs, ft)
return ft
@ -2041,7 +2072,7 @@ func TestUpdate(t *testing.T) {
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
fakewidget.MustDraw(ft, cvs, widgetapi.Options{})
fakewidget.MustDraw(ft, cvs, &widgetapi.Meta{Focused: true}, widgetapi.Options{})
testcanvas.MustApply(cvs, ft)
return ft
},
@ -2212,6 +2243,7 @@ func TestUpdate(t *testing.T) {
fakewidget.MustDraw(
ft,
cvs,
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused},
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
)
@ -2243,6 +2275,7 @@ func TestUpdate(t *testing.T) {
fakewidget.MustDraw(
ft,
cvs,
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget},
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonRelease},
)

View File

@ -25,6 +25,7 @@ import (
"github.com/mum4k/termdash/internal/area"
"github.com/mum4k/termdash/internal/canvas"
"github.com/mum4k/termdash/internal/draw"
"github.com/mum4k/termdash/widgetapi"
)
// drawTree draws this container and all of its sub containers.
@ -130,7 +131,11 @@ func drawWidget(c *Container) error {
return err
}
if err := c.opts.widget.Draw(cvs); err != nil {
meta := &widgetapi.Meta{
Focused: c.focusTracker.isActive(c),
}
if err := c.opts.widget.Draw(cvs, meta); err != nil {
return err
}
return cvs.Apply(c.term)

View File

@ -245,7 +245,7 @@ func TestDrawWidget(t *testing.T) {
wAr := image.Rect(5, 2, 17, 6)
wCvs := testcanvas.MustNew(wAr)
// Fake widget border.
fakewidget.MustDraw(ft, wCvs, widgetapi.Options{})
fakewidget.MustDraw(ft, wCvs, &widgetapi.Meta{}, widgetapi.Options{})
testcanvas.MustCopyTo(wCvs, cvs)
testcanvas.MustApply(cvs, ft)
return ft
@ -278,7 +278,7 @@ func TestDrawWidget(t *testing.T) {
wAr := image.Rect(4, 2, 14, 16)
wCvs := testcanvas.MustNew(wAr)
// Fake widget border.
fakewidget.MustDraw(ft, wCvs, widgetapi.Options{})
fakewidget.MustDraw(ft, wCvs, &widgetapi.Meta{Focused: true}, widgetapi.Options{})
testcanvas.MustCopyTo(wCvs, cvs)
testcanvas.MustApply(cvs, ft)
return ft
@ -357,7 +357,7 @@ func TestDrawWidget(t *testing.T) {
wAr := image.Rect(9, 3, 25, 13)
wCvs := testcanvas.MustNew(wAr)
// Fake widget border.
fakewidget.MustDraw(ft, wCvs, widgetapi.Options{})
fakewidget.MustDraw(ft, wCvs, &widgetapi.Meta{Focused: true}, widgetapi.Options{})
testcanvas.MustCopyTo(wCvs, cvs)
testcanvas.MustApply(cvs, ft)
return ft
@ -600,7 +600,7 @@ func TestDrawWidget(t *testing.T) {
// Fake widget.
cvs := testcanvas.MustNew(image.Rect(1, 1, 11, 11))
fakewidget.MustDraw(ft, cvs, widgetapi.Options{})
fakewidget.MustDraw(ft, cvs, &widgetapi.Meta{Focused: true}, widgetapi.Options{})
testcanvas.MustApply(cvs, ft)
return ft
},
@ -632,7 +632,7 @@ func TestDrawWidget(t *testing.T) {
// Fake widget.
cvs := testcanvas.MustNew(image.Rect(1, 1, 11, 21))
fakewidget.MustDraw(ft, cvs, widgetapi.Options{})
fakewidget.MustDraw(ft, cvs, &widgetapi.Meta{Focused: true}, widgetapi.Options{})
testcanvas.MustApply(cvs, ft)
return ft
},
@ -664,7 +664,7 @@ func TestDrawWidget(t *testing.T) {
// Fake widget.
cvs := testcanvas.MustNew(image.Rect(1, 1, 21, 11))
fakewidget.MustDraw(ft, cvs, widgetapi.Options{})
fakewidget.MustDraw(ft, cvs, &widgetapi.Meta{Focused: true}, widgetapi.Options{})
testcanvas.MustApply(cvs, ft)
return ft
},
@ -694,9 +694,15 @@ func TestDrawWidget(t *testing.T) {
)
// Fake widget border.
testdraw.MustBorder(cvs, image.Rect(1, 1, 11, 21))
testdraw.MustText(cvs, "(10,20)", image.Point{2, 2})
wCvs := testcanvas.MustNew(image.Rect(1, 1, 11, 21))
fakewidget.MustDraw(
ft,
wCvs,
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
testcanvas.MustCopyTo(wCvs, cvs)
testcanvas.MustApply(cvs, ft)
return ft
},
@ -727,7 +733,7 @@ func TestDrawWidget(t *testing.T) {
// Fake widget.
cvs := testcanvas.MustNew(image.Rect(1, 1, 20, 20))
fakewidget.MustDraw(ft, cvs, widgetapi.Options{})
fakewidget.MustDraw(ft, cvs, &widgetapi.Meta{Focused: true}, widgetapi.Options{})
testcanvas.MustApply(cvs, ft)
return ft
},
@ -757,9 +763,15 @@ func TestDrawWidget(t *testing.T) {
)
// Fake widget border.
testdraw.MustBorder(cvs, image.Rect(1, 1, 11, 21))
testdraw.MustText(cvs, "(10,20)", image.Point{2, 2})
wCvs := testcanvas.MustNew(image.Rect(1, 1, 11, 21))
fakewidget.MustDraw(
ft,
wCvs,
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
testcanvas.MustCopyTo(wCvs, cvs)
testcanvas.MustApply(cvs, ft)
return ft
},
@ -788,9 +800,15 @@ func TestDrawWidget(t *testing.T) {
)
// Fake widget border.
testdraw.MustBorder(cvs, image.Rect(6, 1, 16, 21))
testdraw.MustText(cvs, "(10,20)", image.Point{7, 2})
wCvs := testcanvas.MustNew(image.Rect(6, 1, 16, 21))
fakewidget.MustDraw(
ft,
wCvs,
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
testcanvas.MustCopyTo(wCvs, cvs)
testcanvas.MustApply(cvs, ft)
return ft
},
@ -819,9 +837,15 @@ func TestDrawWidget(t *testing.T) {
)
// Fake widget border.
testdraw.MustBorder(cvs, image.Rect(11, 1, 21, 21))
testdraw.MustText(cvs, "(10,20)", image.Point{12, 2})
wCvs := testcanvas.MustNew(image.Rect(11, 1, 21, 21))
fakewidget.MustDraw(
ft,
wCvs,
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
testcanvas.MustCopyTo(wCvs, cvs)
testcanvas.MustApply(cvs, ft)
return ft
},
@ -850,9 +874,15 @@ func TestDrawWidget(t *testing.T) {
)
// Fake widget border.
testdraw.MustBorder(cvs, image.Rect(1, 1, 21, 11))
testdraw.MustText(cvs, "(20,10)", image.Point{2, 2})
wCvs := testcanvas.MustNew(image.Rect(1, 1, 21, 11))
fakewidget.MustDraw(
ft,
wCvs,
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
testcanvas.MustCopyTo(wCvs, cvs)
testcanvas.MustApply(cvs, ft)
return ft
},
@ -881,9 +911,15 @@ func TestDrawWidget(t *testing.T) {
)
// Fake widget border.
testdraw.MustBorder(cvs, image.Rect(1, 6, 21, 16))
testdraw.MustText(cvs, "(20,10)", image.Point{2, 7})
wCvs := testcanvas.MustNew(image.Rect(1, 6, 21, 16))
fakewidget.MustDraw(
ft,
wCvs,
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
testcanvas.MustCopyTo(wCvs, cvs)
testcanvas.MustApply(cvs, ft)
return ft
},
@ -912,9 +948,15 @@ func TestDrawWidget(t *testing.T) {
)
// Fake widget border.
testdraw.MustBorder(cvs, image.Rect(1, 11, 21, 21))
testdraw.MustText(cvs, "(20,10)", image.Point{2, 12})
wCvs := testcanvas.MustNew(image.Rect(1, 11, 21, 21))
fakewidget.MustDraw(
ft,
wCvs,
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
testcanvas.MustCopyTo(wCvs, cvs)
testcanvas.MustApply(cvs, ft)
return ft
},
@ -994,21 +1036,25 @@ func TestDrawHandlesTerminalResize(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 0, 30, 5)),
&widgetapi.Meta{},
widgetapi.Options{},
)
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 5, 30, 10)),
&widgetapi.Meta{},
widgetapi.Options{},
)
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(30, 0, 45, 10)),
&widgetapi.Meta{},
widgetapi.Options{},
)
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(45, 0, 60, 10)),
&widgetapi.Meta{},
widgetapi.Options{},
)
return ft
@ -1023,21 +1069,25 @@ func TestDrawHandlesTerminalResize(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 0, 40, 5)),
&widgetapi.Meta{},
widgetapi.Options{},
)
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 5, 40, 10)),
&widgetapi.Meta{},
widgetapi.Options{},
)
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(40, 0, 60, 10)),
&widgetapi.Meta{},
widgetapi.Options{},
)
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(60, 0, 80, 10)),
&widgetapi.Meta{},
widgetapi.Options{},
)
return ft
@ -1052,21 +1102,25 @@ func TestDrawHandlesTerminalResize(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 0, 25, 5)),
&widgetapi.Meta{},
widgetapi.Options{},
)
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 5, 25, 10)),
&widgetapi.Meta{},
widgetapi.Options{},
)
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(25, 0, 37, 10)),
&widgetapi.Meta{},
widgetapi.Options{},
)
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(37, 0, 50, 10)),
&widgetapi.Meta{},
widgetapi.Options{},
)
return ft

View File

@ -368,7 +368,7 @@ func TestBuilder(t *testing.T) {
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
fakewidget.MustDraw(ft, cvs, widgetapi.Options{})
fakewidget.MustDraw(ft, cvs, &widgetapi.Meta{Focused: true}, widgetapi.Options{})
return ft
},
},
@ -384,8 +384,8 @@ func TestBuilder(t *testing.T) {
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
top, bot := mustHSplit(ft.Area(), 50)
fakewidget.MustDraw(ft, testcanvas.MustNew(top), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(bot), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(top), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(bot), &widgetapi.Meta{}, widgetapi.Options{})
return ft
},
},
@ -401,8 +401,8 @@ func TestBuilder(t *testing.T) {
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
top, bot := mustHSplit(ft.Area(), 20)
fakewidget.MustDraw(ft, testcanvas.MustNew(top), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(bot), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(top), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(bot), &widgetapi.Meta{}, widgetapi.Options{})
return ft
},
},
@ -418,8 +418,8 @@ func TestBuilder(t *testing.T) {
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
left, right := mustVSplit(ft.Area(), 50)
fakewidget.MustDraw(ft, testcanvas.MustNew(left), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(right), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(left), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(right), &widgetapi.Meta{}, widgetapi.Options{})
return ft
},
},
@ -435,8 +435,8 @@ func TestBuilder(t *testing.T) {
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
left, right := mustVSplit(ft.Area(), 20)
fakewidget.MustDraw(ft, testcanvas.MustNew(left), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(right), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(left), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(right), &widgetapi.Meta{}, widgetapi.Options{})
return ft
},
},
@ -465,10 +465,10 @@ func TestBuilder(t *testing.T) {
topLeft, topRight := mustVSplit(top, 50)
botLeft, botRight := mustVSplit(bot, 50)
fakewidget.MustDraw(ft, testcanvas.MustNew(topLeft), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botLeft), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topLeft), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topRight), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botLeft), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botRight), &widgetapi.Meta{}, widgetapi.Options{})
return ft
},
},
@ -497,10 +497,10 @@ func TestBuilder(t *testing.T) {
topLeft, topRight := mustVSplit(top, 20)
botLeft, botRight := mustVSplit(bot, 80)
fakewidget.MustDraw(ft, testcanvas.MustNew(topLeft), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botLeft), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topLeft), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topRight), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botLeft), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botRight), &widgetapi.Meta{}, widgetapi.Options{})
return ft
},
},
@ -529,10 +529,10 @@ func TestBuilder(t *testing.T) {
topLeft, topRight := mustVSplit(top, 50)
botLeft, botRight := mustVSplit(bot, 50)
fakewidget.MustDraw(ft, testcanvas.MustNew(topLeft), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botLeft), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topLeft), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topRight), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botLeft), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botRight), &widgetapi.Meta{}, widgetapi.Options{})
return ft
},
},
@ -561,10 +561,10 @@ func TestBuilder(t *testing.T) {
topLeft, topRight := mustHSplit(left, 20)
botLeft, botRight := mustHSplit(right, 80)
fakewidget.MustDraw(ft, testcanvas.MustNew(topLeft), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botLeft), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topLeft), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topRight), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botLeft), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botRight), &widgetapi.Meta{}, widgetapi.Options{})
return ft
},
},
@ -613,14 +613,14 @@ func TestBuilder(t *testing.T) {
topBotLeft, topBotRight := mustVSplit(topBot, 50)
botTopLeft, botTopRight := mustVSplit(botTop, 50)
botBotLeft, botBotRight := mustVSplit(botBot, 50)
fakewidget.MustDraw(ft, testcanvas.MustNew(topTopLeft), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topTopRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topBotLeft), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topBotRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botTopLeft), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botTopRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botBotLeft), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botBotRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topTopLeft), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topTopRight), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topBotLeft), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topBotRight), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botTopLeft), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botTopRight), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botBotLeft), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botBotRight), &widgetapi.Meta{}, widgetapi.Options{})
return ft
},
},
@ -643,10 +643,10 @@ func TestBuilder(t *testing.T) {
left, right := mustVSplit(bot, 20)
topRight, botRight := mustHSplit(right, 25)
fakewidget.MustDraw(ft, testcanvas.MustNew(top), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(left), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(top), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(left), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topRight), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botRight), &widgetapi.Meta{}, widgetapi.Options{})
return ft
},
},
@ -670,15 +670,15 @@ func TestBuilder(t *testing.T) {
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
top, bot := mustHSplit(ft.Area(), 50)
fakewidget.MustDraw(ft, testcanvas.MustNew(bot), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(bot), &widgetapi.Meta{}, widgetapi.Options{})
topTop, topBot := mustHSplit(top, 20)
left, right := mustVSplit(topBot, 20)
topRight, botRight := mustHSplit(right, 25)
fakewidget.MustDraw(ft, testcanvas.MustNew(topTop), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(left), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botRight), widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topTop), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(left), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(topRight), &widgetapi.Meta{}, widgetapi.Options{})
fakewidget.MustDraw(ft, testcanvas.MustNew(botRight), &widgetapi.Meta{}, widgetapi.Options{})
return ft
},
},
@ -705,7 +705,7 @@ func TestBuilder(t *testing.T) {
draw.BorderCellOpts(cell.FgColor(cell.ColorYellow)),
)
wCvs := testcanvas.MustNew(area.ExcludeBorder(cvs.Area()))
fakewidget.MustDraw(ft, wCvs, widgetapi.Options{})
fakewidget.MustDraw(ft, wCvs, &widgetapi.Meta{Focused: true}, widgetapi.Options{})
testcanvas.MustCopyTo(wCvs, cvs)
testcanvas.MustApply(cvs, ft)
return ft

View File

@ -48,7 +48,7 @@ won't change the data between calls to **Options** and **Draw**.
A widget can draw a character indicating that a resize is needed in such cases:
```go
func (w *Widget) Draw(cvs *canvas.Canvas) error {
func (w *Widget) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
min := w.minSize() // Output depends on the current state.
needAr, err := area.FromSize(min)
if err != nil {
@ -84,12 +84,15 @@ func TestWidget(t *testing.T) {
tests := []struct {
desc string
canvas image.Rectangle
meta *widgetapi.Meta
opts []Option
want func(size image.Point) *faketerm.Terminal
wantErr bool
}{
{
desc: "a test case",
// The metadata widget receives when drawn.
meta: &widgetapi.Meta{},
// canvas determines the size of the allocated canvas in the test case.
canvas: image.Rect(0,0,10,10),
// want creates the expected content on the fake terminal.
@ -112,7 +115,7 @@ func TestWidget(t *testing.T) {
}
widget := New()
err = widget.Draw(c)
err = widget.Draw(c, tc.meta)
if (err != nil) != tc.wantErr {
t.Errorf("Draw => unexpected error: %v, wantErr: %v", err, tc.wantErr)
}

View File

@ -31,12 +31,13 @@ import (
)
// outputLines are the number of lines written by this plugin.
const outputLines = 3
const outputLines = 4
const (
sizeLine = iota
keyboardLine
mouseLine
focusLine
)
// MinimumSize is the minimum size required to draw this widget.
@ -47,16 +48,17 @@ var MinimumSize = image.Point{24, 5}
// canvas. It writes the last received keyboard event onto the second line. It
// writes the last received mouse event onto the third line. If a non-empty
// string is provided via the Text() method, that text will be written right
// after the canvas size on the first line.
// after the canvas size on the first line. If the widget's container is
// focused it writes "focus" onto the fourth line.
//
// The widget requests the same options that are provided to the constructor.
// If the options or canvas size don't allow for the three lines mentioned
// above, the widget skips the ones it has no space for.
// If the options or canvas size don't allow for the lines mentioned above, the
// widget skips the ones it has no space for.
//
// This is thread-safe and must not be copied.
// Implements widgetapi.Widget.
type Mirror struct {
// lines are the three lines that will be drawn on the canvas.
// lines are the lines that will be drawn on the canvas.
lines []string
// text is the text provided by the last call to Text().
@ -83,9 +85,13 @@ func New(opts widgetapi.Options) *Mirror {
// 2x2 border on it, or of any of the text lines end up being longer than the
// width of the canvas.
// Draw implements widgetapi.Widget.Draw.
func (mi *Mirror) Draw(cvs *canvas.Canvas) error {
func (mi *Mirror) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
mi.mu.Lock()
defer mi.mu.Unlock()
if meta.Focused {
mi.lines[focusLine] = "focus"
}
if err := cvs.Clear(); err != nil {
return err
}
@ -156,20 +162,20 @@ func (mi *Mirror) Options() widgetapi.Options {
// Draw draws the content that would be expected after placing the Mirror
// widget onto the provided canvas and forwarding the given events.
func Draw(t terminalapi.Terminal, cvs *canvas.Canvas, opts widgetapi.Options, events ...terminalapi.Event) error {
func Draw(t terminalapi.Terminal, cvs *canvas.Canvas, meta *widgetapi.Meta, opts widgetapi.Options, events ...terminalapi.Event) error {
mirror := New(opts)
return DrawWithMirror(mirror, t, cvs, events...)
return DrawWithMirror(mirror, t, cvs, meta, events...)
}
// MustDraw is like Draw, but panics on all errors.
func MustDraw(t terminalapi.Terminal, cvs *canvas.Canvas, opts widgetapi.Options, events ...terminalapi.Event) {
if err := Draw(t, cvs, opts, events...); err != nil {
func MustDraw(t terminalapi.Terminal, cvs *canvas.Canvas, meta *widgetapi.Meta, opts widgetapi.Options, events ...terminalapi.Event) {
if err := Draw(t, cvs, meta, opts, events...); err != nil {
panic(fmt.Sprintf("Draw => %v", err))
}
}
// DrawWithMirror is like Draw, but uses the provided Mirror instead of creating one.
func DrawWithMirror(mirror *Mirror, t terminalapi.Terminal, cvs *canvas.Canvas, events ...terminalapi.Event) error {
func DrawWithMirror(mirror *Mirror, t terminalapi.Terminal, cvs *canvas.Canvas, meta *widgetapi.Meta, events ...terminalapi.Event) error {
for _, ev := range events {
switch e := ev.(type) {
case *terminalapi.Mouse:
@ -191,15 +197,15 @@ func DrawWithMirror(mirror *Mirror, t terminalapi.Terminal, cvs *canvas.Canvas,
}
}
if err := mirror.Draw(cvs); err != nil {
if err := mirror.Draw(cvs, meta); err != nil {
return err
}
return cvs.Apply(t)
}
// MustDrawWithMirror is like DrawWithMirror, but panics on all errors.
func MustDrawWithMirror(mirror *Mirror, t terminalapi.Terminal, cvs *canvas.Canvas, events ...terminalapi.Event) {
if err := DrawWithMirror(mirror, t, cvs, events...); err != nil {
func MustDrawWithMirror(mirror *Mirror, t terminalapi.Terminal, cvs *canvas.Canvas, meta *widgetapi.Meta, events ...terminalapi.Event) {
if err := DrawWithMirror(mirror, t, cvs, meta, events...); err != nil {
panic(fmt.Sprintf("DrawWithMirror => %v", err))
}
}

View File

@ -48,12 +48,14 @@ func TestMirror(t *testing.T) {
mouseEvents []mouseEvents // Mouse events to send before calling Draw().
apiEvents func(*Mirror) // External events via the widget's API.
cvs *canvas.Canvas
meta *widgetapi.Meta
want func(size image.Point) *faketerm.Terminal
wantErr bool
}{
{
desc: "canvas too small to draw a box",
cvs: testcanvas.MustNew(image.Rect(0, 0, 1, 1)),
meta: &widgetapi.Meta{},
want: func(size image.Point) *faketerm.Terminal {
return faketerm.MustNew(size)
},
@ -62,6 +64,7 @@ func TestMirror(t *testing.T) {
{
desc: "the canvas size text doesn't fit onto the line",
cvs: testcanvas.MustNew(image.Rect(0, 0, 3, 3)),
meta: &widgetapi.Meta{},
want: func(size image.Point) *faketerm.Terminal {
return faketerm.MustNew(size)
},
@ -70,6 +73,7 @@ func TestMirror(t *testing.T) {
{
desc: "draws the box and canvas size",
cvs: testcanvas.MustNew(image.Rect(0, 0, 7, 3)),
meta: &widgetapi.Meta{},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -79,12 +83,29 @@ func TestMirror(t *testing.T) {
return ft
},
},
{
desc: "indicates that it is focused",
cvs: testcanvas.MustNew(image.Rect(0, 0, 7, 6)),
meta: &widgetapi.Meta{
Focused: true,
},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
testdraw.MustBorder(cvs, cvs.Area())
testdraw.MustText(cvs, "(7,6)", image.Point{1, 1})
testdraw.MustText(cvs, "focus", image.Point{1, 4})
testcanvas.MustApply(cvs, ft)
return ft
},
},
{
desc: "draws the box, canvas size and custom text",
apiEvents: func(mi *Mirror) {
mi.Text("hi")
},
cvs: testcanvas.MustNew(image.Rect(0, 0, 9, 3)),
cvs: testcanvas.MustNew(image.Rect(0, 0, 9, 3)),
meta: &widgetapi.Meta{},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -97,6 +118,7 @@ func TestMirror(t *testing.T) {
{
desc: "skips canvas size if there isn't a line for it",
cvs: testcanvas.MustNew(image.Rect(0, 0, 3, 2)),
meta: &widgetapi.Meta{},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -115,7 +137,8 @@ func TestMirror(t *testing.T) {
k: &terminalapi.Keyboard{Key: keyboard.KeyEnd},
},
},
cvs: testcanvas.MustNew(image.Rect(0, 0, 8, 4)),
cvs: testcanvas.MustNew(image.Rect(0, 0, 8, 4)),
meta: &widgetapi.Meta{},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -133,7 +156,8 @@ func TestMirror(t *testing.T) {
k: &terminalapi.Keyboard{Key: keyboard.KeyEnd},
},
},
cvs: testcanvas.MustNew(image.Rect(0, 0, 8, 3)),
cvs: testcanvas.MustNew(image.Rect(0, 0, 8, 3)),
meta: &widgetapi.Meta{},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -155,7 +179,8 @@ func TestMirror(t *testing.T) {
Button: mouse.ButtonMiddle},
},
},
cvs: testcanvas.MustNew(image.Rect(0, 0, 19, 5)),
cvs: testcanvas.MustNew(image.Rect(0, 0, 19, 5)),
meta: &widgetapi.Meta{},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -173,7 +198,8 @@ func TestMirror(t *testing.T) {
m: &terminalapi.Mouse{Button: mouse.ButtonLeft},
},
},
cvs: testcanvas.MustNew(image.Rect(0, 0, 13, 4)),
cvs: testcanvas.MustNew(image.Rect(0, 0, 13, 4)),
meta: &widgetapi.Meta{},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -195,7 +221,8 @@ func TestMirror(t *testing.T) {
m: &terminalapi.Mouse{Button: mouse.ButtonLeft},
},
},
cvs: testcanvas.MustNew(image.Rect(0, 0, 17, 5)),
cvs: testcanvas.MustNew(image.Rect(0, 0, 17, 5)),
meta: &widgetapi.Meta{},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -227,7 +254,8 @@ func TestMirror(t *testing.T) {
wantErr: true,
},
},
cvs: testcanvas.MustNew(image.Rect(0, 0, 12, 5)),
cvs: testcanvas.MustNew(image.Rect(0, 0, 12, 5)),
meta: &widgetapi.Meta{},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -261,7 +289,7 @@ func TestMirror(t *testing.T) {
}
}
err := w.Draw(tc.cvs)
err := w.Draw(tc.cvs, tc.meta)
if (err != nil) != tc.wantErr {
t.Errorf("Draw => unexpected error: %v, wantErr: %v", err, tc.wantErr)
}
@ -296,6 +324,7 @@ func TestDraw(t *testing.T) {
desc string
opts widgetapi.Options
cvs *canvas.Canvas
meta *widgetapi.Meta
events []terminalapi.Event
want func(size image.Point) *faketerm.Terminal
wantErr bool
@ -303,6 +332,7 @@ func TestDraw(t *testing.T) {
{
desc: "canvas too small to draw a box",
cvs: testcanvas.MustNew(image.Rect(0, 0, 1, 1)),
meta: &widgetapi.Meta{},
want: func(size image.Point) *faketerm.Terminal {
return faketerm.MustNew(size)
},
@ -311,6 +341,7 @@ func TestDraw(t *testing.T) {
{
desc: "draws the box and canvas size",
cvs: testcanvas.MustNew(image.Rect(0, 0, 9, 3)),
meta: &widgetapi.Meta{},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -326,7 +357,8 @@ func TestDraw(t *testing.T) {
WantKeyboard: widgetapi.KeyScopeFocused,
WantMouse: widgetapi.MouseScopeWidget,
},
cvs: testcanvas.MustNew(image.Rect(0, 0, 17, 5)),
cvs: testcanvas.MustNew(image.Rect(0, 0, 17, 5)),
meta: &widgetapi.Meta{},
events: []terminalapi.Event{
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
&terminalapi.Mouse{Button: mouse.ButtonLeft},
@ -347,7 +379,7 @@ func TestDraw(t *testing.T) {
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
got := faketerm.MustNew(tc.cvs.Size())
err := Draw(got, tc.cvs, tc.opts, tc.events...)
err := Draw(got, tc.cvs, tc.meta, tc.opts, tc.events...)
if (err != nil) != tc.wantErr {
t.Errorf("Draw => got error:%v, wantErr: %v", err, tc.wantErr)
}

View File

@ -211,6 +211,7 @@ func TestRun(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
return ft
@ -248,6 +249,7 @@ func TestRun(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{
WantMouse: widgetapi.MouseScopeWidget,
},
@ -274,6 +276,7 @@ func TestRun(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{
WantKeyboard: widgetapi.KeyScopeFocused,
WantMouse: widgetapi.MouseScopeWidget,
@ -308,6 +311,7 @@ func TestRun(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
return ft
@ -339,6 +343,7 @@ func TestRun(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{
WantKeyboard: widgetapi.KeyScopeFocused,
},
@ -373,6 +378,7 @@ func TestRun(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{
WantMouse: widgetapi.MouseScopeWidget,
},
@ -480,6 +486,7 @@ func TestController(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{
WantKeyboard: widgetapi.KeyScopeFocused,
WantMouse: widgetapi.MouseScopeWidget,
@ -508,6 +515,7 @@ func TestController(t *testing.T) {
mirror,
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
)
return ft
},
@ -530,6 +538,7 @@ func TestController(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
return ft
@ -550,6 +559,7 @@ func TestController(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
return ft
@ -579,6 +589,7 @@ func TestController(t *testing.T) {
fakewidget.MustDraw(
ft,
testcanvas.MustNew(ft.Area()),
&widgetapi.Meta{Focused: true},
widgetapi.Options{},
)
return ft

View File

@ -139,6 +139,12 @@ type Options struct {
WantMouse MouseScope
}
// Meta provide additional metadata to widgets.
type Meta struct {
// Focused asserts whether the widget's container is focused.
Focused bool
}
// Widget is a single widget on the dashboard.
// Implementations must be thread safe.
type Widget interface {
@ -149,7 +155,9 @@ type Widget interface {
//
// The widget must not assume that the size of the canvas or its content
// remains the same between calls.
Draw(cvs *canvas.Canvas) error
//
// The argument meta is guaranteed to be valid (i.e. non-nil).
Draw(cvs *canvas.Canvas, meta *Meta) error
// Keyboard is called when the widget is focused on the dashboard and a key
// shortcut the widget registered for was pressed. Only called if the widget

View File

@ -73,7 +73,7 @@ func New(opts ...Option) (*BarChart, error) {
// Draw draws the BarChart widget onto the canvas.
// Implements widgetapi.Widget.Draw.
func (bc *BarChart) Draw(cvs *canvas.Canvas) error {
func (bc *BarChart) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
bc.mu.Lock()
defer bc.mu.Unlock()

View File

@ -34,6 +34,7 @@ func TestBarChart(t *testing.T) {
opts []Option
update func(*BarChart) error // update gets called before drawing of the widget.
canvas image.Rectangle
meta *widgetapi.Meta
want func(size image.Point) *faketerm.Terminal
wantCapacity int
wantErr bool
@ -660,7 +661,7 @@ func TestBarChart(t *testing.T) {
return
}
err = bc.Draw(c)
err = bc.Draw(c, tc.meta)
if (err != nil) != tc.wantDrawErr {
t.Errorf("Draw => unexpected error: %v, wantDrawErr: %v", err, tc.wantDrawErr)
}

View File

@ -112,7 +112,7 @@ var (
// Draw draws the Button widget onto the canvas.
// Implements widgetapi.Widget.Draw.
func (b *Button) Draw(cvs *canvas.Canvas) error {
func (b *Button) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
b.mu.Lock()
defer b.mu.Unlock()

View File

@ -71,6 +71,7 @@ func TestButton(t *testing.T) {
opts []Option
events []terminalapi.Event
canvas image.Rectangle
meta *widgetapi.Meta
// timeSince is used to replace time.Since for tests, leave nil to use
// the original.
@ -635,7 +636,7 @@ func TestButton(t *testing.T) {
if err != nil {
t.Fatalf("canvas.New => unexpected error: %v", err)
}
err = b.Draw(c)
err = b.Draw(c, tc.meta)
if (err != nil) != tc.wantDrawErr {
t.Errorf("Draw => unexpected error: %v, wantDrawErr: %v", err, tc.wantDrawErr)
}
@ -688,7 +689,7 @@ func TestButton(t *testing.T) {
t.Fatalf("canvas.New => unexpected error: %v", err)
}
err = b.Draw(c)
err = b.Draw(c, tc.meta)
if (err != nil) != tc.wantDrawErr {
t.Errorf("Draw => unexpected error: %v, wantDrawErr: %v", err, tc.wantDrawErr)
}

View File

@ -186,7 +186,7 @@ func (d *Donut) drawText(cvs *canvas.Canvas, mid image.Point, holeR int) error {
// Draw draws the Donut widget onto the canvas.
// Implements widgetapi.Widget.Draw.
func (d *Donut) Draw(cvs *canvas.Canvas) error {
func (d *Donut) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
d.mu.Lock()
defer d.mu.Unlock()

View File

@ -36,6 +36,7 @@ func TestDonut(t *testing.T) {
opts []Option
update func(*Donut) error // update gets called before drawing of the widget.
canvas image.Rectangle
meta *widgetapi.Meta
want func(size image.Point) *faketerm.Terminal
wantNewErr bool
wantUpdateErr bool // whether to expect an error on a call to the update function
@ -604,7 +605,7 @@ func TestDonut(t *testing.T) {
}
}
err = d.Draw(c)
err = d.Draw(c, tc.meta)
if (err != nil) != tc.wantDrawErr {
t.Errorf("Draw => unexpected error: %v, wantDrawErr: %v", err, tc.wantDrawErr)
}

View File

@ -250,7 +250,7 @@ func (g *Gauge) drawText(cvs *canvas.Canvas, progress image.Rectangle) error {
// Draw draws the Gauge widget onto the canvas.
// Implements widgetapi.Widget.Draw.
func (g *Gauge) Draw(cvs *canvas.Canvas) error {
func (g *Gauge) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
g.mu.Lock()
defer g.mu.Unlock()

View File

@ -50,6 +50,7 @@ func TestGauge(t *testing.T) {
percent *percentCall // if set, the test case calls Gauge.Percent().
absolute *absoluteCall // if set the test case calls Gauge.Absolute().
canvas image.Rectangle
meta *widgetapi.Meta
want func(size image.Point) *faketerm.Terminal
wantErr bool
wantUpdateErr bool // whether to expect an error on a call to Gauge.Percent() or Gauge.Absolute().
@ -779,7 +780,7 @@ func TestGauge(t *testing.T) {
}
err = g.Draw(c)
err = g.Draw(c, tc.meta)
if (err != nil) != tc.wantDrawErr {
t.Errorf("Draw => unexpected error: %v, wantDrawErr: %v", err, tc.wantDrawErr)
}

View File

@ -292,7 +292,7 @@ func (lc *LineChart) axesDetails(cvs *canvas.Canvas) (*axes.XDetails, *axes.YDet
// Draw draws the values as line charts.
// Implements widgetapi.Widget.Draw.
func (lc *LineChart) Draw(cvs *canvas.Canvas) error {
func (lc *LineChart) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
lc.mu.Lock()
defer lc.mu.Unlock()

View File

@ -36,6 +36,7 @@ func TestLineChartDraws(t *testing.T) {
tests := []struct {
desc string
canvas image.Rectangle
meta *widgetapi.Meta
opts []Option
writes func(*LineChart) error
want func(size image.Point) *faketerm.Terminal
@ -1163,7 +1164,7 @@ func TestLineChartDraws(t *testing.T) {
}
// Draw once so zoom tracker is initialized.
cvs := testcanvas.MustNew(image.Rect(0, 0, 20, 10))
if err := lc.Draw(cvs); err != nil {
if err := lc.Draw(cvs, &widgetapi.Meta{}); err != nil {
return err
}
return lc.Mouse(&terminalapi.Mouse{
@ -1214,7 +1215,7 @@ func TestLineChartDraws(t *testing.T) {
}
// Draw once so zoom tracker is initialized.
cvs := testcanvas.MustNew(image.Rect(0, 0, 20, 10))
if err := lc.Draw(cvs); err != nil {
if err := lc.Draw(cvs, &widgetapi.Meta{}); err != nil {
return err
}
return lc.Mouse(&terminalapi.Mouse{
@ -1265,7 +1266,7 @@ func TestLineChartDraws(t *testing.T) {
}
// Draw once so zoom tracker is initialized.
cvs := testcanvas.MustNew(image.Rect(0, 0, 20, 10))
if err := lc.Draw(cvs); err != nil {
if err := lc.Draw(cvs, &widgetapi.Meta{}); err != nil {
return err
}
return lc.Mouse(&terminalapi.Mouse{
@ -1316,13 +1317,13 @@ func TestLineChartDraws(t *testing.T) {
// Draw twice with different canvas size to simulate resize.
{
cvs := testcanvas.MustNew(image.Rect(0, 0, 20, 7))
if err := lc.Draw(cvs); err != nil {
if err := lc.Draw(cvs, &widgetapi.Meta{}); err != nil {
return err
}
}
{
cvs := testcanvas.MustNew(image.Rect(0, 0, 20, 10))
if err := lc.Draw(cvs); err != nil {
if err := lc.Draw(cvs, &widgetapi.Meta{}); err != nil {
return err
}
}
@ -1380,7 +1381,7 @@ func TestLineChartDraws(t *testing.T) {
// Draw once so zoom tracker is initialized.
cvs := testcanvas.MustNew(image.Rect(0, 0, 11, 10))
if err := lc.Draw(cvs); err != nil {
if err := lc.Draw(cvs, &widgetapi.Meta{}); err != nil {
return err
}
return lc.Mouse(&terminalapi.Mouse{
@ -1435,7 +1436,7 @@ func TestLineChartDraws(t *testing.T) {
// Draw once so zoom tracker is initialized.
cvs := testcanvas.MustNew(image.Rect(0, 0, 11, 10))
if err := lc.Draw(cvs); err != nil {
if err := lc.Draw(cvs, &widgetapi.Meta{}); err != nil {
return err
}
if err := lc.Mouse(&terminalapi.Mouse{
@ -1554,7 +1555,7 @@ func TestLineChartDraws(t *testing.T) {
}
{
err := widget.Draw(c)
err := widget.Draw(c, tc.meta)
if (err != nil) != tc.wantDrawErr {
t.Fatalf("Draw => unexpected error: %v, wantDrawErr: %v", err, tc.wantDrawErr)
}

View File

@ -174,7 +174,7 @@ func (sd *SegmentDisplay) preprocess(cvsAr image.Rectangle) (*segArea, error) {
// Draw draws the SegmentDisplay widget onto the canvas.
// Implements widgetapi.Widget.Draw.
func (sd *SegmentDisplay) Draw(cvs *canvas.Canvas) error {
func (sd *SegmentDisplay) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
sd.mu.Lock()
defer sd.mu.Unlock()

View File

@ -45,6 +45,7 @@ func TestSegmentDisplay(t *testing.T) {
opts []Option
update func(*SegmentDisplay) error // update gets called before drawing of the widget.
canvas image.Rectangle
meta *widgetapi.Meta
want func(size image.Point) *faketerm.Terminal
wantNewErr bool
wantUpdateErr bool // whether to expect an error on a call to the update function
@ -796,7 +797,7 @@ func TestSegmentDisplay(t *testing.T) {
}
}
err = sd.Draw(c)
err = sd.Draw(c, tc.meta)
if (err != nil) != tc.wantDrawErr {
t.Errorf("Draw => unexpected error: %v, wantDrawErr: %v", err, tc.wantDrawErr)
}

View File

@ -66,7 +66,7 @@ func New(opts ...Option) (*SparkLine, error) {
// Draw draws the SparkLine widget onto the canvas.
// Implements widgetapi.Widget.Draw.
func (sl *SparkLine) Draw(cvs *canvas.Canvas) error {
func (sl *SparkLine) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
sl.mu.Lock()
defer sl.mu.Unlock()

View File

@ -34,6 +34,7 @@ func TestSparkLine(t *testing.T) {
opts []Option
update func(*SparkLine) error // update gets called before drawing of the widget.
canvas image.Rectangle
meta *widgetapi.Meta
want func(size image.Point) *faketerm.Terminal
wantCapacity int
wantErr bool
@ -475,7 +476,7 @@ func TestSparkLine(t *testing.T) {
return
}
err = sp.Draw(c)
err = sp.Draw(c, tc.meta)
if (err != nil) != tc.wantDrawErr {
t.Errorf("Draw => unexpected error: %v, wantDrawErr: %v", err, tc.wantDrawErr)
}

View File

@ -206,7 +206,7 @@ func (t *Text) draw(cvs *canvas.Canvas) error {
// Draw draws the text onto the canvas.
// Implements widgetapi.Widget.Draw.
func (t *Text) Draw(cvs *canvas.Canvas) error {
func (t *Text) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
t.mu.Lock()
defer t.mu.Unlock()

View File

@ -35,6 +35,7 @@ func TestTextDraws(t *testing.T) {
tests := []struct {
desc string
canvas image.Rectangle
meta *widgetapi.Meta
opts []Option
writes func(*Text) error
events func(*Text)
@ -601,7 +602,7 @@ func TestTextDraws(t *testing.T) {
},
events: func(widget *Text) {
// Draw once to roll the content all the way down before we scroll.
if err := widget.Draw(testcanvas.MustNew(image.Rect(0, 0, 10, 3))); err != nil {
if err := widget.Draw(testcanvas.MustNew(image.Rect(0, 0, 10, 3)), &widgetapi.Meta{}); err != nil {
panic(err)
}
widget.Mouse(&terminalapi.Mouse{
@ -630,7 +631,7 @@ func TestTextDraws(t *testing.T) {
},
events: func(widget *Text) {
// Draw once to roll the content all the way down before we scroll.
if err := widget.Draw(testcanvas.MustNew(image.Rect(0, 0, 10, 3))); err != nil {
if err := widget.Draw(testcanvas.MustNew(image.Rect(0, 0, 10, 3)), &widgetapi.Meta{}); err != nil {
panic(err)
}
widget.Keyboard(&terminalapi.Keyboard{
@ -659,7 +660,7 @@ func TestTextDraws(t *testing.T) {
},
events: func(widget *Text) {
// Draw once to roll the content all the way down before we scroll.
if err := widget.Draw(testcanvas.MustNew(image.Rect(0, 0, 10, 3))); err != nil {
if err := widget.Draw(testcanvas.MustNew(image.Rect(0, 0, 10, 3)), &widgetapi.Meta{}); err != nil {
panic(err)
}
widget.Keyboard(&terminalapi.Keyboard{
@ -689,7 +690,7 @@ func TestTextDraws(t *testing.T) {
},
events: func(widget *Text) {
// Draw once to roll the content all the way down before we scroll.
if err := widget.Draw(testcanvas.MustNew(image.Rect(0, 0, 10, 3))); err != nil {
if err := widget.Draw(testcanvas.MustNew(image.Rect(0, 0, 10, 3)), &widgetapi.Meta{}); err != nil {
panic(err)
}
widget.Mouse(&terminalapi.Mouse{
@ -719,7 +720,7 @@ func TestTextDraws(t *testing.T) {
},
events: func(widget *Text) {
// Draw once to roll the content all the way down before we scroll.
if err := widget.Draw(testcanvas.MustNew(image.Rect(0, 0, 10, 3))); err != nil {
if err := widget.Draw(testcanvas.MustNew(image.Rect(0, 0, 10, 3)), &widgetapi.Meta{}); err != nil {
panic(err)
}
widget.Keyboard(&terminalapi.Keyboard{
@ -749,7 +750,7 @@ func TestTextDraws(t *testing.T) {
},
events: func(widget *Text) {
// Draw once to roll the content all the way down before we scroll.
if err := widget.Draw(testcanvas.MustNew(image.Rect(0, 0, 10, 3))); err != nil {
if err := widget.Draw(testcanvas.MustNew(image.Rect(0, 0, 10, 3)), &widgetapi.Meta{}); err != nil {
panic(err)
}
widget.Keyboard(&terminalapi.Keyboard{
@ -798,7 +799,7 @@ func TestTextDraws(t *testing.T) {
tc.events(widget)
}
if err := widget.Draw(c); err != nil {
if err := widget.Draw(c, tc.meta); err != nil {
t.Fatalf("Draw => unexpected error: %v", err)
}