mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
all: [API] deliver key events to the first matching filter
Replace the key.Filter.Target field with a Focus field that matches only of the specified tag has focus. This has the advantage of simpler event delivery and for lower latency in delivering key events to new handlers. For example, consider a UI where a button is activated by a key press, which is turn displays a dialog with another button activated by the same key. This change allows two button press(+releases) in the same frame to arrive at the intended targets: one key press(+release) for each button. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+5
-10
@@ -147,17 +147,12 @@ func (b *Clickable) Update(gtx layout.Context) (Click, bool) {
|
||||
})
|
||||
}
|
||||
}
|
||||
filters := []event.Filter{
|
||||
key.FocusFilter{Target: b},
|
||||
}
|
||||
if b.focused {
|
||||
filters = append(filters,
|
||||
key.Filter{Target: b, Name: key.NameReturn},
|
||||
key.Filter{Target: b, Name: key.NameSpace},
|
||||
)
|
||||
}
|
||||
for {
|
||||
e, ok := gtx.Event(filters...)
|
||||
e, ok := gtx.Event(
|
||||
key.FocusFilter{Target: b},
|
||||
key.Filter{Focus: b, Name: key.NameReturn},
|
||||
key.Filter{Focus: b, Name: key.NameSpace},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
+34
-44
@@ -333,57 +333,47 @@ func (e *Editor) clickDragEvents(gtx layout.Context) []event.Event {
|
||||
return combinedEvents
|
||||
}
|
||||
|
||||
func condFilter(pred bool, f key.Filter) event.Filter {
|
||||
if pred {
|
||||
return f
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Editor) processKey(gtx layout.Context) {
|
||||
if e.text.Changed() {
|
||||
e.events = append(e.events, ChangeEvent{})
|
||||
}
|
||||
filters := []event.Filter{key.FocusFilter{Target: e}, transfer.TargetFilter{Target: e, Type: "application/text"}}
|
||||
if e.focused {
|
||||
filters = append(filters,
|
||||
key.Filter{Target: e, Name: key.NameEnter, Optional: key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NameReturn, Optional: key.ModShift},
|
||||
caret, _ := e.text.Selection()
|
||||
atBeginning := caret == 0
|
||||
atEnd := caret == e.text.Len()
|
||||
if gtx.Locale.Direction.Progression() != system.FromOrigin {
|
||||
atEnd, atBeginning = atBeginning, atEnd
|
||||
}
|
||||
filters := []event.Filter{
|
||||
key.FocusFilter{Target: e},
|
||||
transfer.TargetFilter{Target: e, Type: "application/text"},
|
||||
key.Filter{Focus: e, Name: key.NameEnter, Optional: key.ModShift},
|
||||
key.Filter{Focus: e, Name: key.NameReturn, Optional: key.ModShift},
|
||||
|
||||
key.Filter{Target: e, Name: "Z", Required: key.ModShortcut, Optional: key.ModShift},
|
||||
key.Filter{Target: e, Name: "C", Required: key.ModShortcut},
|
||||
key.Filter{Target: e, Name: "V", Required: key.ModShortcut},
|
||||
key.Filter{Target: e, Name: "X", Required: key.ModShortcut},
|
||||
key.Filter{Target: e, Name: "A", Required: key.ModShortcut},
|
||||
key.Filter{Focus: e, Name: "Z", Required: key.ModShortcut, Optional: key.ModShift},
|
||||
key.Filter{Focus: e, Name: "C", Required: key.ModShortcut},
|
||||
key.Filter{Focus: e, Name: "V", Required: key.ModShortcut},
|
||||
key.Filter{Focus: e, Name: "X", Required: key.ModShortcut},
|
||||
key.Filter{Focus: e, Name: "A", Required: key.ModShortcut},
|
||||
|
||||
key.Filter{Target: e, Name: key.NameDeleteBackward, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NameDeleteForward, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
key.Filter{Focus: e, Name: key.NameDeleteBackward, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
key.Filter{Focus: e, Name: key.NameDeleteForward, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
|
||||
key.Filter{Target: e, Name: key.NameHome, Optional: key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NameEnd, Optional: key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NamePageDown, Optional: key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NamePageUp, Optional: key.ModShift},
|
||||
)
|
||||
caret, _ := e.text.Selection()
|
||||
if caret > 0 {
|
||||
if gtx.Locale.Direction.Progression() == system.FromOrigin {
|
||||
filters = append(filters,
|
||||
key.Filter{Target: e, Name: key.NameLeftArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NameUpArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
)
|
||||
} else {
|
||||
filters = append(filters,
|
||||
key.Filter{Target: e, Name: key.NameRightArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NameDownArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
)
|
||||
}
|
||||
}
|
||||
if caret < e.text.Len() {
|
||||
if gtx.Locale.Direction.Progression() == system.FromOrigin {
|
||||
filters = append(filters,
|
||||
key.Filter{Target: e, Name: key.NameRightArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NameDownArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
)
|
||||
} else {
|
||||
filters = append(filters,
|
||||
key.Filter{Target: e, Name: key.NameLeftArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NameUpArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
)
|
||||
}
|
||||
}
|
||||
key.Filter{Focus: e, Name: key.NameHome, Optional: key.ModShift},
|
||||
key.Filter{Focus: e, Name: key.NameEnd, Optional: key.ModShift},
|
||||
key.Filter{Focus: e, Name: key.NamePageDown, Optional: key.ModShift},
|
||||
key.Filter{Focus: e, Name: key.NamePageUp, Optional: key.ModShift},
|
||||
condFilter(!atBeginning, key.Filter{Focus: e, Name: key.NameLeftArrow, Optional: key.ModShortcutAlt | key.ModShift}),
|
||||
condFilter(!atBeginning, key.Filter{Focus: e, Name: key.NameUpArrow, Optional: key.ModShortcutAlt | key.ModShift}),
|
||||
condFilter(!atEnd, key.Filter{Focus: e, Name: key.NameRightArrow, Optional: key.ModShortcutAlt | key.ModShift}),
|
||||
condFilter(!atEnd, key.Filter{Focus: e, Name: key.NameDownArrow, Optional: key.ModShortcutAlt | key.ModShift}),
|
||||
}
|
||||
// adjust keeps track of runes dropped because of MaxLen.
|
||||
var adjust int
|
||||
|
||||
@@ -1177,6 +1177,29 @@ func TestEditor_Submit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoFilterAllocs(t *testing.T) {
|
||||
b := testing.Benchmark(func(b *testing.B) {
|
||||
r := new(input.Router)
|
||||
e := new(Editor)
|
||||
gtx := layout.Context{
|
||||
Ops: new(op.Ops),
|
||||
Constraints: layout.Constraints{
|
||||
Max: image.Pt(100, 100),
|
||||
},
|
||||
Locale: english,
|
||||
Source: r.Source(),
|
||||
}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
e.Update(gtx)
|
||||
}
|
||||
})
|
||||
if allocs := b.AllocsPerOp(); allocs != 0 {
|
||||
t.Fatalf("expected 0 AllocsPerOp, got %d", allocs)
|
||||
}
|
||||
}
|
||||
|
||||
// textWidth is a text helper for building simple selection events.
|
||||
// It assumes single-run lines, which isn't safe with non-test text
|
||||
// data.
|
||||
|
||||
+5
-10
@@ -64,17 +64,12 @@ func (e *Enum) Update(gtx layout.Context) bool {
|
||||
}
|
||||
}
|
||||
}
|
||||
filters := []event.Filter{
|
||||
key.FocusFilter{Target: &state.tag},
|
||||
}
|
||||
if e.focused && e.focus == state.key {
|
||||
filters = append(filters,
|
||||
key.Filter{Target: &state.tag, Name: key.NameReturn},
|
||||
key.Filter{Target: &state.tag, Name: key.NameSpace},
|
||||
)
|
||||
}
|
||||
for {
|
||||
ev, ok := gtx.Event(filters...)
|
||||
ev, ok := gtx.Event(
|
||||
key.FocusFilter{Target: &state.tag},
|
||||
key.Filter{Focus: &state.tag, Name: key.NameReturn},
|
||||
key.Filter{Focus: &state.tag, Name: key.NameSpace},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
+16
-21
@@ -301,28 +301,23 @@ func (e *Selectable) clickDragEvents(gtx layout.Context) []event.Event {
|
||||
}
|
||||
|
||||
func (e *Selectable) processKey(gtx layout.Context) {
|
||||
filters := []event.Filter{
|
||||
key.FocusFilter{Target: e},
|
||||
}
|
||||
if e.focused {
|
||||
filters = append(filters,
|
||||
key.Filter{Target: e, Name: key.NameLeftArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NameRightArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NameUpArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NameDownArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
|
||||
key.Filter{Target: e, Name: key.NamePageUp, Optional: key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NamePageDown, Optional: key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NameEnd, Optional: key.ModShift},
|
||||
key.Filter{Target: e, Name: key.NameHome, Optional: key.ModShift},
|
||||
|
||||
key.Filter{Target: e, Name: "C", Required: key.ModShortcut},
|
||||
key.Filter{Target: e, Name: "X", Required: key.ModShortcut},
|
||||
key.Filter{Target: e, Name: "A", Required: key.ModShortcut},
|
||||
)
|
||||
}
|
||||
for {
|
||||
ke, ok := gtx.Event(filters...)
|
||||
ke, ok := gtx.Event(
|
||||
key.FocusFilter{Target: e},
|
||||
key.Filter{Focus: e, Name: key.NameLeftArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
key.Filter{Focus: e, Name: key.NameRightArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
key.Filter{Focus: e, Name: key.NameUpArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
key.Filter{Focus: e, Name: key.NameDownArrow, Optional: key.ModShortcutAlt | key.ModShift},
|
||||
|
||||
key.Filter{Focus: e, Name: key.NamePageUp, Optional: key.ModShift},
|
||||
key.Filter{Focus: e, Name: key.NamePageDown, Optional: key.ModShift},
|
||||
key.Filter{Focus: e, Name: key.NameEnd, Optional: key.ModShift},
|
||||
key.Filter{Focus: e, Name: key.NameHome, Optional: key.ModShift},
|
||||
|
||||
key.Filter{Focus: e, Name: "C", Required: key.ModShortcut},
|
||||
key.Filter{Focus: e, Name: "X", Required: key.ModShortcut},
|
||||
key.Filter{Focus: e, Name: "A", Required: key.ModShortcut},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user