io/key: [API] replace key.InputOp with a filter

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2023-10-20 09:08:39 -05:00
parent 73c3849da4
commit 27ef6dd7a2
21 changed files with 294 additions and 383 deletions
+10 -9
View File
@@ -7,6 +7,7 @@ import (
"time"
"gioui.org/gesture"
"gioui.org/io/event"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/io/semantic"
@@ -25,7 +26,7 @@ type Clickable struct {
keyTag struct{}
requestClicks int
focused bool
pressedKey string
pressedKey key.Name
}
// Click represents a click.
@@ -102,13 +103,7 @@ func (b *Clickable) Layout(gtx layout.Context, w layout.Widget) layout.Dimension
defer clip.Rect(image.Rectangle{Max: dims.Size}).Push(gtx.Ops).Pop()
semantic.EnabledOp(gtx.Enabled()).Add(gtx.Ops)
b.click.Add(gtx.Ops)
if gtx.Enabled() {
keys := key.Set("⏎|Space")
if !b.focused {
keys = ""
}
key.InputOp{Tag: &b.keyTag, Keys: keys}.Add(gtx.Ops)
}
event.InputOp(gtx.Ops, &b.keyTag)
c.Add(gtx.Ops)
return dims
}
@@ -162,7 +157,13 @@ func (b *Clickable) Update(gtx layout.Context) []Click {
})
}
}
for _, e := range gtx.Events(&b.keyTag, key.FocusFilter{}) {
filters := []event.Filter{
key.FocusFilter{},
}
if b.focused {
filters = append(filters, key.Filter{Name: key.NameReturn}, key.Filter{Name: key.NameSpace})
}
for _, e := range gtx.Events(&b.keyTag, filters...) {
switch e := e.(type) {
case key.FocusEvent:
b.focused = e.Focus
+50 -28
View File
@@ -330,9 +330,57 @@ func (e *Editor) processKey(gtx layout.Context) {
if e.text.Changed() {
e.events = append(e.events, ChangeEvent{})
}
filters := []event.Filter{key.FocusFilter{}, transfer.TargetFilter{Type: "application/text"}}
if e.focused {
filters = append(filters,
key.Filter{Name: key.NameEnter, Optional: key.ModShift},
key.Filter{Name: key.NameReturn, Optional: key.ModShift},
key.Filter{Name: "Z", Required: key.ModShortcut, Optional: key.ModShift},
key.Filter{Name: "C", Required: key.ModShortcut},
key.Filter{Name: "V", Required: key.ModShortcut},
key.Filter{Name: "X", Required: key.ModShortcut},
key.Filter{Name: "A", Required: key.ModShortcut},
key.Filter{Name: key.NameDeleteBackward, Optional: key.ModShortcutAlt | key.ModShift},
key.Filter{Name: key.NameDeleteForward, Optional: key.ModShortcutAlt | key.ModShift},
key.Filter{Name: key.NameHome, Optional: key.ModShift},
key.Filter{Name: key.NameEnd, Optional: key.ModShift},
key.Filter{Name: key.NamePageDown, Optional: key.ModShift},
key.Filter{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{Name: key.NameLeftArrow, Optional: key.ModShortcutAlt | key.ModShift},
key.Filter{Name: key.NameUpArrow, Optional: key.ModShortcutAlt | key.ModShift},
)
} else {
filters = append(filters,
key.Filter{Name: key.NameRightArrow, Optional: key.ModShortcutAlt | key.ModShift},
key.Filter{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{Name: key.NameRightArrow, Optional: key.ModShortcutAlt | key.ModShift},
key.Filter{Name: key.NameDownArrow, Optional: key.ModShortcutAlt | key.ModShift},
)
} else {
filters = append(filters,
key.Filter{Name: key.NameLeftArrow, Optional: key.ModShortcutAlt | key.ModShift},
key.Filter{Name: key.NameUpArrow, Optional: key.ModShortcutAlt | key.ModShift},
)
}
}
}
// adjust keeps track of runes dropped because of MaxLen.
var adjust int
for _, ke := range gtx.Events(&e.eventKey, transfer.TargetFilter{Type: "application/text"}, key.FocusFilter{}) {
for _, ke := range gtx.Events(&e.eventKey, filters...) {
e.blinkStart = gtx.Now
switch ke := ke.(type) {
case key.FocusEvent:
@@ -625,33 +673,7 @@ func (e *Editor) layout(gtx layout.Context, textMaterial, selectMaterial op.Call
defer clip.Rect(image.Rectangle{Max: visibleDims.Size}).Push(gtx.Ops).Pop()
pointer.CursorText.Add(gtx.Ops)
var keys key.Set
if e.focused {
const keyFilterNoLeftUp = "(ShortAlt)-(Shift)-[→,↓]|(Shift)-[⏎,⌤]|(ShortAlt)-(Shift)-[⌫,⌦]|(Shift)-[⇞,⇟,⇱,⇲]|Short-[C,V,X,A]|Short-(Shift)-Z"
const keyFilterNoRightDown = "(ShortAlt)-(Shift)-[←,↑]|(Shift)-[⏎,⌤]|(ShortAlt)-(Shift)-[⌫,⌦]|(Shift)-[⇞,⇟,⇱,⇲]|Short-[C,V,X,A]|Short-(Shift)-Z"
const keyFilterNoArrows = "(Shift)-[⏎,⌤]|(ShortAlt)-(Shift)-[⌫,⌦]|(Shift)-[⇞,⇟,⇱,⇲]|Short-[C,V,X,A]|Short-(Shift)-Z"
const keyFilterAllArrows = "(ShortAlt)-(Shift)-[←,→,↑,↓]|(Shift)-[⏎,⌤]|(ShortAlt)-(Shift)-[⌫,⌦]|(Shift)-[⇞,⇟,⇱,⇲]|Short-[C,V,X,A]|Short-(Shift)-Z"
caret, _ := e.text.Selection()
switch {
case caret == 0 && caret == e.text.Len():
keys = keyFilterNoArrows
case caret == 0:
if gtx.Locale.Direction.Progression() == system.FromOrigin {
keys = keyFilterNoLeftUp
} else {
keys = keyFilterNoRightDown
}
case caret == e.text.Len():
if gtx.Locale.Direction.Progression() == system.FromOrigin {
keys = keyFilterNoRightDown
} else {
keys = keyFilterNoLeftUp
}
default:
keys = keyFilterAllArrows
}
}
key.InputOp{Tag: &e.eventKey, Keys: keys}.Add(gtx.Ops)
event.InputOp(gtx.Ops, &e.eventKey)
key.InputHintOp{Tag: &e.eventKey, Hint: e.InputHint}.Add(gtx.Ops)
e.scroller.Add(gtx.Ops)
+7 -1
View File
@@ -124,6 +124,9 @@ func TestEditorReadOnly(t *testing.T) {
gtx.Ops.Reset()
layoutEditor()
r.Frame(gtx.Ops)
gtx.Ops.Reset()
layoutEditor()
r.Frame(gtx.Ops)
// Select everything.
gtx.Ops.Reset()
@@ -1005,8 +1008,11 @@ func TestSelectMove(t *testing.T) {
gtx.Ops.Reset()
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
r.Frame(gtx.Ops)
gtx.Ops.Reset()
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
r.Frame(gtx.Ops)
for _, keyName := range []string{key.NameLeftArrow, key.NameRightArrow, key.NameUpArrow, key.NameDownArrow} {
for _, keyName := range []key.Name{key.NameLeftArrow, key.NameRightArrow, key.NameUpArrow, key.NameDownArrow} {
// Select 345
e.SetCaret(3, 6)
if expected, got := "345", e.SelectedText(); expected != got {
+10 -5
View File
@@ -4,6 +4,7 @@ package widget
import (
"gioui.org/gesture"
"gioui.org/io/event"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/io/semantic"
@@ -59,7 +60,13 @@ func (e *Enum) Update(gtx layout.Context) bool {
}
}
}
for _, ev := range gtx.Events(&state.tag, key.FocusFilter{}) {
filters := []event.Filter{
key.FocusFilter{},
}
if e.focused && e.focus == state.key {
filters = append(filters, key.Filter{Name: key.NameReturn}, key.Filter{Name: key.NameSpace})
}
for _, ev := range gtx.Events(&state.tag, filters...) {
switch ev := ev.(type) {
case key.FocusEvent:
if ev.Focus {
@@ -69,7 +76,7 @@ func (e *Enum) Update(gtx layout.Context) bool {
e.focused = false
}
case key.Event:
if !e.focused || ev.State != key.Release {
if ev.State != key.Release {
break
}
if ev.Name != key.NameReturn && ev.Name != key.NameSpace {
@@ -117,9 +124,7 @@ func (e *Enum) Layout(gtx layout.Context, k string, content layout.Widget) layou
}
clk := &state.click
clk.Add(gtx.Ops)
if gtx.Enabled() {
key.InputOp{Tag: &state.tag, Keys: "⏎|Space"}.Add(gtx.Ops)
}
event.InputOp(gtx.Ops, &state.tag)
semantic.SelectedOp(k == e.Value).Add(gtx.Ops)
semantic.EnabledOp(gtx.Enabled()).Add(gtx.Ops)
c.Add(gtx.Ops)
+22 -7
View File
@@ -204,12 +204,7 @@ func (l *Selectable) Layout(gtx layout.Context, lt *text.Shaper, font font.Font,
dims := l.text.Dimensions()
defer clip.Rect(image.Rectangle{Max: dims.Size}).Push(gtx.Ops).Pop()
pointer.CursorText.Add(gtx.Ops)
var keys key.Set
if l.focused {
const keyFilter = "(ShortAlt)-(Shift)-[←,→,↑,↓]|(Shift)-[⇞,⇟,⇱,⇲]|Short-[C,X,A]"
keys = keyFilter
}
key.InputOp{Tag: l, Keys: keys}.Add(gtx.Ops)
event.InputOp(gtx.Ops, l)
l.clicker.Add(gtx.Ops)
l.dragger.Add(gtx.Ops)
@@ -304,7 +299,27 @@ func (e *Selectable) clickDragEvents(gtx layout.Context) []event.Event {
}
func (e *Selectable) processKey(gtx layout.Context) {
for _, ke := range gtx.Events(e, key.FocusFilter{}) {
filters := []event.Filter{
key.FocusFilter{},
}
if e.focused {
filters = append(filters,
key.Filter{Name: key.NameLeftArrow, Optional: key.ModShortcutAlt | key.ModShift},
key.Filter{Name: key.NameRightArrow, Optional: key.ModShortcutAlt | key.ModShift},
key.Filter{Name: key.NameUpArrow, Optional: key.ModShortcutAlt | key.ModShift},
key.Filter{Name: key.NameDownArrow, Optional: key.ModShortcutAlt | key.ModShift},
key.Filter{Name: key.NamePageUp, Optional: key.ModShift},
key.Filter{Name: key.NamePageDown, Optional: key.ModShift},
key.Filter{Name: key.NameEnd, Optional: key.ModShift},
key.Filter{Name: key.NameHome, Optional: key.ModShift},
key.Filter{Name: "C", Required: key.ModShortcut},
key.Filter{Name: "X", Required: key.ModShortcut},
key.Filter{Name: "A", Required: key.ModShortcut},
)
}
for _, ke := range gtx.Events(e, filters...) {
switch ke := ke.(type) {
case key.FocusEvent:
e.focused = ke.Focus
+3 -1
View File
@@ -57,8 +57,10 @@ func TestSelectableMove(t *testing.T) {
s.SetCaret(3, 6)
s.Layout(gtx, cache, font.Font{}, fontSize, op.CallOp{}, op.CallOp{})
r.Frame(gtx.Ops)
s.Layout(gtx, cache, font.Font{}, fontSize, op.CallOp{}, op.CallOp{})
r.Frame(gtx.Ops)
for _, keyName := range []string{key.NameLeftArrow, key.NameRightArrow, key.NameUpArrow, key.NameDownArrow} {
for _, keyName := range []key.Name{key.NameLeftArrow, key.NameRightArrow, key.NameUpArrow, key.NameDownArrow} {
// Select 345
s.SetCaret(3, 6)
if start, end := s.Selection(); start != 3 || end != 6 {