io/pointer: re-introduce PassOp

A previous change merged PassOp with AreaOp under the assumption that
the pass mode would be set on a particular area. That assumption turns
out not to hold, so this change brings back PassOp as an independent
stack operation.

This is an API change: replace AreaOp{Pass: true} with a separate
pointer.PassOp operation.

Fixes gio#288

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-10-09 16:27:24 +02:00
parent 61a3395879
commit 960f3068a1
5 changed files with 58 additions and 29 deletions
+13 -2
View File
@@ -23,7 +23,7 @@ type Ops struct {
nextStateID int
macroStack stack
stacks [3]stack
stacks [4]stack
}
type OpType byte
@@ -45,6 +45,8 @@ const (
TypeLinearGradient
TypeArea
TypePopArea
TypePass
TypePopPass
TypePointerInput
TypeClipboardRead
TypeClipboardWrite
@@ -88,6 +90,7 @@ const (
ClipStack StackKind = iota
AreaStack
TransStack
PassStack
)
const (
@@ -102,8 +105,10 @@ const (
TypePaintLen = 1
TypeColorLen = 1 + 4
TypeLinearGradientLen = 1 + 8*2 + 4*2
TypeAreaLen = 1 + 1 + 1 + 4*4
TypeAreaLen = 1 + 1 + 4*4
TypePopAreaLen = 1
TypePassLen = 1
TypePopPassLen = 1
TypePointerInputLen = 1 + 1 + 1 + 2*4 + 2*4
TypeClipboardReadLen = 1
TypeClipboardWriteLen = 1
@@ -313,6 +318,8 @@ func (t OpType) Size() int {
TypeLinearGradientLen,
TypeAreaLen,
TypePopAreaLen,
TypePassLen,
TypePopPassLen,
TypePointerInputLen,
TypeClipboardReadLen,
TypeClipboardWriteLen,
@@ -370,6 +377,10 @@ func (t OpType) String() string {
return "Area"
case TypePopArea:
return "PopArea"
case TypePass:
return "Pass"
case TypePopPass:
return "PopPass"
case TypePointerInput:
return "PointerInput"
case TypeClipboardRead:
+32 -14
View File
@@ -42,14 +42,9 @@ type Event struct {
Modifiers key.Modifiers
}
// AreaOp updates the hit area to the intersection of the current
// hit area and the area. The area is transformed before applying
// it.
// AreaOp pushes the current hit area to the stack and updates it to the
// intersection of the current hit area and the transformed area.
type AreaOp struct {
// PassThrough areas and their children don't block events to siblings
// them.
PassThrough bool
kind areaKind
rect image.Rectangle
}
@@ -61,6 +56,18 @@ type AreaStack struct {
macroID int
}
// PassOp sets the pass-through mode. AreaOps added while the pass-through
// mode is set don't block events to siblings.
type PassOp struct {
}
// PassStack represents a PassOp on the pass stack.
type PassStack struct {
ops *ops.Ops
id ops.StackID
macroID int
}
// CursorNameOp sets the cursor for the current area.
type CursorNameOp struct {
Name CursorName
@@ -204,14 +211,11 @@ func (a AreaOp) add(o *op.Ops, push bool) {
data := o.Internal.Write(ops.TypeAreaLen)
data[0] = byte(ops.TypeArea)
data[1] = byte(a.kind)
if a.PassThrough {
data[2] = 1
}
bo := binary.LittleEndian
bo.PutUint32(data[3:], uint32(a.rect.Min.X))
bo.PutUint32(data[7:], uint32(a.rect.Min.Y))
bo.PutUint32(data[11:], uint32(a.rect.Max.X))
bo.PutUint32(data[15:], uint32(a.rect.Max.Y))
bo.PutUint32(data[2:], uint32(a.rect.Min.X))
bo.PutUint32(data[6:], uint32(a.rect.Min.Y))
bo.PutUint32(data[10:], uint32(a.rect.Max.X))
bo.PutUint32(data[14:], uint32(a.rect.Max.Y))
}
func (o AreaStack) Pop() {
@@ -220,6 +224,20 @@ func (o AreaStack) Pop() {
data[0] = byte(ops.TypePopArea)
}
// Push the current pass mode to the pass stack and set the pass mode.
func (p PassOp) Push(o *op.Ops) PassStack {
id, mid := o.Internal.PushOp(ops.PassStack)
data := o.Internal.Write(ops.TypePassLen)
data[0] = byte(ops.TypePass)
return PassStack{ops: &o.Internal, id: id, macroID: mid}
}
func (p PassStack) Pop() {
p.ops.PopOp(ops.PassStack, p.id, p.macroID)
data := p.ops.Write(ops.TypePopPassLen)
data[0] = byte(ops.TypePopPass)
}
func (op CursorNameOp) Add(o *op.Ops) {
data := o.Internal.Write1(ops.TypeCursorLen, op.Name)
data[0] = byte(ops.TypeCursor)
+10 -8
View File
@@ -64,7 +64,6 @@ type pointerHandler struct {
}
type areaOp struct {
pass bool
kind areaKind
rect f32.Rectangle
}
@@ -82,7 +81,7 @@ type areaKind uint8
type collectState struct {
t f32.Affine2D
node int
pass bool
pass int
}
const (
@@ -122,7 +121,7 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents) {
n := q.hitTree[i]
area = n.area
}
q.areas = append(q.areas, areaNode{trans: state.t, next: area, area: op, pass: op.pass})
q.areas = append(q.areas, areaNode{trans: state.t, next: area, area: op, pass: state.pass > 0})
q.nodeStack = append(q.nodeStack, state.node)
q.hitTree = append(q.hitTree, hitNode{
next: state.node,
@@ -133,6 +132,10 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents) {
n := len(q.nodeStack)
state.node = q.nodeStack[n-1]
q.nodeStack = q.nodeStack[:n-1]
case ops.TypePass:
state.pass++
case ops.TypePopPass:
state.pass--
case ops.TypeTransform:
dop, push := ops.DecodeTransform(encOp.Data)
if push {
@@ -480,18 +483,17 @@ func (op *areaOp) Decode(d []byte) {
}
rect := f32.Rectangle{
Min: f32.Point{
X: opDecodeFloat32(d[3:]),
Y: opDecodeFloat32(d[7:]),
X: opDecodeFloat32(d[2:]),
Y: opDecodeFloat32(d[6:]),
},
Max: f32.Point{
X: opDecodeFloat32(d[11:]),
Y: opDecodeFloat32(d[15:]),
X: opDecodeFloat32(d[10:]),
Y: opDecodeFloat32(d[14:]),
},
}
*op = areaOp{
kind: areaKind(d[1]),
rect: rect,
pass: d[2] != 0,
}
}
+1 -3
View File
@@ -29,11 +29,9 @@ func ExampleClickable_passthrough() {
// widget lays out two buttons on top of each other.
widget := func() {
button1.Layout(gtx)
area := pointer.Rect(image.Rectangle{Max: gtx.Constraints.Max})
// button2 completely covers button1, but pass-through allows pointer
// events to pass through to button1.
area.PassThrough = true
defer area.Push(gtx.Ops).Pop()
defer pointer.PassOp{}.Push(gtx.Ops).Pop()
button2.Layout(gtx)
}
+2 -2
View File
@@ -161,7 +161,7 @@ func (s ScrollbarStyle) layout(gtx layout.Context, axis layout.Axis, viewportSta
// Stack a normal clickable area on top of the draggable area
// to capture non-dragging clicks.
pointerArea.PassThrough = true
defer pointer.PassOp{}.Push(gtx.Ops).Pop()
defer pointerArea.Push(gtx.Ops).Pop()
s.Scrollbar.AddTrack(gtx.Ops)
@@ -206,7 +206,7 @@ func (s ScrollbarStyle) layout(gtx layout.Context, axis layout.Axis, viewportSta
// Add the indicator pointer hit area.
area := pointer.Rect(image.Rectangle{Max: indicatorDims})
area.PassThrough = true
defer pointer.PassOp{}.Push(gtx.Ops).Pop()
defer area.Push(gtx.Ops).Pop()
s.Scrollbar.AddIndicator(gtx.Ops)