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