1
0
mirror of https://github.com/mum4k/termdash.git synced 2025-04-25 13:48:50 +08:00

Button now supports separate color values for text chunks.

Custom color values can be specified for each of its main states.
This commit is contained in:
Jakub Sobon 2020-11-27 15:11:59 -05:00
parent cf9554ae9f
commit 31c7a24041
No known key found for this signature in database
GPG Key ID: F2451A77FB05D3B7
6 changed files with 532 additions and 19 deletions

View File

@ -12,12 +12,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `container` now allows users to configure keyboard keys that move focus to
the next or the previous container.
- the `button` widget allows users to specify multiple trigger keys.
- the `button` widget can now be drawn without the shadow and the pressing the
- the `button` widget can now be drawn without the shadow and the press
animation.
- the `button` widget can now be drawn without horizontal padding around its
text.
- the `button`widget now allows specifying cell options for each cell of the
displayed text.
displayed text. Separate cell options can be specified for each of button's
main states (up, focused and up, down).
- the `button` widget allows specifying separate fill color values for each of
its main states (up, focused and up, down).
- added a new demo called `formdemo` under the `button` widget that
demonstrates the ability of `termdash` to display forms with editable content
and keyboard navigation.
## [0.13.0] - 17-Nov-2020

View File

@ -196,7 +196,17 @@ func (b *Button) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
buttonAr = shadowAr
}
if err := cvs.SetAreaCells(buttonAr, buttonRune, cell.BgColor(b.opts.fillColor)); err != nil {
var fillColor cell.Color
switch {
case b.state == button.Down && b.opts.pressedFillColor != nil:
fillColor = *b.opts.pressedFillColor
case meta.Focused && b.opts.focusedFillColor != nil:
fillColor = *b.opts.focusedFillColor
default:
fillColor = b.opts.fillColor
}
if err := cvs.SetAreaCells(buttonAr, buttonRune, cell.BgColor(fillColor)); err != nil {
return err
}
@ -229,7 +239,16 @@ func (b *Button) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
}
tOpts := b.givenTOpts[optRange.AttrIdx]
cells, err := cvs.SetCell(cur, r, tOpts.cellOpts...)
var cellOpts []cell.Option
switch {
case b.state == button.Down && len(tOpts.pressedCellOpts) > 0:
cellOpts = tOpts.pressedCellOpts
case meta.Focused && len(tOpts.focusedCellOpts) > 0:
cellOpts = tOpts.focusedCellOpts
default:
cellOpts = tOpts.cellOpts
}
cells, err := cvs.SetCell(cur, r, cellOpts...)
if err != nil {
return err
}

View File

