diff --git a/cmd/gogio/testdata/red.go b/cmd/gogio/testdata/red.go index 67ef722b..3324d1b3 100644 --- a/cmd/gogio/testdata/red.go +++ b/cmd/gogio/testdata/red.go @@ -124,7 +124,10 @@ func (w *quarterWidget) Layout(gtx layout.Context) layout.Dimensions { pointer.Rect(image.Rectangle{ Max: image.Pt(gtx.Constraints.Max.X, gtx.Constraints.Max.Y), }).Add(gtx.Ops) - pointer.InputOp{Tag: w}.Add(gtx.Ops) + pointer.InputOp{ + Tag: w, + Types: pointer.Press, + }.Add(gtx.Ops) for _, e := range gtx.Events(w) { if e, ok := e.(pointer.Event); ok && e.Type == pointer.Press { diff --git a/gesture/gesture.go b/gesture/gesture.go index d529840c..28aad990 100644 --- a/gesture/gesture.go +++ b/gesture/gesture.go @@ -112,7 +112,10 @@ var touchSlop = unit.Dp(3) // Add the handler to the operation list to receive click events. func (c *Click) Add(ops *op.Ops) { - op := pointer.InputOp{Tag: c} + op := pointer.InputOp{ + Tag: c, + Types: pointer.Press | pointer.Release | pointer.Enter | pointer.Leave, + } op.Add(ops) } @@ -168,7 +171,11 @@ func (c *Click) Events(q event.Queue) []ClickEvent { // Add the handler to the operation list to receive scroll events. func (s *Scroll) Add(ops *op.Ops) { - oph := pointer.InputOp{Tag: s, Grab: s.grab} + oph := pointer.InputOp{ + Tag: s, + Grab: s.grab, + Types: pointer.Press | pointer.Move | pointer.Release | pointer.Scroll, + } oph.Add(ops) if s.flinger.Active() { op.InvalidateOp{}.Add(ops) diff --git a/internal/opconst/ops.go b/internal/opconst/ops.go index 46396aac..aca341f9 100644 --- a/internal/opconst/ops.go +++ b/internal/opconst/ops.go @@ -38,7 +38,7 @@ const ( TypePaintLen = 1 + 4*4 TypeColorLen = 1 + 4 TypeAreaLen = 1 + 1 + 4*4 - TypePointerInputLen = 1 + 1 + TypePointerInputLen = 1 + 1 + 1 TypePassLen = 1 + 1 TypeKeyInputLen = 1 + 1 TypeHideInputLen = 1 diff --git a/io/pointer/doc.go b/io/pointer/doc.go index ed66d9ae..abc0ef1b 100644 --- a/io/pointer/doc.go +++ b/io/pointer/doc.go @@ -8,6 +8,23 @@ object such as a finger. The InputOp operation is used to declare a handler ready for pointer events. Use an event.Queue to receive events. +Types + +Only events that match a specified list of types are delivered to a handler. + +For example, to receive Press, Move, and Release events (but not Enter, +Leave, or Scroll): + + var ops op.Ops + var h *Handler = ... + + pointer.InputOp{ + Tag: h, + Types: pointer.Press | pointer.Move | pointer.Release, + }.Add(ops) + +Cancel events are always delivered. + Areas The area operations are used for specifying the area where @@ -15,9 +32,6 @@ subsequent InputOp are active. For example, to set up a rectangular hit area: - var ops op.Ops - var h *Handler = ... - r := image.Rectangle{...} pointer.Rect(r).Add(ops) pointer.InputOp{Tag: h}.Add(ops) diff --git a/io/pointer/pointer.go b/io/pointer/pointer.go index c3502a8a..9dc1f35a 100644 --- a/io/pointer/pointer.go +++ b/io/pointer/pointer.go @@ -56,6 +56,8 @@ type InputOp struct { // Grab, if set, request that the handler get // Grabbed priority. Grab bool + // Types is a bitwise-or of event types to receive. + Types Type } // PassOp sets the pass-through mode. @@ -83,7 +85,7 @@ type areaKind uint8 const ( // A Cancel event is generated when the current gesture is // interrupted by other handlers or the system. - Cancel Type = iota + Cancel Type = (1 << iota) >> 1 // Press of a pointer. Press // Release of a pointer. @@ -157,6 +159,7 @@ func (h InputOp) Add(o *op.Ops) { if h.Grab { data[1] = 1 } + data[2] = byte(h.Types) } func (op PassOp) Add(o *op.Ops) { diff --git a/io/router/pointer.go b/io/router/pointer.go index e8e3c54e..1bd16040 100644 --- a/io/router/pointer.go +++ b/io/router/pointer.go @@ -46,6 +46,7 @@ type pointerHandler struct { active bool transform op.TransformOp wantsGrab bool + types pointer.Type } type areaOp struct { @@ -109,6 +110,7 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t o h.area = area h.transform = t h.wantsGrab = op.Grab + h.types = op.Types } } } @@ -264,7 +266,7 @@ func (q *pointerQueue) deliverEvent(p *pointerInfo, events *handlerEvents, e poi } e.Position = h.transform.Invert().Transform(e.Position) - events.Add(k, e) + addPointerEvent(events, k, e, h.types) } } @@ -293,15 +295,21 @@ func (q *pointerQueue) deliverEnterLeaveEvents(p *pointerInfo, events *handlerEv case !hit && entered != -1: p.entered = append(p.entered[:entered], p.entered[entered+1:]...) e.Type = pointer.Leave - events.Add(k, e) + addPointerEvent(events, k, e, h.types) case hit && entered == -1: p.entered = append(p.entered, k) e.Type = pointer.Enter - events.Add(k, e) + addPointerEvent(events, k, e, h.types) } } } +func addPointerEvent(events *handlerEvents, k event.Tag, e pointer.Event, types pointer.Type) { + if e.Type&types == e.Type { + events.Add(k, e) + } +} + func (op *areaOp) Decode(d []byte) { if opconst.OpType(d[0]) != opconst.TypeArea { panic("invalid op") @@ -352,8 +360,9 @@ func decodePointerInputOp(d []byte, refs []interface{}) pointer.InputOp { panic("invalid op") } return pointer.InputOp{ - Tag: refs[0].(event.Tag), - Grab: d[1] != 0, + Tag: refs[0].(event.Tag), + Grab: d[1] != 0, + Types: pointer.Type(d[2]), } } diff --git a/io/router/pointer_test.go b/io/router/pointer_test.go index 1d1085e7..9f9bee86 100644 --- a/io/router/pointer_test.go +++ b/io/router/pointer_test.go @@ -47,12 +47,14 @@ func TestPointerMove(t *testing.T) { handler2 := new(int) var ops op.Ops + types := pointer.Move | pointer.Enter | pointer.Leave + // Handler 1 area: (0, 0) - (100, 100) pointer.Rect(image.Rect(0, 0, 100, 100)).Add(&ops) - pointer.InputOp{Tag: handler1}.Add(&ops) + pointer.InputOp{Tag: handler1, Types: types}.Add(&ops) // Handler 2 area: (50, 50) - (100, 100) (areas intersect). pointer.Rect(image.Rect(50, 50, 200, 200)).Add(&ops) - pointer.InputOp{Tag: handler2}.Add(&ops) + pointer.InputOp{Tag: handler2, Types: types}.Add(&ops) var r Router r.Frame(&ops) @@ -86,6 +88,43 @@ func TestPointerMove(t *testing.T) { assertEventSequence(t, r.Events(handler2), pointer.Cancel, pointer.Enter, pointer.Move, pointer.Leave) } +func TestPointerTypes(t *testing.T) { + handler := new(int) + var ops op.Ops + pointer.Rect(image.Rect(0, 0, 100, 100)).Add(&ops) + pointer.InputOp{ + Tag: handler, + Types: pointer.Press | pointer.Release, + }.Add(&ops) + + var r Router + r.Frame(&ops) + r.Add( + pointer.Event{ + Type: pointer.Press, + Position: f32.Point{ + X: 50, + Y: 50, + }, + }, + pointer.Event{ + Type: pointer.Move, + Position: f32.Point{ + X: 150, + Y: 150, + }, + }, + pointer.Event{ + Type: pointer.Release, + Position: f32.Point{ + X: 150, + Y: 150, + }, + }, + ) + assertEventSequence(t, r.Events(handler), pointer.Cancel, pointer.Press, pointer.Release) +} + func TestPointerEnterLeave(t *testing.T) { handler1 := new(int) handler2 := new(int) @@ -190,13 +229,15 @@ func TestPointerEnterLeaveNested(t *testing.T) { handler2 := new(int) var ops op.Ops + types := pointer.Press | pointer.Move | pointer.Release | pointer.Enter | pointer.Leave + // Handler 1 area: (0, 0) - (100, 100) pointer.Rect(image.Rect(0, 0, 100, 100)).Add(&ops) - pointer.InputOp{Tag: handler1}.Add(&ops) + pointer.InputOp{Tag: handler1, Types: types}.Add(&ops) // Handler 2 area: (25, 25) - (75, 75) (nested within first). pointer.Rect(image.Rect(25, 25, 75, 75)).Add(&ops) - pointer.InputOp{Tag: handler2}.Add(&ops) + pointer.InputOp{Tag: handler2, Types: types}.Add(&ops) var r Router r.Frame(&ops) @@ -360,7 +401,10 @@ func TestMultitouch(t *testing.T) { func addPointerHandler(ops *op.Ops, tag event.Tag, area image.Rectangle) { defer op.Push(ops).Pop() pointer.Rect(area).Add(ops) - pointer.InputOp{Tag: tag}.Add(ops) + pointer.InputOp{ + Tag: tag, + Types: pointer.Press | pointer.Move | pointer.Release | pointer.Enter | pointer.Leave, + }.Add(ops) } // pointerTypes converts a sequence of event.Event to their pointer.Types. It assumes @@ -411,7 +455,10 @@ func BenchmarkRouterAdd(b *testing.B) { Y: 100, }, }).Add(&ops) - pointer.InputOp{Tag: handlers[i]}.Add(&ops) + pointer.InputOp{ + Tag: handlers[i], + Types: pointer.Move, + }.Add(&ops) } var r Router r.Frame(&ops)