mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-04 17:05:38 +00:00
io/input,io/key: [API] introduce Command, replace FocusOp with FocusCmd
Modeling focus change as an operation is awkward, because focus changes logically happen during event processing, not layout. In particular, you want to apply focus changes even if a widget is subsequently never laid out. Now that input.Source is concrete, it's much more straightforward to offer focus changes as a command which can be queued through the Source. A future change may similarly offer a command for directional focus changes. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -69,7 +69,6 @@ const (
|
|||||||
TypeTarget
|
TypeTarget
|
||||||
TypeOffer
|
TypeOffer
|
||||||
TypeKeyInput
|
TypeKeyInput
|
||||||
TypeKeyFocus
|
|
||||||
TypeKeySoftKeyboard
|
TypeKeySoftKeyboard
|
||||||
TypeSave
|
TypeSave
|
||||||
TypeLoad
|
TypeLoad
|
||||||
@@ -154,7 +153,6 @@ const (
|
|||||||
TypeTargetLen = 1
|
TypeTargetLen = 1
|
||||||
TypeOfferLen = 1
|
TypeOfferLen = 1
|
||||||
TypeKeyInputLen = 1 + 1
|
TypeKeyInputLen = 1 + 1
|
||||||
TypeKeyFocusLen = 1 + 1
|
|
||||||
TypeKeySoftKeyboardLen = 1 + 1
|
TypeKeySoftKeyboardLen = 1 + 1
|
||||||
TypeSaveLen = 1 + 4
|
TypeSaveLen = 1 + 4
|
||||||
TypeLoadLen = 1 + 4
|
TypeLoadLen = 1 + 4
|
||||||
@@ -437,7 +435,6 @@ var opProps = [0x100]opProp{
|
|||||||
TypeTarget: {Size: TypeTargetLen, NumRefs: 2},
|
TypeTarget: {Size: TypeTargetLen, NumRefs: 2},
|
||||||
TypeOffer: {Size: TypeOfferLen, NumRefs: 3},
|
TypeOffer: {Size: TypeOfferLen, NumRefs: 3},
|
||||||
TypeKeyInput: {Size: TypeKeyInputLen, NumRefs: 2},
|
TypeKeyInput: {Size: TypeKeyInputLen, NumRefs: 2},
|
||||||
TypeKeyFocus: {Size: TypeKeyFocusLen, NumRefs: 1},
|
|
||||||
TypeKeySoftKeyboard: {Size: TypeKeySoftKeyboardLen, NumRefs: 0},
|
TypeKeySoftKeyboard: {Size: TypeKeySoftKeyboardLen, NumRefs: 0},
|
||||||
TypeSave: {Size: TypeSaveLen, NumRefs: 0},
|
TypeSave: {Size: TypeSaveLen, NumRefs: 0},
|
||||||
TypeLoad: {Size: TypeLoadLen, NumRefs: 0},
|
TypeLoad: {Size: TypeLoadLen, NumRefs: 0},
|
||||||
@@ -514,8 +511,6 @@ func (t OpType) String() string {
|
|||||||
return "Offer"
|
return "Offer"
|
||||||
case TypeKeyInput:
|
case TypeKeyInput:
|
||||||
return "KeyInput"
|
return "KeyInput"
|
||||||
case TypeKeyFocus:
|
|
||||||
return "KeyFocus"
|
|
||||||
case TypeKeySoftKeyboard:
|
case TypeKeySoftKeyboard:
|
||||||
return "KeySoftKeyboard"
|
return "KeySoftKeyboard"
|
||||||
case TypeSave:
|
case TypeSave:
|
||||||
|
|||||||
+25
-42
@@ -44,14 +44,6 @@ type keyHandler struct {
|
|||||||
filter key.Set
|
filter key.Set
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyCollector tracks state required to update a keyQueue
|
|
||||||
// from key ops.
|
|
||||||
type keyCollector struct {
|
|
||||||
q *keyQueue
|
|
||||||
focus event.Tag
|
|
||||||
changed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type dirFocusEntry struct {
|
type dirFocusEntry struct {
|
||||||
tag event.Tag
|
tag event.Tag
|
||||||
row int
|
row int
|
||||||
@@ -99,8 +91,7 @@ func (q *keyQueue) Reset() {
|
|||||||
q.dirOrder = q.dirOrder[:0]
|
q.dirOrder = q.dirOrder[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *keyQueue) Frame(events *handlerEvents, collector keyCollector) {
|
func (q *keyQueue) Frame(events *handlerEvents) {
|
||||||
changed, focus := collector.changed, collector.focus
|
|
||||||
for k, h := range q.handlers {
|
for k, h := range q.handlers {
|
||||||
if !h.visible {
|
if !h.visible {
|
||||||
delete(q.handlers, k)
|
delete(q.handlers, k)
|
||||||
@@ -109,14 +100,11 @@ func (q *keyQueue) Frame(events *handlerEvents, collector keyCollector) {
|
|||||||
q.focus = nil
|
q.focus = nil
|
||||||
q.state = TextInputClose
|
q.state = TextInputClose
|
||||||
}
|
}
|
||||||
} else if h.new && k != focus {
|
} else if h.new && k != q.focus {
|
||||||
// Reset the handler on (each) first appearance, but don't trigger redraw.
|
// Reset the handler on (each) first appearance, but don't trigger redraw.
|
||||||
events.AddNoRedraw(k, key.FocusEvent{Focus: false})
|
events.AddNoRedraw(k, key.FocusEvent{Focus: false})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if changed {
|
|
||||||
q.setFocus(focus, events)
|
|
||||||
}
|
|
||||||
q.updateFocusLayout()
|
q.updateFocusLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +175,7 @@ func (q *keyQueue) MoveFocus(dir key.FocusDirection, events *handlerEvents) bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
order = (order + len(q.order)) % len(q.order)
|
order = (order + len(q.order)) % len(q.order)
|
||||||
q.setFocus(q.order[order], events)
|
q.Focus(q.order[order], events)
|
||||||
return true
|
return true
|
||||||
case key.FocusRight, key.FocusLeft:
|
case key.FocusRight, key.FocusLeft:
|
||||||
next := order
|
next := order
|
||||||
@@ -200,7 +188,7 @@ func (q *keyQueue) MoveFocus(dir key.FocusDirection, events *handlerEvents) bool
|
|||||||
if 0 <= next && next < len(q.dirOrder) {
|
if 0 <= next && next < len(q.dirOrder) {
|
||||||
newFocus := q.dirOrder[next]
|
newFocus := q.dirOrder[next]
|
||||||
if newFocus.row == focus.row {
|
if newFocus.row == focus.row {
|
||||||
q.setFocus(newFocus.tag, events)
|
q.Focus(newFocus.tag, events)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -237,7 +225,7 @@ func (q *keyQueue) MoveFocus(dir key.FocusDirection, events *handlerEvents) bool
|
|||||||
order += delta
|
order += delta
|
||||||
}
|
}
|
||||||
if closest != nil {
|
if closest != nil {
|
||||||
q.setFocus(closest, events)
|
q.Focus(closest, events)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,7 +246,7 @@ func (q *keyQueue) Accepts(t event.Tag, e key.Event) bool {
|
|||||||
return q.handlers[t].filter.Contains(e.Name, e.Modifiers)
|
return q.handlers[t].filter.Contains(e.Name, e.Modifiers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *keyQueue) setFocus(focus event.Tag, events *handlerEvents) {
|
func (q *keyQueue) Focus(focus event.Tag, events *handlerEvents) {
|
||||||
if focus != nil {
|
if focus != nil {
|
||||||
if _, exists := q.handlers[focus]; !exists {
|
if _, exists := q.handlers[focus]; !exists {
|
||||||
focus = nil
|
focus = nil
|
||||||
@@ -280,51 +268,46 @@ func (q *keyQueue) setFocus(focus event.Tag, events *handlerEvents) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *keyCollector) focusOp(tag event.Tag) {
|
func (q *keyQueue) softKeyboard(show bool) {
|
||||||
k.focus = tag
|
|
||||||
k.changed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *keyCollector) softKeyboard(show bool) {
|
|
||||||
if show {
|
if show {
|
||||||
k.q.state = TextInputOpen
|
q.state = TextInputOpen
|
||||||
} else {
|
} else {
|
||||||
k.q.state = TextInputClose
|
q.state = TextInputClose
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *keyCollector) handlerFor(tag event.Tag, area int, bounds image.Rectangle) *keyHandler {
|
func (q *keyQueue) handlerFor(tag event.Tag, area int, bounds image.Rectangle) *keyHandler {
|
||||||
h, ok := k.q.handlers[tag]
|
h, ok := q.handlers[tag]
|
||||||
if !ok {
|
if !ok {
|
||||||
h = &keyHandler{new: true, order: -1}
|
h = &keyHandler{new: true, order: -1}
|
||||||
k.q.handlers[tag] = h
|
q.handlers[tag] = h
|
||||||
}
|
}
|
||||||
if h.order == -1 {
|
if h.order == -1 {
|
||||||
h.order = len(k.q.order)
|
h.order = len(q.order)
|
||||||
k.q.order = append(k.q.order, tag)
|
q.order = append(q.order, tag)
|
||||||
k.q.dirOrder = append(k.q.dirOrder, dirFocusEntry{tag: tag, area: area, bounds: bounds})
|
q.dirOrder = append(q.dirOrder, dirFocusEntry{tag: tag, area: area, bounds: bounds})
|
||||||
}
|
}
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *keyCollector) inputOp(op key.InputOp, area int, bounds image.Rectangle) {
|
func (q *keyQueue) inputOp(op key.InputOp, area int, bounds image.Rectangle) {
|
||||||
h := k.handlerFor(op.Tag, area, bounds)
|
h := q.handlerFor(op.Tag, area, bounds)
|
||||||
h.visible = true
|
h.visible = true
|
||||||
h.hint = op.Hint
|
h.hint = op.Hint
|
||||||
h.filter = op.Keys
|
h.filter = op.Keys
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *keyCollector) selectionOp(t f32.Affine2D, op key.SelectionOp) {
|
func (q *keyQueue) selectionOp(t f32.Affine2D, op key.SelectionOp) {
|
||||||
if op.Tag == k.q.focus {
|
if op.Tag == q.focus {
|
||||||
k.q.content.Selection.Range = op.Range
|
q.content.Selection.Range = op.Range
|
||||||
k.q.content.Selection.Caret = op.Caret
|
q.content.Selection.Caret = op.Caret
|
||||||
k.q.content.Selection.Transform = t
|
q.content.Selection.Transform = t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *keyCollector) snippetOp(op key.SnippetOp) {
|
func (q *keyQueue) snippetOp(op key.SnippetOp) {
|
||||||
if op.Tag == k.q.focus {
|
if op.Tag == q.focus {
|
||||||
k.q.content.Snippet = op.Snippet
|
q.content.Snippet = op.Snippet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-11
@@ -39,7 +39,7 @@ func TestKeyMultiples(t *testing.T) {
|
|||||||
|
|
||||||
key.SoftKeyboardOp{Show: true}.Add(ops)
|
key.SoftKeyboardOp{Show: true}.Add(ops)
|
||||||
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
||||||
key.FocusOp{Tag: &handlers[2]}.Add(ops)
|
r.Source().Queue(key.FocusCmd{Tag: &handlers[2]})
|
||||||
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
||||||
|
|
||||||
// The last one must be focused:
|
// The last one must be focused:
|
||||||
@@ -60,10 +60,10 @@ func TestKeyStacked(t *testing.T) {
|
|||||||
r := new(Router)
|
r := new(Router)
|
||||||
|
|
||||||
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
||||||
key.FocusOp{Tag: nil}.Add(ops)
|
r.Source().Queue(key.FocusCmd{})
|
||||||
key.SoftKeyboardOp{Show: false}.Add(ops)
|
key.SoftKeyboardOp{Show: false}.Add(ops)
|
||||||
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
||||||
key.FocusOp{Tag: &handlers[1]}.Add(ops)
|
r.Source().Queue(key.FocusCmd{Tag: &handlers[1]})
|
||||||
key.InputOp{Tag: &handlers[2]}.Add(ops)
|
key.InputOp{Tag: &handlers[2]}.Add(ops)
|
||||||
key.SoftKeyboardOp{Show: true}.Add(ops)
|
key.SoftKeyboardOp{Show: true}.Add(ops)
|
||||||
key.InputOp{Tag: &handlers[3]}.Add(ops)
|
key.InputOp{Tag: &handlers[3]}.Add(ops)
|
||||||
@@ -99,7 +99,7 @@ func TestKeyRemoveFocus(t *testing.T) {
|
|||||||
|
|
||||||
// New InputOp with Focus and Keyboard:
|
// New InputOp with Focus and Keyboard:
|
||||||
key.InputOp{Tag: &handlers[0], Keys: "Short-Tab"}.Add(ops)
|
key.InputOp{Tag: &handlers[0], Keys: "Short-Tab"}.Add(ops)
|
||||||
key.FocusOp{Tag: &handlers[0]}.Add(ops)
|
r.Source().Queue(key.FocusCmd{Tag: &handlers[0]})
|
||||||
key.SoftKeyboardOp{Show: true}.Add(ops)
|
key.SoftKeyboardOp{Show: true}.Add(ops)
|
||||||
|
|
||||||
// New InputOp without any focus:
|
// New InputOp without any focus:
|
||||||
@@ -125,7 +125,7 @@ func TestKeyRemoveFocus(t *testing.T) {
|
|||||||
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
||||||
|
|
||||||
// Remove focus by focusing on a tag that don't exist.
|
// Remove focus by focusing on a tag that don't exist.
|
||||||
key.FocusOp{Tag: new(int)}.Add(ops)
|
r.Source().Queue(key.FocusCmd{Tag: new(int)})
|
||||||
|
|
||||||
r.Frame(ops)
|
r.Frame(ops)
|
||||||
|
|
||||||
@@ -150,19 +150,19 @@ func TestKeyRemoveFocus(t *testing.T) {
|
|||||||
|
|
||||||
// Set focus to InputOp which already
|
// Set focus to InputOp which already
|
||||||
// exists in the previous frame:
|
// exists in the previous frame:
|
||||||
key.FocusOp{Tag: &handlers[0]}.Add(ops)
|
r.Source().Queue(key.FocusCmd{Tag: &handlers[0]})
|
||||||
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
||||||
key.SoftKeyboardOp{Show: true}.Add(ops)
|
key.SoftKeyboardOp{Show: true}.Add(ops)
|
||||||
|
|
||||||
// Remove focus.
|
// Remove focus.
|
||||||
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
key.InputOp{Tag: &handlers[1]}.Add(ops)
|
||||||
key.FocusOp{Tag: nil}.Add(ops)
|
r.Source().Queue(key.FocusCmd{})
|
||||||
|
|
||||||
r.Frame(ops)
|
r.Frame(ops)
|
||||||
|
|
||||||
assertKeyEventUnexpected(t, r.Events(&handlers[1]))
|
assertKeyEventUnexpected(t, r.Events(&handlers[1]))
|
||||||
assertFocus(t, r, nil)
|
assertFocus(t, r, nil)
|
||||||
assertKeyboard(t, r, TextInputOpen)
|
assertKeyboard(t, r, TextInputClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyFocusedInvisible(t *testing.T) {
|
func TestKeyFocusedInvisible(t *testing.T) {
|
||||||
@@ -171,7 +171,7 @@ func TestKeyFocusedInvisible(t *testing.T) {
|
|||||||
r := new(Router)
|
r := new(Router)
|
||||||
|
|
||||||
// Set new InputOp with focus:
|
// Set new InputOp with focus:
|
||||||
key.FocusOp{Tag: &handlers[0]}.Add(ops)
|
r.Source().Queue(key.FocusCmd{Tag: &handlers[0]})
|
||||||
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
key.InputOp{Tag: &handlers[0]}.Add(ops)
|
||||||
key.SoftKeyboardOp{Show: true}.Add(ops)
|
key.SoftKeyboardOp{Show: true}.Add(ops)
|
||||||
|
|
||||||
@@ -351,8 +351,7 @@ func TestKeyRouting(t *testing.T) {
|
|||||||
|
|
||||||
r2 := new(Router)
|
r2 := new(Router)
|
||||||
|
|
||||||
call.Add(ops)
|
r2.Source().Queue(key.FocusCmd{Tag: &handlers[3]})
|
||||||
key.FocusOp{Tag: &handlers[3]}.Add(ops)
|
|
||||||
r2.Frame(ops)
|
r2.Frame(ops)
|
||||||
|
|
||||||
r2.Queue(A, B)
|
r2.Queue(A, B)
|
||||||
|
|||||||
+41
-17
@@ -33,8 +33,7 @@ type Router struct {
|
|||||||
collector pointerCollector
|
collector pointerCollector
|
||||||
}
|
}
|
||||||
key struct {
|
key struct {
|
||||||
queue keyQueue
|
queue keyQueue
|
||||||
collector keyCollector
|
|
||||||
}
|
}
|
||||||
cqueue clipboardQueue
|
cqueue clipboardQueue
|
||||||
|
|
||||||
@@ -45,6 +44,9 @@ type Router struct {
|
|||||||
// InvalidateOp summary.
|
// InvalidateOp summary.
|
||||||
wakeup bool
|
wakeup bool
|
||||||
wakeupTime time.Time
|
wakeupTime time.Time
|
||||||
|
|
||||||
|
// Changes queued for next call to Frame.
|
||||||
|
commands []Command
|
||||||
}
|
}
|
||||||
|
|
||||||
// Source implements the interface between a Router and user interface widgets.
|
// Source implements the interface between a Router and user interface widgets.
|
||||||
@@ -53,6 +55,12 @@ type Source struct {
|
|||||||
r *Router
|
r *Router
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command represents a request such as moving the focus, or initiating a clipboard read.
|
||||||
|
// Commands are queued by calling [Source.Queue].
|
||||||
|
type Command interface {
|
||||||
|
ImplementsCommand()
|
||||||
|
}
|
||||||
|
|
||||||
// SemanticNode represents a node in the tree describing the components
|
// SemanticNode represents a node in the tree describing the components
|
||||||
// contained in a frame.
|
// contained in a frame.
|
||||||
type SemanticNode struct {
|
type SemanticNode struct {
|
||||||
@@ -98,6 +106,15 @@ func (q *Router) Source() Source {
|
|||||||
return Source{r: q}
|
return Source{r: q}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Queue a command to be executed after the current frame
|
||||||
|
// has completed.
|
||||||
|
func (s Source) Queue(c Command) {
|
||||||
|
if !s.Enabled() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.r.queue(c)
|
||||||
|
}
|
||||||
|
|
||||||
// Enabled reports whether the source is enabled. Only enabled
|
// Enabled reports whether the source is enabled. Only enabled
|
||||||
// Sources deliver events and respond to commands.
|
// Sources deliver events and respond to commands.
|
||||||
func (s Source) Enabled() bool {
|
func (s Source) Enabled() bool {
|
||||||
@@ -129,9 +146,10 @@ func (q *Router) Frame(frame *op.Ops) {
|
|||||||
}
|
}
|
||||||
q.reader.Reset(ops)
|
q.reader.Reset(ops)
|
||||||
q.collect()
|
q.collect()
|
||||||
|
q.executeCommands()
|
||||||
q.pointer.queue.Frame(&q.handlers)
|
q.pointer.queue.Frame(&q.handlers)
|
||||||
q.key.queue.Frame(&q.handlers, q.key.collector)
|
q.key.queue.Frame(&q.handlers)
|
||||||
|
|
||||||
if q.handlers.HadEvents() {
|
if q.handlers.HadEvents() {
|
||||||
q.wakeup = true
|
q.wakeup = true
|
||||||
q.wakeupTime = time.Time{}
|
q.wakeupTime = time.Time{}
|
||||||
@@ -189,6 +207,20 @@ func (q *Router) Queue(events ...event.Event) bool {
|
|||||||
return q.handlers.HadEvents()
|
return q.handlers.HadEvents()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *Router) queue(f Command) {
|
||||||
|
q.commands = append(q.commands, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Router) executeCommands() {
|
||||||
|
for _, req := range q.commands {
|
||||||
|
switch req := req.(type) {
|
||||||
|
case key.FocusCmd:
|
||||||
|
q.key.queue.Focus(req.Tag, &q.handlers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q.commands = nil
|
||||||
|
}
|
||||||
|
|
||||||
func rangeOverlaps(r1, r2 key.Range) bool {
|
func rangeOverlaps(r1, r2 key.Range) bool {
|
||||||
r1 = rangeNorm(r1)
|
r1 = rangeNorm(r1)
|
||||||
r2 = rangeNorm(r2)
|
r2 = rangeNorm(r2)
|
||||||
@@ -377,8 +409,7 @@ func (q *Router) collect() {
|
|||||||
pc := &q.pointer.collector
|
pc := &q.pointer.collector
|
||||||
pc.q = &q.pointer.queue
|
pc.q = &q.pointer.queue
|
||||||
pc.reset()
|
pc.reset()
|
||||||
kc := &q.key.collector
|
kq := &q.key.queue
|
||||||
*kc = keyCollector{q: &q.key.queue}
|
|
||||||
q.key.queue.Reset()
|
q.key.queue.Reset()
|
||||||
var t f32.Affine2D
|
var t f32.Affine2D
|
||||||
bo := binary.LittleEndian
|
bo := binary.LittleEndian
|
||||||
@@ -473,18 +504,11 @@ func (q *Router) collect() {
|
|||||||
act := system.Action(encOp.Data[1])
|
act := system.Action(encOp.Data[1])
|
||||||
pc.actionInputOp(act)
|
pc.actionInputOp(act)
|
||||||
|
|
||||||
// Key ops.
|
|
||||||
case ops.TypeKeyFocus:
|
|
||||||
tag, _ := encOp.Refs[0].(event.Tag)
|
|
||||||
op := key.FocusOp{
|
|
||||||
Tag: tag,
|
|
||||||
}
|
|
||||||
kc.focusOp(op.Tag)
|
|
||||||
case ops.TypeKeySoftKeyboard:
|
case ops.TypeKeySoftKeyboard:
|
||||||
op := key.SoftKeyboardOp{
|
op := key.SoftKeyboardOp{
|
||||||
Show: encOp.Data[1] != 0,
|
Show: encOp.Data[1] != 0,
|
||||||
}
|
}
|
||||||
kc.softKeyboard(op.Show)
|
kq.softKeyboard(op.Show)
|
||||||
case ops.TypeKeyInput:
|
case ops.TypeKeyInput:
|
||||||
filter := key.Set(*encOp.Refs[1].(*string))
|
filter := key.Set(*encOp.Refs[1].(*string))
|
||||||
op := key.InputOp{
|
op := key.InputOp{
|
||||||
@@ -495,7 +519,7 @@ func (q *Router) collect() {
|
|||||||
a := pc.currentArea()
|
a := pc.currentArea()
|
||||||
b := pc.currentAreaBounds()
|
b := pc.currentAreaBounds()
|
||||||
pc.keyInputOp(op)
|
pc.keyInputOp(op)
|
||||||
kc.inputOp(op, a, b)
|
kq.inputOp(op, a, b)
|
||||||
case ops.TypeSnippet:
|
case ops.TypeSnippet:
|
||||||
op := key.SnippetOp{
|
op := key.SnippetOp{
|
||||||
Tag: encOp.Refs[0].(event.Tag),
|
Tag: encOp.Refs[0].(event.Tag),
|
||||||
@@ -507,7 +531,7 @@ func (q *Router) collect() {
|
|||||||
Text: *(encOp.Refs[1].(*string)),
|
Text: *(encOp.Refs[1].(*string)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
kc.snippetOp(op)
|
kq.snippetOp(op)
|
||||||
case ops.TypeSelection:
|
case ops.TypeSelection:
|
||||||
op := key.SelectionOp{
|
op := key.SelectionOp{
|
||||||
Tag: encOp.Refs[0].(event.Tag),
|
Tag: encOp.Refs[0].(event.Tag),
|
||||||
@@ -524,7 +548,7 @@ func (q *Router) collect() {
|
|||||||
Descent: math.Float32frombits(bo.Uint32(encOp.Data[21:])),
|
Descent: math.Float32frombits(bo.Uint32(encOp.Data[21:])),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
kc.selectionOp(t, op)
|
kq.selectionOp(t, op)
|
||||||
|
|
||||||
// Semantic ops.
|
// Semantic ops.
|
||||||
case ops.TypeSemanticLabel:
|
case ops.TypeSemanticLabel:
|
||||||
|
|||||||
+9
-13
@@ -56,14 +56,6 @@ type SoftKeyboardOp struct {
|
|||||||
Show bool
|
Show bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// FocusOp sets or clears the keyboard focus. It replaces any previous
|
|
||||||
// FocusOp in the same frame.
|
|
||||||
type FocusOp struct {
|
|
||||||
// Tag is the new focus. The focus is cleared if Tag is nil, or if Tag
|
|
||||||
// has no InputOp in the same frame.
|
|
||||||
Tag event.Tag
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectionOp updates the selection for an input handler.
|
// SelectionOp updates the selection for an input handler.
|
||||||
type SelectionOp struct {
|
type SelectionOp struct {
|
||||||
Tag event.Tag
|
Tag event.Tag
|
||||||
@@ -242,6 +234,13 @@ func (m Modifiers) Contain(m2 Modifiers) bool {
|
|||||||
return m&m2 == m2
|
return m&m2 == m2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FocusCmd requests to set or clear the keyboard focus.
|
||||||
|
type FocusCmd struct {
|
||||||
|
// Tag is the new focus. The focus is cleared if Tag is nil, or if Tag
|
||||||
|
// has no InputOp in the same frame.
|
||||||
|
Tag event.Tag
|
||||||
|
}
|
||||||
|
|
||||||
func (k Set) Contains(name string, mods Modifiers) bool {
|
func (k Set) Contains(name string, mods Modifiers) bool {
|
||||||
ks := string(k)
|
ks := string(k)
|
||||||
for len(ks) > 0 {
|
for len(ks) > 0 {
|
||||||
@@ -346,11 +345,6 @@ func (h SoftKeyboardOp) Add(o *op.Ops) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h FocusOp) Add(o *op.Ops) {
|
|
||||||
data := ops.Write1(&o.Internal, ops.TypeKeyFocusLen, h.Tag)
|
|
||||||
data[0] = byte(ops.TypeKeyFocus)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SnippetOp) Add(o *op.Ops) {
|
func (s SnippetOp) Add(o *op.Ops) {
|
||||||
data := ops.Write2String(&o.Internal, ops.TypeSnippetLen, s.Tag, s.Text)
|
data := ops.Write2String(&o.Internal, ops.TypeSnippetLen, s.Tag, s.Text)
|
||||||
data[0] = byte(ops.TypeSnippet)
|
data[0] = byte(ops.TypeSnippet)
|
||||||
@@ -377,6 +371,8 @@ func (FocusEvent) ImplementsEvent() {}
|
|||||||
func (SnippetEvent) ImplementsEvent() {}
|
func (SnippetEvent) ImplementsEvent() {}
|
||||||
func (SelectionEvent) ImplementsEvent() {}
|
func (SelectionEvent) ImplementsEvent() {}
|
||||||
|
|
||||||
|
func (FocusCmd) ImplementsCommand() {}
|
||||||
|
|
||||||
func (m Modifiers) String() string {
|
func (m Modifiers) String() string {
|
||||||
var strs []string
|
var strs []string
|
||||||
if m.Contain(ModCtrl) {
|
if m.Contain(ModCtrl) {
|
||||||
|
|||||||
+2
-2
@@ -122,7 +122,7 @@ func (b *Clickable) Update(gtx layout.Context) []Click {
|
|||||||
b.focused = false
|
b.focused = false
|
||||||
}
|
}
|
||||||
if b.requestFocus {
|
if b.requestFocus {
|
||||||
key.FocusOp{Tag: &b.keyTag}.Add(gtx.Ops)
|
gtx.Queue(key.FocusCmd{Tag: &b.keyTag})
|
||||||
b.requestFocus = false
|
b.requestFocus = false
|
||||||
}
|
}
|
||||||
for len(b.history) > 0 {
|
for len(b.history) > 0 {
|
||||||
@@ -159,7 +159,7 @@ func (b *Clickable) Update(gtx layout.Context) []Click {
|
|||||||
}
|
}
|
||||||
case gesture.KindPress:
|
case gesture.KindPress:
|
||||||
if e.Source == pointer.Mouse {
|
if e.Source == pointer.Mouse {
|
||||||
key.FocusOp{Tag: &b.keyTag}.Add(gtx.Ops)
|
gtx.Queue(key.FocusCmd{Tag: &b.keyTag})
|
||||||
}
|
}
|
||||||
b.history = append(b.history, Press{
|
b.history = append(b.history, Press{
|
||||||
Position: e.Position,
|
Position: e.Position,
|
||||||
|
|||||||
+1
-1
@@ -645,7 +645,7 @@ func (e *Editor) layout(gtx layout.Context, textMaterial, selectMaterial op.Call
|
|||||||
}
|
}
|
||||||
key.InputOp{Tag: &e.eventKey, Hint: e.InputHint, Keys: keys}.Add(gtx.Ops)
|
key.InputOp{Tag: &e.eventKey, Hint: e.InputHint, Keys: keys}.Add(gtx.Ops)
|
||||||
if e.requestFocus {
|
if e.requestFocus {
|
||||||
key.FocusOp{Tag: &e.eventKey}.Add(gtx.Ops)
|
gtx.Queue(key.FocusCmd{Tag: &e.eventKey})
|
||||||
key.SoftKeyboardOp{Show: true}.Add(gtx.Ops)
|
key.SoftKeyboardOp{Show: true}.Add(gtx.Ops)
|
||||||
}
|
}
|
||||||
e.requestFocus = false
|
e.requestFocus = false
|
||||||
|
|||||||
+1
-1
@@ -50,7 +50,7 @@ func (e *Enum) Update(gtx layout.Context) bool {
|
|||||||
switch ev.Kind {
|
switch ev.Kind {
|
||||||
case gesture.KindPress:
|
case gesture.KindPress:
|
||||||
if ev.Source == pointer.Mouse {
|
if ev.Source == pointer.Mouse {
|
||||||
key.FocusOp{Tag: &state.tag}.Add(gtx.Ops)
|
gtx.Queue(key.FocusCmd{Tag: &state.tag})
|
||||||
}
|
}
|
||||||
case gesture.KindClick:
|
case gesture.KindClick:
|
||||||
if state.key != e.Value {
|
if state.key != e.Value {
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ func (l *Selectable) Layout(gtx layout.Context, lt *text.Shaper, font font.Font,
|
|||||||
}
|
}
|
||||||
key.InputOp{Tag: l, Keys: keys}.Add(gtx.Ops)
|
key.InputOp{Tag: l, Keys: keys}.Add(gtx.Ops)
|
||||||
if l.requestFocus {
|
if l.requestFocus {
|
||||||
key.FocusOp{Tag: l}.Add(gtx.Ops)
|
gtx.Queue(key.FocusCmd{Tag: l})
|
||||||
key.SoftKeyboardOp{Show: true}.Add(gtx.Ops)
|
key.SoftKeyboardOp{Show: true}.Add(gtx.Ops)
|
||||||
}
|
}
|
||||||
l.requestFocus = false
|
l.requestFocus = false
|
||||||
|
|||||||
Reference in New Issue
Block a user