@ -65,8 +65,12 @@ func (ct *callbackTracker) callback() error {
func TestButton(t *testing.T) {
tests := []struct {
desc string
text string
desc string
// Only one of these must be specified.
text string // Calls New() as the constructor.
textChunks []*TextChunk // Calls NewFromChunks() as the constructor.
callback *callbackTracker
opts []Option
events []terminalapi.Event
@ -86,6 +90,7 @@ func TestButton(t *testing.T) {
{
desc: "New fails with nil callback",
canvas: image.Rect(0, 0, 1, 1),
meta: &widgetapi.Meta{Focused: false},
wantNewErr: true,
},
{
@ -95,6 +100,7 @@ func TestButton(t *testing.T) {
KeyUpDelay(-1 * time.Second),
},
canvas: image.Rect(0, 0, 1, 1),
meta: &widgetapi.Meta{Focused: false},
wantNewErr: true,
},
{
@ -104,6 +110,7 @@ func TestButton(t *testing.T) {
Height(0),
},
canvas: image.Rect(0, 0, 1, 1),
meta: &widgetapi.Meta{Focused: false},
wantNewErr: true,
},
{
@ -113,6 +120,7 @@ func TestButton(t *testing.T) {
Width(0),
},
canvas: image.Rect(0, 0, 1, 1),
meta: &widgetapi.Meta{Focused: false},
wantNewErr: true,
},
{
@ -122,6 +130,79 @@ func TestButton(t *testing.T) {
TextHorizontalPadding(-1),
},
canvas: image.Rect(0, 0, 1, 1),
meta: &widgetapi.Meta{Focused: false},
wantNewErr: true,
},
{
desc: "NewFromChunks fails with nil callback",
textChunks: []*TextChunk{
NewChunk("text"),
},
canvas: image.Rect(0, 0, 1, 1),
meta: &widgetapi.Meta{Focused: false},
wantNewErr: true,
},
{
desc: "NewFromChunks fails with negative keyUpDelay",
textChunks: []*TextChunk{
NewChunk("text"),
},
callback: &callbackTracker{},
opts: []Option{
KeyUpDelay(-1 * time.Second),
},
canvas: image.Rect(0, 0, 1, 1),
meta: &widgetapi.Meta{Focused: false},
wantNewErr: true,
},
{
desc: "NewFromChunks fails with zero Height",
textChunks: []*TextChunk{
NewChunk("text"),
},
callback: &callbackTracker{},
opts: []Option{
Height(0),
},
canvas: image.Rect(0, 0, 1, 1),
meta: &widgetapi.Meta{Focused: false},
wantNewErr: true,
},
{
desc: "NewFromChunks fails with zero Width",
textChunks: []*TextChunk{
NewChunk("text"),
},
callback: &callbackTracker{},
opts: []Option{
Width(0),
},
canvas: image.Rect(0, 0, 1, 1),
meta: &widgetapi.Meta{Focused: false},
wantNewErr: true,
},
{
desc: "NewFromChunks fails with negative textHorizontalPadding",
textChunks: []*TextChunk{
NewChunk("text"),
},
callback: &callbackTracker{},
opts: []Option{
TextHorizontalPadding(-1),
},
canvas: image.Rect(0, 0, 1, 1),
meta: &widgetapi.Meta{Focused: false},
wantNewErr: true,
},
{
desc: "NewFromChunks fails with zero chunks",
textChunks: []*TextChunk{},
callback: &callbackTracker{},
opts: []Option{
TextHorizontalPadding(-1),
},
canvas: image.Rect(0, 0, 1, 1),
meta: &widgetapi.Meta{Focused: false},
wantNewErr: true,
},
{
@ -129,6 +210,7 @@ func TestButton(t *testing.T) {
callback: &callbackTracker{},
text: "hello",
canvas: image.Rect(0, 0, 1, 1),
meta: &widgetapi.Meta{Focused: false},
wantDrawErr: true,
},
{
@ -136,6 +218,7 @@ func TestButton(t *testing.T) {
callback: &callbackTracker{},
text: "hello",
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -166,6 +249,7 @@ func TestButton(t *testing.T) {
},
text: "hello",
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -190,6 +274,7 @@ func TestButton(t *testing.T) {
callback: &callbackTracker{},
text: "hello",
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft},
},
@ -220,6 +305,7 @@ func TestButton(t *testing.T) {
},
text: "hello",
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft},
},
@ -247,6 +333,7 @@ func TestButton(t *testing.T) {
callback: &callbackTracker{},
text: "hello",
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft},
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonRelease},
@ -284,6 +371,7 @@ func TestButton(t *testing.T) {
Key(keyboard.KeyEnter),
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
},
@ -317,6 +405,7 @@ func TestButton(t *testing.T) {
Keys(keyboard.KeyEnter, keyboard.KeyTab),
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Keyboard{Key: keyboard.KeyTab},
},
@ -350,6 +439,7 @@ func TestButton(t *testing.T) {
GlobalKey(keyboard.KeyTab),
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Keyboard{Key: keyboard.KeyTab},
},
@ -383,6 +473,7 @@ func TestButton(t *testing.T) {
GlobalKeys(keyboard.KeyEnter, keyboard.KeyTab),
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Keyboard{Key: keyboard.KeyTab},
},
@ -413,6 +504,7 @@ func TestButton(t *testing.T) {
callback: &callbackTracker{},
text: "hello",
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
},
@ -449,6 +541,7 @@ func TestButton(t *testing.T) {
return 200 * time.Millisecond
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
},
@ -486,6 +579,7 @@ func TestButton(t *testing.T) {
return 200 * time.Millisecond
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
},
@ -526,6 +620,7 @@ func TestButton(t *testing.T) {
return 200 * time.Millisecond
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
@ -561,6 +656,7 @@ func TestButton(t *testing.T) {
callback: &callbackTracker{},
text: "hello",
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft},
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonRelease},
@ -599,6 +695,7 @@ func TestButton(t *testing.T) {
},
text: "hello",
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft},
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonRelease},
@ -619,6 +716,7 @@ func TestButton(t *testing.T) {
return 200 * time.Millisecond
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
events: []terminalapi.Event{
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
},
@ -629,6 +727,7 @@ func TestButton(t *testing.T) {
callback: &callbackTracker{},
text: "hello",
canvas: image.Rect(0, 0, 8, 2),
meta: &widgetapi.Meta{Focused: false},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -656,6 +755,7 @@ func TestButton(t *testing.T) {
callback: &callbackTracker{},
text: "h",
canvas: image.Rect(0, 0, 4, 2),
meta: &widgetapi.Meta{Focused: false},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -686,6 +786,7 @@ func TestButton(t *testing.T) {
TextColor(cell.ColorRed),
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -716,6 +817,7 @@ func TestButton(t *testing.T) {
FillColor(cell.ColorRed),
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -746,6 +848,7 @@ func TestButton(t *testing.T) {
ShadowColor(cell.ColorRed),
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
@ -768,6 +871,303 @@ func TestButton(t *testing.T) {
},
wantCallback: &callbackTracker{},
},
{
desc: "draws button with text chunks and custom fill color in up state",
callback: &callbackTracker{},
opts: []Option{
FillColor(cell.ColorBlue),
FocusedFillColor(cell.ColorYellow),
PressedFillColor(cell.ColorRed),
DisableShadow(),
},
textChunks: []*TextChunk{
NewChunk(
"h",
TextCellOpts(cell.FgColor(cell.ColorBlack)),
FocusedTextCellOpts(cell.FgColor(cell.ColorWhite)),
PressedTextCellOpts(cell.FgColor(cell.ColorGreen)),
),
NewChunk(
"ello",
TextCellOpts(cell.FgColor(cell.ColorMagenta)),
FocusedTextCellOpts(cell.FgColor(cell.ColorMagenta)),
PressedTextCellOpts(cell.FgColor(cell.ColorMagenta)),
),
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
// Button.
testcanvas.MustSetAreaCells(cvs, image.Rect(0, 0, 8, 4), 'x', cell.BgColor(cell.ColorBlue))
// Text.
testdraw.MustText(cvs, "h", image.Point{1, 1},
draw.TextCellOpts(
cell.FgColor(cell.ColorBlack),
cell.BgColor(cell.ColorBlue)),
)
testdraw.MustText(cvs, "ello", image.Point{2, 1},
draw.TextCellOpts(
cell.FgColor(cell.ColorMagenta),
cell.BgColor(cell.ColorBlue)),
)
testcanvas.MustApply(cvs, ft)
return ft
},
wantCallback: &callbackTracker{},
},
{
desc: "draws button with text chunks and custom fill color in focused up state",
callback: &callbackTracker{},
opts: []Option{
FillColor(cell.ColorBlue),
FocusedFillColor(cell.ColorYellow),
PressedFillColor(cell.ColorRed),
DisableShadow(),
},
textChunks: []*TextChunk{
NewChunk(
"h",
TextCellOpts(cell.FgColor(cell.ColorBlack)),
FocusedTextCellOpts(cell.FgColor(cell.ColorWhite)),
PressedTextCellOpts(cell.FgColor(cell.ColorGreen)),
),
NewChunk(
"ello",
TextCellOpts(cell.FgColor(cell.ColorMagenta)),
FocusedTextCellOpts(cell.FgColor(cell.ColorMagenta)),
PressedTextCellOpts(cell.FgColor(cell.ColorMagenta)),
),
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: true},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
// Button.
testcanvas.MustSetAreaCells(cvs, image.Rect(0, 0, 8, 4), 'x', cell.BgColor(cell.ColorYellow))
// Text.
testdraw.MustText(cvs, "h", image.Point{1, 1},
draw.TextCellOpts(
cell.FgColor(cell.ColorWhite),
cell.BgColor(cell.ColorYellow)),
)
testdraw.MustText(cvs, "ello", image.Point{2, 1},
draw.TextCellOpts(
cell.FgColor(cell.ColorMagenta),
cell.BgColor(cell.ColorYellow)),
)
testcanvas.MustApply(cvs, ft)
return ft
},
wantCallback: &callbackTracker{},
},
{
desc: "draws button with text chunks in up state, focused colors default to regular colors",
callback: &callbackTracker{},
opts: []Option{
FillColor(cell.ColorBlue),
PressedFillColor(cell.ColorRed),
DisableShadow(),
},
textChunks: []*TextChunk{
NewChunk(
"h",
TextCellOpts(cell.FgColor(cell.ColorBlack)),
PressedTextCellOpts(cell.FgColor(cell.ColorGreen)),
),
NewChunk(
"ello",
TextCellOpts(cell.FgColor(cell.ColorMagenta)),
PressedTextCellOpts(cell.FgColor(cell.ColorMagenta)),
),
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: true},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
// Button.
testcanvas.MustSetAreaCells(cvs, image.Rect(0, 0, 8, 4), 'x', cell.BgColor(cell.ColorBlue))
// Text.
testdraw.MustText(cvs, "h", image.Point{1, 1},
draw.TextCellOpts(
cell.FgColor(cell.ColorBlack),
cell.BgColor(cell.ColorBlue)),
)
testdraw.MustText(cvs, "ello", image.Point{2, 1},
draw.TextCellOpts(
cell.FgColor(cell.ColorMagenta),
cell.BgColor(cell.ColorBlue)),
)
testcanvas.MustApply(cvs, ft)
return ft
},
wantCallback: &callbackTracker{},
},
{
desc: "draws button with text chunks and custom fill color in down state",
events: []terminalapi.Event{
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft},
},
callback: &callbackTracker{},
opts: []Option{
FillColor(cell.ColorBlue),
FocusedFillColor(cell.ColorYellow),
PressedFillColor(cell.ColorRed),
DisableShadow(),
},
textChunks: []*TextChunk{
NewChunk(
"h",
TextCellOpts(cell.FgColor(cell.ColorBlack)),
FocusedTextCellOpts(cell.FgColor(cell.ColorWhite)),
PressedTextCellOpts(cell.FgColor(cell.ColorGreen)),
),
NewChunk(
"ello",
TextCellOpts(cell.FgColor(cell.ColorMagenta)),
FocusedTextCellOpts(cell.FgColor(cell.ColorMagenta)),
PressedTextCellOpts(cell.FgColor(cell.ColorMagenta)),
),
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
// Button.
testcanvas.MustSetAreaCells(cvs, image.Rect(0, 0, 8, 4), 'x', cell.BgColor(cell.ColorRed))
// Text.
testdraw.MustText(cvs, "h", image.Point{1, 1},
draw.TextCellOpts(
cell.FgColor(cell.ColorGreen),
cell.BgColor(cell.ColorRed)),
)
testdraw.MustText(cvs, "ello", image.Point{2, 1},
draw.TextCellOpts(
cell.FgColor(cell.ColorMagenta),
cell.BgColor(cell.ColorRed)),
)
testcanvas.MustApply(cvs, ft)
return ft
},
wantCallback: &callbackTracker{},
},
{
desc: "draws button with text chunks and custom fill color in down focused state (focus has no impact)",
events: []terminalapi.Event{
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft},
},
callback: &callbackTracker{},
opts: []Option{
FillColor(cell.ColorBlue),
FocusedFillColor(cell.ColorYellow),
PressedFillColor(cell.ColorRed),
DisableShadow(),
},
textChunks: []*TextChunk{
NewChunk(
"h",
TextCellOpts(cell.FgColor(cell.ColorBlack)),
FocusedTextCellOpts(cell.FgColor(cell.ColorWhite)),
PressedTextCellOpts(cell.FgColor(cell.ColorGreen)),
),
NewChunk(
"ello",
TextCellOpts(cell.FgColor(cell.ColorMagenta)),
FocusedTextCellOpts(cell.FgColor(cell.ColorMagenta)),
PressedTextCellOpts(cell.FgColor(cell.ColorMagenta)),
),
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: true},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
// Button.
testcanvas.MustSetAreaCells(cvs, image.Rect(0, 0, 8, 4), 'x', cell.BgColor(cell.ColorRed))
// Text.
testdraw.MustText(cvs, "h", image.Point{1, 1},
draw.TextCellOpts(
cell.FgColor(cell.ColorGreen),
cell.BgColor(cell.ColorRed)),
)
testdraw.MustText(cvs, "ello", image.Point{2, 1},
draw.TextCellOpts(
cell.FgColor(cell.ColorMagenta),
cell.BgColor(cell.ColorRed)),
)
testcanvas.MustApply(cvs, ft)
return ft
},
wantCallback: &callbackTracker{},
},
{
desc: "draws button with text chunks in down satte, pressed colors default to regular colors",
events: []terminalapi.Event{
&terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft},
},
callback: &callbackTracker{},
opts: []Option{
FillColor(cell.ColorBlue),
FocusedFillColor(cell.ColorYellow),
DisableShadow(),
},
textChunks: []*TextChunk{
NewChunk(
"h",
TextCellOpts(cell.FgColor(cell.ColorBlack)),
FocusedTextCellOpts(cell.FgColor(cell.ColorWhite)),
),
NewChunk(
"ello",
TextCellOpts(cell.FgColor(cell.ColorMagenta)),
FocusedTextCellOpts(cell.FgColor(cell.ColorMagenta)),
),
},
canvas: image.Rect(0, 0, 8, 4),
meta: &widgetapi.Meta{Focused: false},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
// Button.
testcanvas.MustSetAreaCells(cvs, image.Rect(0, 0, 8, 4), 'x', cell.BgColor(cell.ColorBlue))
// Text.
testdraw.MustText(cvs, "h", image.Point{1, 1},
draw.TextCellOpts(
cell.FgColor(cell.ColorBlack),
cell.BgColor(cell.ColorBlue)),
)
testdraw.MustText(cvs, "ello", image.Point{2, 1},
draw.TextCellOpts(
cell.FgColor(cell.ColorMagenta),
cell.BgColor(cell.ColorBlue)),
)
testcanvas.MustApply(cvs, ft)
return ft
},
wantCallback: &callbackTracker{},
},
}
buttonRune = 'x'
@ -787,12 +1187,30 @@ func TestButton(t *testing.T) {
} else {
cFn = gotCallback.callback
}
b, err := New(tc.text, cFn, tc.opts...)
if (err != nil) != tc.wantNewErr {
t.Errorf("New => unexpected error: %v, wantNewErr: %v", err, tc.wantNewErr)
if tc.text != "" && tc.textChunks != nil {
t.Fatalf("cannot specify both text and textChunks in the testdata")
}
if err != nil {
return
var btn *Button
if tc.textChunks != nil {
b, err := NewFromChunks(tc.textChunks, cFn, tc.opts...)
if (err != nil) != tc.wantNewErr {
t.Errorf("NewFromChunks => unexpected error: %v, wantNewErr: %v", err, tc.wantNewErr)
}
if err != nil {
return
}
btn = b
} else {
b, err := New(tc.text, cFn, tc.opts...)
if (err != nil) != tc.wantNewErr {
t.Errorf("New => unexpected error: %v, wantNewErr: %v", err, tc.wantNewErr)
}
if err != nil {
return
}
btn = b
}
{
@ -801,7 +1219,7 @@ func TestButton(t *testing.T) {
if err != nil {
t.Fatalf("canvas.New => unexpected error: %v", err)
}
err = b.Draw(c, tc.meta)
err = btn.Draw(c, tc.meta)
if (err != nil) != tc.wantDrawErr {
t.Errorf("Draw => unexpected error: %v, wantDrawErr: %v", err, tc.wantDrawErr)
}
@ -813,7 +1231,7 @@ func TestButton(t *testing.T) {
for i, ev := range tc.events {
switch e := ev.(type) {
case *terminalapi.Mouse:
err := b.Mouse(e)
err := btn.Mouse(e)
// Only the last event in test cases is the one that triggers the callback.
if i == len(tc.events)-1 {
if (err != nil) != tc.wantCallbackErr {
@ -829,7 +1247,7 @@ func TestButton(t *testing.T) {
}
case *terminalapi.Keyboard:
err := b.Keyboard(e)
err := btn.Keyboard(e)
// Only the last event in test cases is the one that triggers the callback.
if i == len(tc.events)-1 {
if (err != nil) != tc.wantCallbackErr {
@ -854,7 +1272,7 @@ func TestButton(t *testing.T) {
t.Fatalf("canvas.New => unexpected error: %v", err)
}
err = b.Draw(c, tc.meta)
err = btn.Draw(c, tc.meta)
if (err != nil) != tc.wantDrawErr {
t.Errorf("Draw => unexpected error: %v, wantDrawErr: %v", err, tc.wantDrawErr)
}

View File

@ -31,6 +31,40 @@ import (
"github.com/mum4k/termdash/widgets/segmentdisplay"
)
// buttonChunks creates the text chunks for a button from the provided text.
func buttonChunks(text string) []*button.TextChunk {
if len(text) == 0 {
return nil
}
first := string(text[0])
rest := string(text[1:])
return []*button.TextChunk{
button.NewChunk(
"<",
button.TextCellOpts(cell.FgColor(cell.ColorWhite)),
button.FocusedTextCellOpts(cell.FgColor(cell.ColorBlack)),
button.PressedTextCellOpts(cell.FgColor(cell.ColorBlack)),
),
button.NewChunk(
first,
button.TextCellOpts(cell.FgColor(cell.ColorRed)),
),
button.NewChunk(
rest,
button.TextCellOpts(cell.FgColor(cell.ColorWhite)),
button.FocusedTextCellOpts(cell.FgColor(cell.ColorBlack)),
button.PressedTextCellOpts(cell.FgColor(cell.ColorBlack)),
),
button.NewChunk(
">",
button.TextCellOpts(cell.FgColor(cell.ColorWhite)),
button.FocusedTextCellOpts(cell.FgColor(cell.ColorBlack)),
button.PressedTextCellOpts(cell.FgColor(cell.ColorBlack)),
),
}
}
func main() {
t, err := tcell.New()
if err != nil {
@ -51,7 +85,7 @@ func main() {
panic(err)
}
addB, err := button.New("<Submit>", func() error {
addB, err := button.NewFromChunks(buttonChunks("Submit"), func() error {
val++
return display.Write([]*segmentdisplay.TextChunk{
segmentdisplay.NewChunk(fmt.Sprintf("%d", val)),
@ -61,12 +95,15 @@ func main() {
button.DisableShadow(),
button.Height(1),
button.TextHorizontalPadding(0),
button.FillColor(cell.ColorBlack),
button.FocusedFillColor(cell.ColorNumber(117)),
button.PressedFillColor(cell.ColorNumber(220)),
)
if err != nil {
panic(err)
}
subB, err := button.New("<Cancel>", func() error {
subB, err := button.NewFromChunks(buttonChunks("Cancel"), func() error {
val--
return display.Write([]*segmentdisplay.TextChunk{
segmentdisplay.NewChunk(fmt.Sprintf("%d", val)),
@ -77,6 +114,9 @@ func main() {
button.DisableShadow(),
button.Height(1),
button.TextHorizontalPadding(0),
button.FillColor(cell.ColorBlack),
button.FocusedFillColor(cell.ColorNumber(117)),
button.PressedFillColor(cell.ColorNumber(220)),
)
if err != nil {
panic(err)

View File

@ -43,6 +43,8 @@ func (o option) set(opts *options) {
// options holds the provided options.
type options struct {
fillColor cell.Color
focusedFillColor *cell.Color
pressedFillColor *cell.Color
textColor cell.Color
textHorizontalPadding int
shadowColor cell.Color
@ -98,6 +100,23 @@ func FillColor(c cell.Color) Option {
})
}
// FocusedFillColor sets the fill color of the button when the widget's
// container is focused.
// Defaults to FillColor.
func FocusedFillColor(c cell.Color) Option {
return option(func(opts *options) {
opts.focusedFillColor = &c
})
}
// PressedFillColor sets the fill color of the button when it is pressed.
// Defaults to FillColor.
func PressedFillColor(c cell.Color) Option {
return option(func(opts *options) {
opts.pressedFillColor = &c
})
}
// TextColor sets the color of the text label in the button.
func TextColor(c cell.Color) Option {
return option(func(opts *options) {

View File

@ -26,7 +26,9 @@ type TextOption interface {
// textOptions stores the provided options.
type textOptions struct {
cellOpts []cell.Option
cellOpts []cell.Option
focusedCellOpts []cell.Option
pressedCellOpts []cell.Option
}
// setDefaultFgColor configures a default color for text if one isn't specified
@ -69,6 +71,15 @@ func TextCellOpts(opts ...cell.Option) TextOption {
// If not specified, TextCellOpts will be used instead.
func FocusedTextCellOpts(opts ...cell.Option) TextOption {
return textOption(func(tOpts *textOptions) {
tOpts.cellOpts = opts
tOpts.focusedCellOpts = opts
})
}
// PressedTextCellOpts sets options on the cells that contain the button text
// when it is pressed.
// If not specified, TextCellOpts will be used instead.
func PressedTextCellOpts(opts ...cell.Option) TextOption {
return textOption(func(tOpts *textOptions) {
tOpts.pressedCellOpts = opts
})
}