mirror of
https://github.com/mum4k/termdash.git
synced 2025-04-25 13:48:50 +08:00
Merge branch 'devel' into 243-formdemo
This commit is contained in:
commit
d30bc47245
@ -60,6 +60,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
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).
|
||||
- the `button` widget now has a method `SetCallback` that allows updating the
|
||||
callback function on an existing `button` instance.
|
||||
|
||||
#### Updates to the `textinput` widget
|
||||
|
||||
|
@ -99,6 +99,8 @@ type Button struct {
|
||||
|
||||
// New returns a new Button that will display the provided text.
|
||||
// Each press of the button will invoke the callback function.
|
||||
// The callback function can be nil in which case pressing the button is a
|
||||
// no-op.
|
||||
func New(text string, cFn CallbackFn, opts ...Option) (*Button, error) {
|
||||
return NewFromChunks([]*TextChunk{NewChunk(text)}, cFn, opts...)
|
||||
}
|
||||
@ -106,10 +108,6 @@ func New(text string, cFn CallbackFn, opts ...Option) (*Button, error) {
|
||||
// NewFromChunks is like New, but allows specifying write options for
|
||||
// individual chunks of text displayed in the button.
|
||||
func NewFromChunks(chunks []*TextChunk, cFn CallbackFn, opts ...Option) (*Button, error) {
|
||||
if cFn == nil {
|
||||
return nil, errors.New("the CallbackFn argument cannot be nil")
|
||||
}
|
||||
|
||||
if len(chunks) == 0 {
|
||||
return nil, errors.New("at least one text chunk must be specified")
|
||||
}
|
||||
@ -154,6 +152,13 @@ func NewFromChunks(chunks []*TextChunk, cFn CallbackFn, opts ...Option) (*Button
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetCallback replaces the callback function of the button with the one provided.
|
||||
func (b *Button) SetCallback(cFn CallbackFn) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
b.callback = cFn
|
||||
}
|
||||
|
||||
// Vars to be replaced from tests.
|
||||
var (
|
||||
// Runes to use in cells that contain the button.
|
||||
@ -281,10 +286,12 @@ func (b *Button) keyActivated(k *terminalapi.Keyboard, meta *widgetapi.EventMeta
|
||||
// Implements widgetapi.Widget.Keyboard.
|
||||
func (b *Button) Keyboard(k *terminalapi.Keyboard, meta *widgetapi.EventMeta) error {
|
||||
if b.keyActivated(k, meta) {
|
||||
// Mutex must be released when calling the callback.
|
||||
// Users might call container methods from the callback like the
|
||||
// Container.Update, see #205.
|
||||
return b.callback()
|
||||
if b.callback != nil {
|
||||
// Mutex must be released when calling the callback.
|
||||
// Users might call container methods from the callback like the
|
||||
// Container.Update, see #205.
|
||||
return b.callback()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -307,10 +314,12 @@ func (b *Button) mouseActivated(m *terminalapi.Mouse) bool {
|
||||
// Implements widgetapi.Widget.Mouse.
|
||||
func (b *Button) Mouse(m *terminalapi.Mouse, meta *widgetapi.EventMeta) error {
|
||||
if b.mouseActivated(m) {
|
||||
// Mutex must be released when calling the callback.
|
||||
// Users might call container methods from the callback like the
|
||||
// Container.Update, see #205.
|
||||
return b.callback()
|
||||
if b.callback != nil {
|
||||
// Mutex must be released when calling the callback.
|
||||
// Users might call container methods from the callback like the
|
||||
// Container.Update, see #205.
|
||||
return b.callback()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -45,6 +45,10 @@ type callbackTracker struct {
|
||||
// count is the number of times the callback was called.
|
||||
count int
|
||||
|
||||
// useSetCallback when set to true instructs the test to set the callback
|
||||
// via button.SetCallback instead of button.New or button.NewFromChunks.
|
||||
useSetCallback bool
|
||||
|
||||
// mu protects the tracker.
|
||||
mu sync.Mutex
|
||||
}
|
||||
@ -93,13 +97,6 @@ func TestButton(t *testing.T) {
|
||||
wantDrawErr bool
|
||||
wantCallbackErr bool
|
||||
}{
|
||||
{
|
||||
desc: "New fails with nil callback",
|
||||
canvas: image.Rect(0, 0, 1, 1),
|
||||
text: "hello",
|
||||
meta: &widgetapi.Meta{Focused: false},
|
||||
wantNewErr: true,
|
||||
},
|
||||
{
|
||||
desc: "New fails with negative keyUpDelay",
|
||||
callback: &callbackTracker{},
|
||||
@ -168,15 +165,6 @@ func TestButton(t *testing.T) {
|
||||
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{
|
||||
@ -411,7 +399,44 @@ func TestButton(t *testing.T) {
|
||||
wantCallback: &callbackTracker{},
|
||||
},
|
||||
{
|
||||
desc: "mouse triggered the callback",
|
||||
desc: "mouse triggered a button with nil callback",
|
||||
callback: nil,
|
||||
text: "hello",
|
||||
canvas: image.Rect(0, 0, 8, 4),
|
||||
meta: &widgetapi.Meta{Focused: false},
|
||||
events: []*event{
|
||||
{
|
||||
ev: &terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft},
|
||||
meta: &widgetapi.EventMeta{},
|
||||
},
|
||||
{
|
||||
ev: &terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonRelease},
|
||||
meta: &widgetapi.EventMeta{},
|
||||
},
|
||||
},
|
||||
want: func(size image.Point) *faketerm.Terminal {
|
||||
ft := faketerm.MustNew(size)
|
||||
cvs := testcanvas.MustNew(ft.Area())
|
||||
|
||||
// Shadow.
|
||||
testcanvas.MustSetAreaCells(cvs, image.Rect(1, 1, 8, 4), 's', cell.BgColor(cell.ColorNumber(240)))
|
||||
|
||||
// Button.
|
||||
testcanvas.MustSetAreaCells(cvs, image.Rect(0, 0, 7, 3), 'x', cell.BgColor(cell.ColorNumber(117)))
|
||||
|
||||
// Text.
|
||||
testdraw.MustText(cvs, "hello", image.Point{1, 1},
|
||||
draw.TextCellOpts(
|
||||
cell.FgColor(cell.ColorBlack),
|
||||
cell.BgColor(cell.ColorNumber(117))),
|
||||
)
|
||||
|
||||
testcanvas.MustApply(cvs, ft)
|
||||
return ft
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "mouse triggered a callback set via the constructor",
|
||||
callback: &callbackTracker{},
|
||||
text: "hello",
|
||||
canvas: image.Rect(0, 0, 8, 4),
|
||||
@ -451,6 +476,50 @@ func TestButton(t *testing.T) {
|
||||
count: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "mouse triggered a callback set via SetCallback",
|
||||
callback: &callbackTracker{
|
||||
useSetCallback: true,
|
||||
},
|
||||
text: "hello",
|
||||
canvas: image.Rect(0, 0, 8, 4),
|
||||
meta: &widgetapi.Meta{Focused: false},
|
||||
events: []*event{
|
||||
{
|
||||
ev: &terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft},
|
||||
meta: &widgetapi.EventMeta{},
|
||||
},
|
||||
{
|
||||
ev: &terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonRelease},
|
||||
meta: &widgetapi.EventMeta{},
|
||||
},
|
||||
},
|
||||
want: func(size image.Point) *faketerm.Terminal {
|
||||
ft := faketerm.MustNew(size)
|
||||
cvs := testcanvas.MustNew(ft.Area())
|
||||
|
||||
// Shadow.
|
||||
testcanvas.MustSetAreaCells(cvs, image.Rect(1, 1, 8, 4), 's', cell.BgColor(cell.ColorNumber(240)))
|
||||
|
||||
// Button.
|
||||
testcanvas.MustSetAreaCells(cvs, image.Rect(0, 0, 7, 3), 'x', cell.BgColor(cell.ColorNumber(117)))
|
||||
|
||||
// Text.
|
||||
testdraw.MustText(cvs, "hello", image.Point{1, 1},
|
||||
draw.TextCellOpts(
|
||||
cell.FgColor(cell.ColorBlack),
|
||||
cell.BgColor(cell.ColorNumber(117))),
|
||||
)
|
||||
|
||||
testcanvas.MustApply(cvs, ft)
|
||||
return ft
|
||||
},
|
||||
wantCallback: &callbackTracker{
|
||||
called: true,
|
||||
count: 1,
|
||||
useSetCallback: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "draws button in down state due to a keyboard event, callback triggered",
|
||||
callback: &callbackTracker{},
|
||||
@ -674,6 +743,42 @@ func TestButton(t *testing.T) {
|
||||
},
|
||||
wantCallback: &callbackTracker{},
|
||||
},
|
||||
{
|
||||
desc: "keyboard event triggers a button with nil callback",
|
||||
callback: nil,
|
||||
text: "hello",
|
||||
opts: []Option{
|
||||
Key(keyboard.KeyEnter),
|
||||
},
|
||||
timeSince: func(time.Time) time.Duration {
|
||||
return 200 * time.Millisecond
|
||||
},
|
||||
canvas: image.Rect(0, 0, 8, 4),
|
||||
meta: &widgetapi.Meta{Focused: false},
|
||||
events: []*event{
|
||||
{
|
||||
ev: &terminalapi.Keyboard{Key: keyboard.KeyEnter},
|
||||
meta: &widgetapi.EventMeta{Focused: true},
|
||||
},
|
||||
},
|
||||
want: func(size image.Point) *faketerm.Terminal {
|
||||
ft := faketerm.MustNew(size)
|
||||
cvs := testcanvas.MustNew(ft.Area())
|
||||
|
||||
// Button.
|
||||
testcanvas.MustSetAreaCells(cvs, image.Rect(1, 1, 8, 4), 'x', cell.BgColor(cell.ColorNumber(117)))
|
||||
|
||||
// Text.
|
||||
testdraw.MustText(cvs, "hello", image.Point{2, 2},
|
||||
draw.TextCellOpts(
|
||||
cell.FgColor(cell.ColorBlack),
|
||||
cell.BgColor(cell.ColorNumber(117))),
|
||||
)
|
||||
|
||||
testcanvas.MustApply(cvs, ft)
|
||||
return ft
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "keyboard event triggers the button, trigger time didn't expire so button is down",
|
||||
callback: &callbackTracker{},
|
||||
@ -1373,6 +1478,10 @@ func TestButton(t *testing.T) {
|
||||
var cFn CallbackFn
|
||||
if gotCallback == nil {
|
||||
cFn = nil
|
||||
} else if gotCallback.useSetCallback {
|
||||
// Set an no-op callback via the constructor.
|
||||
// It will be updated to the real one via SetCallback.
|
||||
cFn = func() error { return nil }
|
||||
} else {
|
||||
cFn = gotCallback.callback
|
||||
}
|
||||
@ -1402,6 +1511,10 @@ func TestButton(t *testing.T) {
|
||||
btn = b
|
||||
}
|
||||
|
||||
if gotCallback != nil && gotCallback.useSetCallback {
|
||||
btn.SetCallback(gotCallback.callback)
|
||||
}
|
||||
|
||||
{
|
||||
// Draw once which initializes the mouse state machine with the current canvas area.
|
||||
c, err := canvas.New(tc.canvas)
|
||||
|
Loading…
x
Reference in New Issue
Block a user