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

Merge pull request #274 from mum4k/243-exclusive-keyboard

Allow widgets to request keyboard events exclusively.
This commit is contained in:
Jakub Sobon 2020-11-28 00:51:05 -05:00 committed by GitHub
commit 324fe3f886
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 167 additions and 0 deletions

View File

@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- ability to configure keyboard keys that move focus to the next or the
previous container.
- widgets can not request keyboard events exclusively when focused.
## [0.13.0] - 17-Nov-2020

View File

@ -369,6 +369,9 @@ func (c *Container) keyEvTargets() []*keyEvTarget {
var (
errStr string
targets []*keyEvTarget
// If the currently focused widget set the ExclusiveKeyboardOnFocus
// option, this pointer is set to that widget.
exclusiveWidget widgetapi.Widget
)
// All the targets that should receive this event.
@ -383,6 +386,10 @@ func (c *Container) keyEvTargets() []*keyEvTarget {
Focused: focused,
}
wOpt := cur.opts.widget.Options()
if focused && wOpt.ExclusiveKeyboardOnFocus {
exclusiveWidget = cur.opts.widget
}
switch wOpt.WantKeyboard {
case widgetapi.KeyScopeNone:
// Widget doesn't want any keyboard events.
@ -398,6 +405,12 @@ func (c *Container) keyEvTargets() []*keyEvTarget {
}
return nil
}))
if exclusiveWidget != nil {
targets = []*keyEvTarget{
newKeyEvTarget(exclusiveWidget, &widgetapi.EventMeta{Focused: true}),
}
}
return targets
}

View File

@ -1219,6 +1219,152 @@ func TestKeyboard(t *testing.T) {
return ft
},
},
{
desc: "keyboard event forwarded to exclusive widget only when focused",
termSize: image.Point{40, 20},
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
ft,
SplitVertical(
Left(
PlaceWidget(fakewidget.New(widgetapi.Options{WantKeyboard: widgetapi.KeyScopeGlobal})),
),
Right(
SplitHorizontal(
Top(
PlaceWidget(fakewidget.New(widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused})),
),
Bottom(
PlaceWidget(fakewidget.New(
widgetapi.Options{
WantKeyboard: widgetapi.KeyScopeFocused,
ExclusiveKeyboardOnFocus: true,
},
)),
),
),
),
),
)
},
events: []terminalapi.Event{
// Move focus to the target container.
&terminalapi.Mouse{Position: image.Point{39, 19}, Button: mouse.ButtonLeft},
&terminalapi.Mouse{Position: image.Point{39, 19}, Button: mouse.ButtonRelease},
// Send the keyboard event.
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
// Widget that isn't focused, but registered for global
// keyboard events.
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 0, 20, 20)),
&widgetapi.Meta{},
widgetapi.Options{WantKeyboard: widgetapi.KeyScopeGlobal},
)
// Widget that isn't focused and only wants focused events.
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(20, 0, 40, 10)),
&widgetapi.Meta{},
widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused},
)
// The focused widget receives the key.
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(20, 10, 40, 20)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused},
&fakewidget.Event{
Ev: &terminalapi.Keyboard{Key: keyboard.KeyEnter},
Meta: &widgetapi.EventMeta{Focused: true},
},
)
return ft
},
},
{
desc: "the ExclusiveKeyboardOnFocus option has no effect when widget not focused",
termSize: image.Point{40, 20},
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
ft,
SplitVertical(
Left(
PlaceWidget(fakewidget.New(widgetapi.Options{WantKeyboard: widgetapi.KeyScopeGlobal})),
),
Right(
SplitHorizontal(
Top(
PlaceWidget(fakewidget.New(
widgetapi.Options{
WantKeyboard: widgetapi.KeyScopeFocused,
ExclusiveKeyboardOnFocus: true,
},
)),
),
Bottom(
PlaceWidget(fakewidget.New(
widgetapi.Options{
WantKeyboard: widgetapi.KeyScopeFocused,
},
)),
),
),
),
),
)
},
events: []terminalapi.Event{
// Move focus to the target container.
&terminalapi.Mouse{Position: image.Point{39, 19}, Button: mouse.ButtonLeft},
&terminalapi.Mouse{Position: image.Point{39, 19}, Button: mouse.ButtonRelease},
// Send the keyboard event.
&terminalapi.Keyboard{Key: keyboard.KeyEnter},
},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
// Widget that isn't focused, but registered for global
// keyboard events.
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(0, 0, 20, 20)),
&widgetapi.Meta{},
widgetapi.Options{WantKeyboard: widgetapi.KeyScopeGlobal},
&fakewidget.Event{
Ev: &terminalapi.Keyboard{Key: keyboard.KeyEnter},
Meta: &widgetapi.EventMeta{Focused: false},
},
)
// Widget that isn't focused and only wants focused events.
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(20, 0, 40, 10)),
&widgetapi.Meta{},
widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused},
)
// The focused widget receives the key.
fakewidget.MustDraw(
ft,
testcanvas.MustNew(image.Rect(20, 10, 40, 20)),
&widgetapi.Meta{Focused: true},
widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused},
&fakewidget.Event{
Ev: &terminalapi.Keyboard{Key: keyboard.KeyEnter},
Meta: &widgetapi.EventMeta{Focused: true},
},
)
return ft
},
},
{
desc: "event not forwarded if the widget didn't request it",
termSize: image.Point{40, 20},

View File

@ -130,6 +130,13 @@ type Options struct {
// forwarded to the widget.
WantKeyboard KeyScope
// ExclusiveKeyboardOnFocus allows a widget to request exclusive access to
// keyboard events when its container is focused. When set to true, no
// other widgets will receive any keyboard events that happen while the
// container of this widget is focused even if they registered for
// KeyScopeGlobal.
ExclusiveKeyboardOnFocus bool
// WantMouse allows a widget to request mouse events and specify their
// desired scope. If set to MouseScopeNone, no mouse events are forwarded
// to the widget.