aboutsummaryrefslogtreecommitdiff
path: root/vendor/gioui.org/io/router/router.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gioui.org/io/router/router.go')
-rw-r--r--vendor/gioui.org/io/router/router.go270
1 files changed, 250 insertions, 20 deletions
diff --git a/vendor/gioui.org/io/router/router.go b/vendor/gioui.org/io/router/router.go
index d24d214..a02f0f6 100644
--- a/vendor/gioui.org/io/router/router.go
+++ b/vendor/gioui.org/io/router/router.go
@@ -12,22 +12,37 @@ package router
import (
"encoding/binary"
+ "image"
+ "io"
+ "strings"
"time"
- "gioui.org/internal/opconst"
+ "gioui.org/f32"
"gioui.org/internal/ops"
+ "gioui.org/io/clipboard"
"gioui.org/io/event"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/io/profile"
+ "gioui.org/io/semantic"
+ "gioui.org/io/transfer"
"gioui.org/op"
)
// Router is a Queue implementation that routes events
// to handlers declared in operation lists.
type Router struct {
- pqueue pointerQueue
- kqueue keyQueue
+ savedTrans []f32.Affine2D
+ transStack []f32.Affine2D
+ pointer struct {
+ queue pointerQueue
+ collector pointerCollector
+ }
+ key struct {
+ queue keyQueue
+ collector keyCollector
+ }
+ cqueue clipboardQueue
handlers handlerEvents
@@ -38,11 +53,44 @@ type Router struct {
wakeupTime time.Time
// ProfileOp summary.
- profiling bool
profHandlers map[event.Tag]struct{}
profile profile.Event
}
+// SemanticNode represents a node in the tree describing the components
+// contained in a frame.
+type SemanticNode struct {
+ ID SemanticID
+ ParentID SemanticID
+ Children []SemanticNode
+ Desc SemanticDesc
+
+ areaIdx int
+}
+
+// SemanticDesc provides a semantic description of a UI component.
+type SemanticDesc struct {
+ Class semantic.ClassOp
+ Description string
+ Label string
+ Selected bool
+ Disabled bool
+ Gestures SemanticGestures
+ Bounds f32.Rectangle
+}
+
+// SemanticGestures is a bit-set of supported gestures.
+type SemanticGestures int
+
+const (
+ ClickGesture SemanticGestures = 1 << iota
+)
+
+// SemanticID uniquely identifies a SemanticDescription.
+//
+// By convention, the zero value denotes the non-existent ID.
+type SemanticID uint64
+
type handlerEvents struct {
handlers map[event.Tag][]event.Event
hadEvents bool
@@ -61,33 +109,39 @@ func (q *Router) Events(k event.Tag) []event.Event {
// Frame replaces the declared handlers from the supplied
// operation list. The text input state, wakeup time and whether
// there are active profile handlers is also saved.
-func (q *Router) Frame(ops *op.Ops) {
+func (q *Router) Frame(frame *op.Ops) {
q.handlers.Clear()
q.wakeup = false
- q.profiling = false
for k := range q.profHandlers {
delete(q.profHandlers, k)
}
+ var ops *ops.Ops
+ if frame != nil {
+ ops = &frame.Internal
+ }
q.reader.Reset(ops)
q.collect()
- q.pqueue.Frame(ops, &q.handlers)
- q.kqueue.Frame(ops, &q.handlers)
+ q.pointer.queue.Frame(&q.handlers)
+ q.key.queue.Frame(&q.handlers, q.key.collector)
if q.handlers.HadEvents() {
q.wakeup = true
q.wakeupTime = time.Time{}
}
}
-func (q *Router) Add(events ...event.Event) bool {
+// Queue an event and report whether at least one handler had an event queued.
+func (q *Router) Queue(events ...event.Event) bool {
for _, e := range events {
switch e := e.(type) {
case profile.Event:
q.profile = e
case pointer.Event:
- q.pqueue.Push(e, &q.handlers)
+ q.pointer.queue.Push(e, &q.handlers)
case key.EditEvent, key.Event, key.FocusEvent:
- q.kqueue.Push(e, &q.handlers)
+ q.key.queue.Push(e, &q.handlers)
+ case clipboard.Event:
+ q.cqueue.Push(e, &q.handlers)
}
}
return q.handlers.HadEvents()
@@ -96,25 +150,189 @@ func (q *Router) Add(events ...event.Event) bool {
// TextInputState returns the input state from the most recent
// call to Frame.
func (q *Router) TextInputState() TextInputState {
- return q.kqueue.InputState()
+ return q.key.queue.InputState()
+}
+
+// TextInputHint returns the input mode from the most recent key.InputOp.
+func (q *Router) TextInputHint() (key.InputHint, bool) {
+ return q.key.queue.InputHint()
+}
+
+// WriteClipboard returns the most recent text to be copied
+// to the clipboard, if any.
+func (q *Router) WriteClipboard() (string, bool) {
+ return q.cqueue.WriteClipboard()
+}
+
+// ReadClipboard reports if any new handler is waiting
+// to read the clipboard.
+func (q *Router) ReadClipboard() bool {
+ return q.cqueue.ReadClipboard()
+}
+
+// Cursor returns the last cursor set.
+func (q *Router) Cursor() pointer.CursorName {
+ return q.pointer.queue.cursor
+}
+
+// SemanticAt returns the first semantic description under pos, if any.
+func (q *Router) SemanticAt(pos f32.Point) (SemanticID, bool) {
+ return q.pointer.queue.SemanticAt(pos)
+}
+
+// AppendSemantics appends the semantic tree to nodes, and returns the result.
+// The root node is the first added.
+func (q *Router) AppendSemantics(nodes []SemanticNode) []SemanticNode {
+ q.pointer.collector.q = &q.pointer.queue
+ q.pointer.collector.ensureRoot()
+ return q.pointer.queue.AppendSemantics(nodes)
}
func (q *Router) collect() {
+ q.transStack = q.transStack[:0]
+ pc := &q.pointer.collector
+ pc.q = &q.pointer.queue
+ pc.reset()
+ kc := &q.key.collector
+ *kc = keyCollector{q: &q.key.queue}
+ q.key.queue.Reset()
+ var t f32.Affine2D
for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
- switch opconst.OpType(encOp.Data[0]) {
- case opconst.TypeInvalidate:
+ switch ops.OpType(encOp.Data[0]) {
+ case ops.TypeInvalidate:
op := decodeInvalidateOp(encOp.Data)
if !q.wakeup || op.At.Before(q.wakeupTime) {
q.wakeup = true
q.wakeupTime = op.At
}
- case opconst.TypeProfile:
+ case ops.TypeProfile:
op := decodeProfileOp(encOp.Data, encOp.Refs)
if q.profHandlers == nil {
q.profHandlers = make(map[event.Tag]struct{})
}
- q.profiling = true
q.profHandlers[op.Tag] = struct{}{}
+ case ops.TypeClipboardRead:
+ q.cqueue.ProcessReadClipboard(encOp.Refs)
+ case ops.TypeClipboardWrite:
+ q.cqueue.ProcessWriteClipboard(encOp.Refs)
+ case ops.TypeSave:
+ id := ops.DecodeSave(encOp.Data)
+ if extra := id - len(q.savedTrans) + 1; extra > 0 {
+ q.savedTrans = append(q.savedTrans, make([]f32.Affine2D, extra)...)
+ }
+ q.savedTrans[id] = t
+ case ops.TypeLoad:
+ id := ops.DecodeLoad(encOp.Data)
+ t = q.savedTrans[id]
+ pc.resetState()
+ pc.setTrans(t)
+
+ case ops.TypeClip:
+ var op ops.ClipOp
+ op.Decode(encOp.Data)
+ pc.clip(op)
+ case ops.TypePopClip:
+ pc.popArea()
+ case ops.TypeTransform:
+ t2, push := ops.DecodeTransform(encOp.Data)
+ if push {
+ q.transStack = append(q.transStack, t)
+ }
+ t = t.Mul(t2)
+ pc.setTrans(t)
+ case ops.TypePopTransform:
+ n := len(q.transStack)
+ t = q.transStack[n-1]
+ q.transStack = q.transStack[:n-1]
+ pc.setTrans(t)
+
+ // Pointer ops.
+ case ops.TypePass:
+ pc.pass()
+ case ops.TypePopPass:
+ pc.popPass()
+ case ops.TypePointerInput:
+ bo := binary.LittleEndian
+ op := pointer.InputOp{
+ Tag: encOp.Refs[0].(event.Tag),
+ Grab: encOp.Data[1] != 0,
+ Types: pointer.Type(bo.Uint16(encOp.Data[2:])),
+ ScrollBounds: image.Rectangle{
+ Min: image.Point{
+ X: int(int32(bo.Uint32(encOp.Data[4:]))),
+ Y: int(int32(bo.Uint32(encOp.Data[8:]))),
+ },
+ Max: image.Point{
+ X: int(int32(bo.Uint32(encOp.Data[12:]))),
+ Y: int(int32(bo.Uint32(encOp.Data[16:]))),
+ },
+ },
+ }
+ pc.inputOp(op, &q.handlers)
+ case ops.TypeCursor:
+ name := encOp.Refs[0].(pointer.CursorName)
+ pc.cursor(name)
+ case ops.TypeSource:
+ op := transfer.SourceOp{
+ Tag: encOp.Refs[0].(event.Tag),
+ Type: encOp.Refs[1].(string),
+ }
+ pc.sourceOp(op, &q.handlers)
+ case ops.TypeTarget:
+ op := transfer.TargetOp{
+ Tag: encOp.Refs[0].(event.Tag),
+ Type: encOp.Refs[1].(string),
+ }
+ pc.targetOp(op, &q.handlers)
+ case ops.TypeOffer:
+ op := transfer.OfferOp{
+ Tag: encOp.Refs[0].(event.Tag),
+ Type: encOp.Refs[1].(string),
+ Data: encOp.Refs[2].(io.ReadCloser),
+ }
+ pc.offerOp(op, &q.handlers)
+
+ // Key ops.
+ case ops.TypeKeyFocus:
+ tag, _ := encOp.Refs[0].(event.Tag)
+ op := key.FocusOp{
+ Tag: tag,
+ }
+ kc.focusOp(op.Tag)
+ case ops.TypeKeySoftKeyboard:
+ op := key.SoftKeyboardOp{
+ Show: encOp.Data[1] != 0,
+ }
+ kc.softKeyboard(op.Show)
+ case ops.TypeKeyInput:
+ op := key.InputOp{
+ Tag: encOp.Refs[0].(event.Tag),
+ Hint: key.InputHint(encOp.Data[1]),
+ }
+ kc.inputOp(op)
+
+ // Semantic ops.
+ case ops.TypeSemanticLabel:
+ lbl := encOp.Refs[0].(*string)
+ pc.semanticLabel(*lbl)
+ case ops.TypeSemanticDesc:
+ desc := encOp.Refs[0].(*string)
+ pc.semanticDesc(*desc)
+ case ops.TypeSemanticClass:
+ class := semantic.ClassOp(encOp.Data[1])
+ pc.semanticClass(class)
+ case ops.TypeSemanticSelected:
+ if encOp.Data[1] != 0 {
+ pc.semanticSelected(true)
+ } else {
+ pc.semanticSelected(false)
+ }
+ case ops.TypeSemanticDisabled:
+ if encOp.Data[1] != 0 {
+ pc.semanticDisabled(true)
+ } else {
+ pc.semanticDisabled(false)
+ }
}
}
}
@@ -122,7 +340,7 @@ func (q *Router) collect() {
// Profiling reports whether there was profile handlers in the
// most recent Frame call.
func (q *Router) Profiling() bool {
- return q.profiling
+ return len(q.profHandlers) > 0
}
// WakeupTime returns the most recent time for doing another frame,
@@ -137,9 +355,13 @@ func (h *handlerEvents) init() {
}
}
-func (h *handlerEvents) Add(k event.Tag, e event.Event) {
+func (h *handlerEvents) AddNoRedraw(k event.Tag, e event.Event) {
h.init()
h.handlers[k] = append(h.handlers[k], e)
+}
+
+func (h *handlerEvents) Add(k event.Tag, e event.Event) {
+ h.AddNoRedraw(k, e)
h.hadEvents = true
}
@@ -174,7 +396,7 @@ func (h *handlerEvents) Clear() {
}
func decodeProfileOp(d []byte, refs []interface{}) profile.Op {
- if opconst.OpType(d[0]) != opconst.TypeProfile {
+ if ops.OpType(d[0]) != ops.TypeProfile {
panic("invalid op")
}
return profile.Op{
@@ -184,7 +406,7 @@ func decodeProfileOp(d []byte, refs []interface{}) profile.Op {
func decodeInvalidateOp(d []byte) op.InvalidateOp {
bo := binary.LittleEndian
- if opconst.OpType(d[0]) != opconst.TypeInvalidate {
+ if ops.OpType(d[0]) != ops.TypeInvalidate {
panic("invalid op")
}
var o op.InvalidateOp
@@ -193,3 +415,11 @@ func decodeInvalidateOp(d []byte) op.InvalidateOp {
}
return o
}
+
+func (s SemanticGestures) String() string {
+ var gestures []string
+ if s&ClickGesture != 0 {
+ gestures = append(gestures, "Click")
+ }
+ return strings.Join(gestures, ",")
+}