diff options
Diffstat (limited to 'vendor/gioui.org/io/router/router.go')
-rw-r--r-- | vendor/gioui.org/io/router/router.go | 270 |
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, ",") +} |