~eliasnaur/gio

9d2a6c48d88f709575c1660a04893945c6d0df08 — Gordon Klaus a month ago 4484674
io/pointer: add InputOp.Types to specify which types of events to receive

Signed-off-by: Gordon Klaus <gordon.klaus@gmail.com>
M cmd/gogio/testdata/red.go => cmd/gogio/testdata/red.go +4 -1
@@ 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 {

M gesture/gesture.go => gesture/gesture.go +9 -2
@@ 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)

M internal/opconst/ops.go => internal/opconst/ops.go +1 -1
@@ 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

M io/pointer/doc.go => io/pointer/doc.go +17 -3
@@ 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)

M io/pointer/pointer.go => io/pointer/pointer.go +4 -1
@@ 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) {

M io/router/pointer.go => io/router/pointer.go +14 -5
@@ 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]),
	}
}


M io/router/pointer_test.go => io/router/pointer_test.go +53 -6
@@ 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)