diff --git a/widgets/button/button.go b/widgets/button/button.go index 9084690..25531bb 100644 --- a/widgets/button/button.go +++ b/widgets/button/button.go @@ -28,8 +28,8 @@ import ( ) // CallbackFn is the function called when the button is pressed. -// The callback function must be non-blocking, ideally just storing a value and -// returning, since event processing blocks the redraws. +// The callback function must be light-weight, ideally just storing a value and +// returning, since more button presses might occur. // // The callback function must be thread-safe as the mouse or keyboard events // that press the button are processed in a separate goroutine. @@ -100,10 +100,12 @@ func (*Button) Mouse(m *terminalapi.Mouse) error { func (b *Button) Options() widgetapi.Options { // No need to lock, as the height and width get fixed when New is called. + // TODO calculate width and set MaximumSize too. height := b.opts.height + 1 // One for the shadow. return widgetapi.Options{ MinimumSize: image.Point{b.opts.width, height}, - WantKeyboard: true, + MaximumSize: image.Point{b.opts.width, height}, + WantKeyboard: b.opts.keyScope, WantMouse: true, } } diff --git a/widgets/button/button_test.go b/widgets/button/button_test.go index a770300..1d3243e 100644 --- a/widgets/button/button_test.go +++ b/widgets/button/button_test.go @@ -22,6 +22,7 @@ import ( "github.com/kylelemons/godebug/pretty" "github.com/mum4k/termdash/canvas" + "github.com/mum4k/termdash/keyboard" "github.com/mum4k/termdash/terminal/faketerm" "github.com/mum4k/termdash/terminalapi" "github.com/mum4k/termdash/widgetapi" @@ -150,18 +151,56 @@ func TestMouse(t *testing.T) { } func TestOptions(t *testing.T) { - ct := &callbackTracker{} - b, err := New("text", ct.callback) - if err != nil { - t.Fatalf("New => unexpected error: %v", err) + tests := []struct { + desc string + opts []Option + want widgetapi.Options + }{ + { + desc: "doesn't want keyboard by default", + want: widgetapi.Options{ + MinimumSize: image.Point{6, 3}, + WantKeyboard: widgetapi.KeyScopeNone, + WantMouse: true, + }, + }, + { + desc: "registers for focused keyboard events", + opts: []Option{ + Key(keyboard.KeyEnter), + }, + want: widgetapi.Options{ + MinimumSize: image.Point{6, 3}, + WantKeyboard: widgetapi.KeyScopeFocused, + WantMouse: true, + }, + }, + { + desc: "registers for global keyboard events", + opts: []Option{ + GlobalKey(keyboard.KeyEnter), + }, + want: widgetapi.Options{ + MinimumSize: image.Point{6, 3}, + WantKeyboard: widgetapi.KeyScopeFocused, + WantMouse: true, + }, + }, } - got := b.Options() - want := widgetapi.Options{ - MinimumSize: image.Point{6, 3}, - WantKeyboard: true, - WantMouse: true, - } - if diff := pretty.Compare(want, got); diff != "" { - t.Errorf("Options => unexpected diff (-want, +got):\n%s", diff) + + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + ct := &callbackTracker{} + b, err := New("text", ct.callback, tc.opts...) + if err != nil { + t.Fatalf("New => unexpected error: %v", err) + } + + got := b.Options() + if diff := pretty.Compare(tc.want, got); diff != "" { + t.Errorf("Options => unexpected diff (-want, +got):\n%s", diff) + } + }) } + } diff --git a/widgets/button/options.go b/widgets/button/options.go index a373d22..95fe666 100644 --- a/widgets/button/options.go +++ b/widgets/button/options.go @@ -21,6 +21,7 @@ import ( "github.com/mum4k/termdash/cell" "github.com/mum4k/termdash/keyboard" + "github.com/mum4k/termdash/widgetapi" ) // Option is used to provide options. @@ -45,6 +46,7 @@ type options struct { height int width int key keyboard.Key + keyScope widgetapi.KeyScope } // validate validates the provided options. @@ -103,7 +105,7 @@ func ShadowColor(c cell.Color) Option { } // DefaultHeight is the default for the Height option. -const DefaultHeight = 2 +const DefaultHeight = 3 // Height sets the height of the button in cells. // Must be a positive non-zero integer. @@ -123,13 +125,22 @@ func Width(cells int) Option { }) } -// DefaultKey is the default value for the Key option. -const DefaultKey = keyboard.KeyEnter - // Key configures the keyboard key that presses the button. -// Defaults to DefaultKey. +// The widget responds to this key only if its container if focused. +// When not provided, the widget ignores all keyboard events. func Key(k keyboard.Key) Option { return option(func(opts *options) { opts.key = k + opts.keyScope = widgetapi.KeyScopeFocused + }) +} + +// GlobalKey is like Key, but makes the widget respond to the key even if its +// container isn't focused. +// When not provided, the widget ignores all keyboard events. +func GlobalKey(k keyboard.Key) Option { + return option(func(opts *options) { + opts.key = k + opts.keyScope = widgetapi.KeyScopeFocused }) }