mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
io/key: [API] replace key.InputOp with a filter
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+10
-9
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user