ui: let OpsReader keep track of references

Instead of exposing the entire reference slice, return the relevant
references from Next.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-06-01 20:48:36 +02:00
parent 5966aab77e
commit 0d2cffe196
11 changed files with 166 additions and 104 deletions
+5 -5
View File
@@ -667,7 +667,7 @@ func (d *drawOps) newPathOp() *pathOp {
func (d *drawOps) collectOps(r *ui.OpsReader, state drawState) int {
loop:
for {
data, ok := r.Decode()
data, refs, ok := r.Decode()
if !ok {
break
}
@@ -678,7 +678,7 @@ loop:
state.t = state.t.Mul(op.Transform)
case ops.TypeClip:
var op gdraw.OpClip
op.Decode(data, r.Refs)
op.Decode(data, refs)
if op.Path == nil {
state.clip = f32.Rectangle{}
continue
@@ -702,17 +702,17 @@ loop:
}
case ops.TypeColor:
var op gdraw.OpColor
op.Decode(data, r.Refs)
op.Decode(data, refs)
state.img = nil
state.color = op.Col
case ops.TypeImage:
var op gdraw.OpImage
op.Decode(data, r.Refs)
op.Decode(data, refs)
state.img = op.Img
state.imgRect = op.Rect
case ops.TypeDraw:
var op gdraw.OpDraw
op.Decode(data, r.Refs)
op.Decode(data, refs)
off := state.t.Transform(f32.Point{})
clip := state.clip.Intersect(op.Rect.Add(off))
if clip.Empty() {
+1 -1
View File
@@ -156,7 +156,7 @@ func collectRedraws(r *ui.OpsReader) (time.Time, bool) {
var t time.Time
redraw := false
for {
data, ok := r.Decode()
data, _, ok := r.Decode()
if !ok {
break
}
+12 -15
View File
@@ -31,13 +31,11 @@ func (i OpImage) Add(o *ui.Ops) {
data := make([]byte, ops.TypeImageLen)
data[0] = byte(ops.TypeImage)
bo := binary.LittleEndian
ref := o.Ref(i.Img)
bo.PutUint32(data[1:], uint32(ref))
bo.PutUint32(data[5:], uint32(i.Rect.Min.X))
bo.PutUint32(data[9:], uint32(i.Rect.Min.Y))
bo.PutUint32(data[13:], uint32(i.Rect.Max.X))
bo.PutUint32(data[17:], uint32(i.Rect.Max.Y))
o.Write(data)
bo.PutUint32(data[1:], uint32(i.Rect.Min.X))
bo.PutUint32(data[5:], uint32(i.Rect.Min.Y))
bo.PutUint32(data[9:], uint32(i.Rect.Max.X))
bo.PutUint32(data[13:], uint32(i.Rect.Max.Y))
o.Write(data, []interface{}{i.Img})
}
func (i *OpImage) Decode(data []byte, refs []interface{}) {
@@ -45,19 +43,18 @@ func (i *OpImage) Decode(data []byte, refs []interface{}) {
if ops.OpType(data[0]) != ops.TypeImage {
panic("invalid op")
}
ref := int(bo.Uint32(data[1:]))
sr := image.Rectangle{
Min: image.Point{
X: int(bo.Uint32(data[5:])),
Y: int(bo.Uint32(data[9:])),
X: int(bo.Uint32(data[1:])),
Y: int(bo.Uint32(data[5:])),
},
Max: image.Point{
X: int(bo.Uint32(data[13:])),
Y: int(bo.Uint32(data[17:])),
X: int(bo.Uint32(data[9:])),
Y: int(bo.Uint32(data[13:])),
},
}
*i = OpImage{
Img: refs[ref].(image.Image),
Img: refs[0].(image.Image),
Rect: sr,
}
}
@@ -69,7 +66,7 @@ func (c OpColor) Add(o *ui.Ops) {
data[2] = c.Col.G
data[3] = c.Col.B
data[4] = c.Col.A
o.Write(data)
o.Write(data, nil)
}
func (c *OpColor) Decode(data []byte, refs []interface{}) {
@@ -94,7 +91,7 @@ func (d OpDraw) Add(o *ui.Ops) {
bo.PutUint32(data[5:], math.Float32bits(d.Rect.Min.Y))
bo.PutUint32(data[9:], math.Float32bits(d.Rect.Max.X))
bo.PutUint32(data[13:], math.Float32bits(d.Rect.Max.Y))
o.Write(data)
o.Write(data, nil)
}
func (d *OpDraw) Decode(data []byte, refs []interface{}) {
+2 -8
View File
@@ -3,7 +3,6 @@
package draw
import (
"encoding/binary"
"math"
"gioui.org/ui"
@@ -37,20 +36,15 @@ func (p *Path) Data() interface{} {
func (c OpClip) Add(o *ui.Ops) {
data := make([]byte, ops.TypeClipLen)
data[0] = byte(ops.TypeClip)
bo := binary.LittleEndian
ref := o.Ref(c.Path)
bo.PutUint32(data[1:], uint32(ref))
o.Write(data)
o.Write(data, []interface{}{c.Path})
}
func (c *OpClip) Decode(d []byte, refs []interface{}) {
bo := binary.LittleEndian
if ops.OpType(d[0]) != ops.TypeClip {
panic("invalid op")
}
ref := int(bo.Uint32(d[1:]))
*c = OpClip{
Path: refs[ref].(*Path),
Path: refs[0].(*Path),
}
}
+25 -7
View File
@@ -2,8 +2,11 @@ package ops
type OpType byte
// Start at a high number for easier debugging.
const FirstOpIndex = 200
const (
TypeBlockDef OpType = iota
TypeBlockDef OpType = iota + FirstOpIndex
TypeBlock
TypeTransform
TypeLayer
@@ -20,18 +23,33 @@ const (
)
const (
TypeBlockDefLen = 1 + 4
TypeBlockLen = 1 + 4
TypeBlockDefLen = 1 + 4 + 4
TypeBlockLen = 1 + 4 + 4
TypeTransformLen = 1 + 4*2
TypeLayerLen = 1
TypeRedrawLen = 1 + 8
TypeClipLen = 1 + 4
TypeImageLen = 1 + 4 + 4*4
TypeClipLen = 1
TypeImageLen = 1 + 4*4
TypeDrawLen = 1 + 4*4
TypeColorLen = 1 + 4
TypePointerHandlerLen = 1 + 4 + 4 + 1
TypeKeyHandlerLen = 1 + 4 + 1
TypePointerHandlerLen = 1 + 1
TypeKeyHandlerLen = 1 + 1
TypeHideInputLen = 1
TypePushLen = 1
TypePopLen = 1
TypeBlockDefRefs = 0
TypeBlockRefs = 0
TypeTransformRefs = 0
TypeLayerRefs = 0
TypeRedrawRefs = 0
TypeClipRefs = 1
TypeImageRefs = 1
TypeDrawRefs = 0
TypeColorRefs = 0
TypePointerHandlerRefs = 2
TypeKeyHandlerRefs = 1
TypeHideInputRefs = 0
TypePushRefs = 0
TypePopRefs = 0
)
+3 -9
View File
@@ -3,8 +3,6 @@
package key
import (
"encoding/binary"
"gioui.org/ui"
"gioui.org/ui/internal/ops"
)
@@ -73,30 +71,26 @@ const (
func (h OpHandler) Add(o *ui.Ops) {
data := make([]byte, ops.TypeKeyHandlerLen)
data[0] = byte(ops.TypeKeyHandler)
bo := binary.LittleEndian
if h.Focus {
data[1] = 1
}
bo.PutUint32(data[2:], uint32(o.Ref(h.Key)))
o.Write(data)
o.Write(data, []interface{}{h.Key})
}
func (h *OpHandler) Decode(d []byte, refs []interface{}) {
bo := binary.LittleEndian
if ops.OpType(d[0]) != ops.TypeKeyHandler {
panic("invalid op")
}
key := int(bo.Uint32(d[2:]))
*h = OpHandler{
Focus: d[1] != 0,
Key: refs[key].(Key),
Key: refs[0].(Key),
}
}
func (h OpHideInput) Add(o *ui.Ops) {
data := make([]byte, ops.TypeHideInputLen)
data[0] = byte(ops.TypeHideInput)
o.Write(data)
o.Write(data, nil)
}
func (Edit) ImplementsEvent() {}
+2 -2
View File
@@ -80,14 +80,14 @@ func resolveFocus(r *ui.OpsReader, focus Key) (Key, listenerPriority, bool) {
var hide bool
loop:
for {
data, ok := r.Decode()
data, refs, ok := r.Decode()
if !ok {
break
}
switch ops.OpType(data[0]) {
case ops.TypeKeyHandler:
var op OpHandler
op.Decode(data, r.Refs)
op.Decode(data, refs)
var newPri listenerPriority
switch {
case op.Focus:
+108 -42
View File
@@ -9,7 +9,7 @@ import (
// Ops holds a list of serialized Ops.
type Ops struct {
// Stack of block start indices.
stack []int
stack []pc
// Serialized ops.
data []byte
// Op references.
@@ -17,17 +17,22 @@ type Ops struct {
}
type OpsReader struct {
pc int
pc pc
stack []block
Refs []interface{}
refs []interface{}
data []byte
pseudoOp [1]byte
}
type block struct {
retPC int
endPC int
retPC pc
endPC pc
}
type pc struct {
data int
refs int
}
var typeLengths = [...]int{
@@ -47,16 +52,51 @@ var typeLengths = [...]int{
ops.TypePopLen,
}
var refLengths = [...]int{
ops.TypeBlockDefRefs,
ops.TypeBlockRefs,
ops.TypeTransformRefs,
ops.TypeLayerRefs,
ops.TypeRedrawRefs,
ops.TypeClipRefs,
ops.TypeImageRefs,
ops.TypeDrawRefs,
ops.TypeColorRefs,
ops.TypePointerHandlerRefs,
ops.TypeKeyHandlerRefs,
ops.TypeHideInputRefs,
ops.TypePushRefs,
ops.TypePopRefs,
}
type OpBlock struct {
idx int
pc pc
}
type opBlockDef struct {
endpc pc
}
// Begin a block of ops.
func (o *Ops) Begin() {
o.stack = append(o.stack, o.Size())
data := make([]byte, ops.TypeBlockDefLen)
data[0] = byte(ops.TypeBlockDef)
o.Write(data)
o.stack = append(o.stack, o.pc())
// Make room for a block definition. Filled out in End.
o.data = append(o.data, make([]byte, ops.TypeBlockDefLen)...)
}
func (op *opBlockDef) decode(data []byte) {
if ops.OpType(data[0]) != ops.TypeBlockDef {
panic("invalid op")
}
bo := binary.LittleEndian
dataIdx := int(bo.Uint32(data[1:]))
refsIdx := int(bo.Uint32(data[5:]))
*op = opBlockDef{
endpc: pc{
data: dataIdx,
refs: refsIdx,
},
}
}
// End the most recent block and return
@@ -64,9 +104,13 @@ func (o *Ops) Begin() {
func (o *Ops) End() OpBlock {
start := o.stack[len(o.stack)-1]
o.stack = o.stack[:len(o.stack)-1]
blockLen := o.Size() - start
pc := o.pc()
// Write the block header reserved in Begin.
data := o.data[start.data : start.data+ops.TypeBlockDefLen]
data[0] = byte(ops.TypeBlockDef)
bo := binary.LittleEndian
bo.PutUint32(o.data[start+1:], uint32(blockLen))
bo.PutUint32(data[1:], uint32(pc.data))
bo.PutUint32(data[5:], uint32(pc.refs))
return OpBlock{start}
}
@@ -77,41 +121,51 @@ func (o *Ops) Reset() {
o.data = o.data[:0]
}
func (o *Ops) Ref(r interface{}) int {
o.refs = append(o.refs, r)
return len(o.refs) - 1
}
func (o *Ops) Write(op []byte) {
func (o *Ops) Write(op []byte, refs []interface{}) {
o.data = append(o.data, op...)
o.refs = append(o.refs, refs...)
}
// Size returns the length of the serialized Op data.
func (o *Ops) Size() int {
return len(o.data)
func (o *Ops) pc() pc {
return pc{data: len(o.data), refs: len(o.refs)}
}
func (b *OpBlock) decode(data []byte) {
if ops.OpType(data[0]) != ops.TypeBlock {
panic("invalid op")
}
bo := binary.LittleEndian
dataIdx := int(bo.Uint32(data[1:]))
refsIdx := int(bo.Uint32(data[5:]))
*b = OpBlock{
pc: pc{
data: dataIdx,
refs: refsIdx,
},
}
}
func (b OpBlock) Add(o *Ops) {
data := make([]byte, ops.TypeBlockLen)
data[0] = byte(ops.TypeBlock)
bo := binary.LittleEndian
bo.PutUint32(data[1:], uint32(b.idx))
o.Write(data)
bo.PutUint32(data[1:], uint32(b.pc.data))
bo.PutUint32(data[5:], uint32(b.pc.refs))
o.Write(data, nil)
}
// Reset start reading from the op list.
func (r *OpsReader) Reset(ops *Ops) {
r.Refs = ops.refs
r.refs = ops.refs
r.data = ops.data
r.stack = r.stack[:0]
r.pc = 0
r.pc = pc{}
}
func (r *OpsReader) Decode() ([]byte, bool) {
bo := binary.LittleEndian
func (r *OpsReader) Decode() ([]byte, []interface{}, bool) {
for {
if r.pc == len(r.data) {
return nil, false
if r.pc.data == len(r.data) {
return nil, nil, false
}
if len(r.stack) > 0 {
b := r.stack[len(r.stack)-1]
@@ -119,28 +173,40 @@ func (r *OpsReader) Decode() ([]byte, bool) {
r.pc = b.retPC
r.stack = r.stack[:len(r.stack)-1]
r.pseudoOp[0] = byte(ops.TypePop)
return r.pseudoOp[:], true
return r.pseudoOp[:], nil, true
}
}
t := ops.OpType(r.data[r.pc])
n := typeLengths[t]
data := r.data[r.pc : r.pc+n]
t := ops.OpType(r.data[r.pc.data])
n := typeLengths[t-ops.FirstOpIndex]
nrefs := refLengths[t-ops.FirstOpIndex]
data := r.data[r.pc.data : r.pc.data+n]
refs := r.refs[r.pc.refs : r.pc.refs+nrefs]
switch t {
case ops.TypeBlock:
blockIdx := int(bo.Uint32(data[1:]))
if ops.OpType(r.data[blockIdx]) != ops.TypeBlockDef {
var op OpBlock
op.decode(data)
if ops.OpType(r.data[op.pc.data]) != ops.TypeBlockDef {
panic("invalid block reference")
}
blockLen := int(bo.Uint32(r.data[blockIdx+1:]))
r.stack = append(r.stack, block{r.pc + n, blockIdx + blockLen})
r.pc = blockIdx + ops.TypeBlockDefLen
var opDef opBlockDef
opDef.decode(r.data[op.pc.data : op.pc.data+ops.TypeBlockDefLen])
retPC := r.pc
retPC.data += n
retPC.refs += nrefs
r.stack = append(r.stack, block{retPC: retPC, endPC: opDef.endpc})
r.pc = op.pc
r.pc.data += ops.TypeBlockDefLen
r.pc.refs += ops.TypeBlockDefRefs
r.pseudoOp[0] = byte(ops.TypePush)
return r.pseudoOp[:], true
return r.pseudoOp[:], nil, true
case ops.TypeBlockDef:
r.pc += int(bo.Uint32(data[1:]))
var op opBlockDef
op.decode(data)
r.pc = op.endpc
continue
}
r.pc += n
return data, true
r.pc.data += n
r.pc.refs += nrefs
return data, refs, true
}
}
+3 -10
View File
@@ -3,7 +3,6 @@
package pointer
import (
"encoding/binary"
"time"
"gioui.org/ui"
@@ -72,26 +71,20 @@ const (
func (h OpHandler) Add(o *ui.Ops) {
data := make([]byte, ops.TypePointerHandlerLen)
data[0] = byte(ops.TypePointerHandler)
bo := binary.LittleEndian
if h.Grab {
data[1] = 1
}
bo.PutUint32(data[2:], uint32(o.Ref(h.Key)))
bo.PutUint32(data[6:], uint32(o.Ref(h.Area)))
o.Write(data)
o.Write(data, []interface{}{h.Key, h.Area})
}
func (h *OpHandler) Decode(d []byte, refs []interface{}) {
bo := binary.LittleEndian
if ops.OpType(d[0]) != ops.TypePointerHandler {
panic("invalid op")
}
key := int(bo.Uint32(d[2:]))
area := int(bo.Uint32(d[6:]))
*h = OpHandler{
Grab: d[1] != 0,
Key: refs[key].(Key),
Area: refs[area].(Area),
Key: refs[0].(Key),
Area: refs[1].(Area),
}
}
+2 -2
View File
@@ -39,7 +39,7 @@ type handler struct {
func (q *Queue) collectHandlers(r *ui.OpsReader, t ui.Transform, layer int) {
for {
data, ok := r.Decode()
data, refs, ok := r.Decode()
if !ok {
return
}
@@ -57,7 +57,7 @@ func (q *Queue) collectHandlers(r *ui.OpsReader, t ui.Transform, layer int) {
t = t.Mul(op.Transform)
case ops.TypePointerHandler:
var op OpHandler
op.Decode(data, r.Refs)
op.Decode(data, refs)
q.hitTree = append(q.hitTree, hitNode{level: layer, key: op.Key})
h, ok := q.handlers[op.Key]
if !ok {
+3 -3
View File
@@ -77,7 +77,7 @@ func (r OpRedraw) Add(o *Ops) {
bo.PutUint64(data[1:], uint64(nanos))
}
}
o.Write(data)
o.Write(data, nil)
}
func (r *OpRedraw) Decode(d []byte) {
@@ -110,7 +110,7 @@ func (t OpTransform) Add(o *Ops) {
bo := binary.LittleEndian
bo.PutUint32(data[1:], math.Float32bits(t.Transform.offset.X))
bo.PutUint32(data[5:], math.Float32bits(t.Transform.offset.Y))
o.Write(data)
o.Write(data, nil)
}
func (t *OpTransform) Decode(d []byte) {
@@ -129,7 +129,7 @@ func (t *OpTransform) Decode(d []byte) {
func (l OpLayer) Add(o *Ops) {
data := make([]byte, ops.TypeLayerLen)
data[0] = byte(ops.TypeLayer)
o.Write(data)
o.Write(data, nil)
}
func (l *OpLayer) Decode(d []byte) {