gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/io/input/pointer.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package input
     4  
     5  import (
     6  	"image"
     7  	"io"
     8  
     9  	"gioui.org/f32"
    10  	f32internal "gioui.org/internal/f32"
    11  	"gioui.org/internal/ops"
    12  	"gioui.org/io/event"
    13  	"gioui.org/io/pointer"
    14  	"gioui.org/io/semantic"
    15  	"gioui.org/io/system"
    16  	"gioui.org/io/transfer"
    17  )
    18  
    19  type pointerQueue struct {
    20  	hitTree []hitNode
    21  	areas   []areaNode
    22  
    23  	semantic struct {
    24  		idsAssigned bool
    25  		lastID      SemanticID
    26  		// contentIDs maps semantic content to a list of semantic IDs
    27  		// previously assigned. It is used to maintain stable IDs across
    28  		// frames.
    29  		contentIDs map[semanticContent][]semanticID
    30  	}
    31  }
    32  
    33  type hitNode struct {
    34  	next int
    35  	area int
    36  
    37  	// For handler nodes.
    38  	tag  event.Tag
    39  	pass bool
    40  }
    41  
    42  // pointerState is the input state related to pointer events.
    43  type pointerState struct {
    44  	cursor   pointer.Cursor
    45  	pointers []pointerInfo
    46  }
    47  
    48  type pointerInfo struct {
    49  	id       pointer.ID
    50  	pressed  bool
    51  	handlers []event.Tag
    52  	// last tracks the last pointer event received,
    53  	// used while processing frame events.
    54  	last pointer.Event
    55  
    56  	// entered tracks the tags that contain the pointer.
    57  	entered []event.Tag
    58  
    59  	dataSource event.Tag // dragging source tag
    60  	dataTarget event.Tag // dragging target tag
    61  }
    62  
    63  type pointerHandler struct {
    64  	// areaPlusOne is the index into the list of pointerQueue.areas, plus 1.
    65  	areaPlusOne int
    66  	// setup tracks whether the handler has received
    67  	// the pointer.Cancel event that resets its state.
    68  	setup bool
    69  }
    70  
    71  // pointerFilter represents the union of a set of pointer filters.
    72  type pointerFilter struct {
    73  	kinds pointer.Kind
    74  	// min and max horizontal/vertical scroll
    75  	scrollX, scrollY pointer.ScrollRange
    76  
    77  	sourceMimes []string
    78  	targetMimes []string
    79  }
    80  
    81  type areaOp struct {
    82  	kind areaKind
    83  	rect image.Rectangle
    84  }
    85  
    86  type areaNode struct {
    87  	trans f32.Affine2D
    88  	area  areaOp
    89  
    90  	cursor pointer.Cursor
    91  
    92  	// Tree indices, with -1 being the sentinel.
    93  	parent     int
    94  	firstChild int
    95  	lastChild  int
    96  	sibling    int
    97  
    98  	semantic struct {
    99  		valid   bool
   100  		id      SemanticID
   101  		content semanticContent
   102  	}
   103  	action system.Action
   104  }
   105  
   106  type areaKind uint8
   107  
   108  // collectState represents the state for pointerCollector.
   109  type collectState struct {
   110  	t f32.Affine2D
   111  	// nodePlusOne is the current node index, plus one to
   112  	// make the zero value collectState the initial state.
   113  	nodePlusOne int
   114  	pass        int
   115  }
   116  
   117  // pointerCollector tracks the state needed to update an pointerQueue
   118  // from pointer ops.
   119  type pointerCollector struct {
   120  	q         *pointerQueue
   121  	state     collectState
   122  	nodeStack []int
   123  }
   124  
   125  type semanticContent struct {
   126  	tag      event.Tag
   127  	label    string
   128  	desc     string
   129  	class    semantic.ClassOp
   130  	gestures SemanticGestures
   131  	selected bool
   132  	disabled bool
   133  }
   134  
   135  type semanticID struct {
   136  	id   SemanticID
   137  	used bool
   138  }
   139  
   140  const (
   141  	areaRect areaKind = iota
   142  	areaEllipse
   143  )
   144  
   145  func (c *pointerCollector) resetState() {
   146  	c.state = collectState{}
   147  	c.nodeStack = c.nodeStack[:0]
   148  	// Pop every node except the root.
   149  	if len(c.q.hitTree) > 0 {
   150  		c.state.nodePlusOne = 0 + 1
   151  	}
   152  }
   153  
   154  func (c *pointerCollector) setTrans(t f32.Affine2D) {
   155  	c.state.t = t
   156  }
   157  
   158  func (c *pointerCollector) clip(op ops.ClipOp) {
   159  	kind := areaRect
   160  	if op.Shape == ops.Ellipse {
   161  		kind = areaEllipse
   162  	}
   163  	c.pushArea(kind, op.Bounds)
   164  }
   165  
   166  func (c *pointerCollector) pushArea(kind areaKind, bounds image.Rectangle) {
   167  	parentID := c.currentArea()
   168  	areaID := len(c.q.areas)
   169  	areaOp := areaOp{kind: kind, rect: bounds}
   170  	if parentID != -1 {
   171  		parent := &c.q.areas[parentID]
   172  		if parent.firstChild == -1 {
   173  			parent.firstChild = areaID
   174  		}
   175  		if siblingID := parent.lastChild; siblingID != -1 {
   176  			c.q.areas[siblingID].sibling = areaID
   177  		}
   178  		parent.lastChild = areaID
   179  	}
   180  	an := areaNode{
   181  		trans:      c.state.t,
   182  		area:       areaOp,
   183  		parent:     parentID,
   184  		sibling:    -1,
   185  		firstChild: -1,
   186  		lastChild:  -1,
   187  	}
   188  
   189  	c.q.areas = append(c.q.areas, an)
   190  	c.nodeStack = append(c.nodeStack, c.state.nodePlusOne-1)
   191  	c.addHitNode(hitNode{
   192  		area: areaID,
   193  		pass: true,
   194  	})
   195  }
   196  
   197  func (c *pointerCollector) popArea() {
   198  	n := len(c.nodeStack)
   199  	c.state.nodePlusOne = c.nodeStack[n-1] + 1
   200  	c.nodeStack = c.nodeStack[:n-1]
   201  }
   202  
   203  func (c *pointerCollector) pass() {
   204  	c.state.pass++
   205  }
   206  
   207  func (c *pointerCollector) popPass() {
   208  	c.state.pass--
   209  }
   210  
   211  func (c *pointerCollector) currentArea() int {
   212  	if i := c.state.nodePlusOne - 1; i != -1 {
   213  		n := c.q.hitTree[i]
   214  		return n.area
   215  	}
   216  	return -1
   217  }
   218  
   219  func (c *pointerCollector) currentAreaBounds() image.Rectangle {
   220  	a := c.currentArea()
   221  	if a == -1 {
   222  		panic("no root area")
   223  	}
   224  	return c.q.areas[a].bounds()
   225  }
   226  
   227  func (c *pointerCollector) addHitNode(n hitNode) {
   228  	n.next = c.state.nodePlusOne - 1
   229  	c.q.hitTree = append(c.q.hitTree, n)
   230  	c.state.nodePlusOne = len(c.q.hitTree) - 1 + 1
   231  }
   232  
   233  // newHandler returns the current handler or a new one for tag.
   234  func (c *pointerCollector) newHandler(tag event.Tag, state *pointerHandler) {
   235  	areaID := c.currentArea()
   236  	c.addHitNode(hitNode{
   237  		area: areaID,
   238  		tag:  tag,
   239  		pass: c.state.pass > 0,
   240  	})
   241  	state.areaPlusOne = areaID + 1
   242  }
   243  
   244  func (s *pointerHandler) Reset() {
   245  	s.areaPlusOne = 0
   246  }
   247  
   248  func (c *pointerCollector) actionInputOp(act system.Action) {
   249  	areaID := c.currentArea()
   250  	area := &c.q.areas[areaID]
   251  	area.action = act
   252  }
   253  
   254  func (q *pointerQueue) grab(state pointerState, req pointer.GrabCmd) (pointerState, []taggedEvent) {
   255  	var evts []taggedEvent
   256  	for _, p := range state.pointers {
   257  		if !p.pressed || p.id != req.ID {
   258  			continue
   259  		}
   260  		// Drop other handlers that lost their grab.
   261  		for i := len(p.handlers) - 1; i >= 0; i-- {
   262  			if tag := p.handlers[i]; tag != req.Tag {
   263  				evts = append(evts, taggedEvent{
   264  					tag:   tag,
   265  					event: pointer.Event{Kind: pointer.Cancel},
   266  				})
   267  				state = dropHandler(state, tag)
   268  			}
   269  		}
   270  		break
   271  	}
   272  	return state, evts
   273  }
   274  
   275  func (c *pointerCollector) inputOp(tag event.Tag, state *pointerHandler) {
   276  	areaID := c.currentArea()
   277  	area := &c.q.areas[areaID]
   278  	area.semantic.content.tag = tag
   279  	c.newHandler(tag, state)
   280  }
   281  
   282  func (p *pointerFilter) Add(f event.Filter) {
   283  	switch f := f.(type) {
   284  	case transfer.SourceFilter:
   285  		for _, m := range p.sourceMimes {
   286  			if m == f.Type {
   287  				return
   288  			}
   289  		}
   290  		p.sourceMimes = append(p.sourceMimes, f.Type)
   291  	case transfer.TargetFilter:
   292  		for _, m := range p.targetMimes {
   293  			if m == f.Type {
   294  				return
   295  			}
   296  		}
   297  		p.targetMimes = append(p.targetMimes, f.Type)
   298  	case pointer.Filter:
   299  		p.kinds = p.kinds | f.Kinds
   300  		p.scrollX = p.scrollX.Union(f.ScrollX)
   301  		p.scrollY = p.scrollY.Union(f.ScrollY)
   302  	}
   303  }
   304  
   305  func (p *pointerFilter) Matches(e event.Event) bool {
   306  	switch e := e.(type) {
   307  	case pointer.Event:
   308  		return e.Kind&p.kinds == e.Kind
   309  	case transfer.CancelEvent, transfer.InitiateEvent:
   310  		return len(p.sourceMimes) > 0 || len(p.targetMimes) > 0
   311  	case transfer.RequestEvent:
   312  		for _, t := range p.sourceMimes {
   313  			if t == e.Type {
   314  				return true
   315  			}
   316  		}
   317  	case transfer.DataEvent:
   318  		for _, t := range p.targetMimes {
   319  			if t == e.Type {
   320  				return true
   321  			}
   322  		}
   323  	}
   324  	return false
   325  }
   326  
   327  func (p *pointerFilter) Merge(p2 pointerFilter) {
   328  	p.kinds = p.kinds | p2.kinds
   329  	p.scrollX = p.scrollX.Union(p2.scrollX)
   330  	p.scrollY = p.scrollY.Union(p2.scrollY)
   331  	p.sourceMimes = append(p.sourceMimes, p2.sourceMimes...)
   332  	p.targetMimes = append(p.targetMimes, p2.targetMimes...)
   333  }
   334  
   335  // clampScroll splits a scroll distance in the remaining scroll and the
   336  // scroll accepted by the filter.
   337  func (p *pointerFilter) clampScroll(scroll f32.Point) (left, scrolled f32.Point) {
   338  	left.X, scrolled.X = clampSplit(scroll.X, p.scrollX.Min, p.scrollX.Max)
   339  	left.Y, scrolled.Y = clampSplit(scroll.Y, p.scrollY.Min, p.scrollY.Max)
   340  	return
   341  }
   342  
   343  func clampSplit(v float32, min, max int) (float32, float32) {
   344  	if m := float32(max); v > m {
   345  		return v - m, m
   346  	}
   347  	if m := float32(min); v < m {
   348  		return v - m, m
   349  	}
   350  	return 0, v
   351  }
   352  
   353  func (s *pointerHandler) ResetEvent() (event.Event, bool) {
   354  	if s.setup {
   355  		return nil, false
   356  	}
   357  	s.setup = true
   358  	return pointer.Event{Kind: pointer.Cancel}, true
   359  }
   360  
   361  func (c *pointerCollector) semanticLabel(lbl string) {
   362  	areaID := c.currentArea()
   363  	area := &c.q.areas[areaID]
   364  	area.semantic.valid = true
   365  	area.semantic.content.label = lbl
   366  }
   367  
   368  func (c *pointerCollector) semanticDesc(desc string) {
   369  	areaID := c.currentArea()
   370  	area := &c.q.areas[areaID]
   371  	area.semantic.valid = true
   372  	area.semantic.content.desc = desc
   373  }
   374  
   375  func (c *pointerCollector) semanticClass(class semantic.ClassOp) {
   376  	areaID := c.currentArea()
   377  	area := &c.q.areas[areaID]
   378  	area.semantic.valid = true
   379  	area.semantic.content.class = class
   380  }
   381  
   382  func (c *pointerCollector) semanticSelected(selected bool) {
   383  	areaID := c.currentArea()
   384  	area := &c.q.areas[areaID]
   385  	area.semantic.valid = true
   386  	area.semantic.content.selected = selected
   387  }
   388  
   389  func (c *pointerCollector) semanticEnabled(enabled bool) {
   390  	areaID := c.currentArea()
   391  	area := &c.q.areas[areaID]
   392  	area.semantic.valid = true
   393  	area.semantic.content.disabled = !enabled
   394  }
   395  
   396  func (c *pointerCollector) cursor(cursor pointer.Cursor) {
   397  	areaID := c.currentArea()
   398  	area := &c.q.areas[areaID]
   399  	area.cursor = cursor
   400  }
   401  
   402  func (q *pointerQueue) offerData(handlers map[event.Tag]*handler, state pointerState, req transfer.OfferCmd) (pointerState, []taggedEvent) {
   403  	var evts []taggedEvent
   404  	for i, p := range state.pointers {
   405  		if p.dataSource != req.Tag {
   406  			continue
   407  		}
   408  		if p.dataTarget != nil {
   409  			evts = append(evts, taggedEvent{tag: p.dataTarget, event: transfer.DataEvent{
   410  				Type: req.Type,
   411  				Open: func() io.ReadCloser {
   412  					return req.Data
   413  				},
   414  			}})
   415  		}
   416  		state.pointers = append([]pointerInfo{}, state.pointers...)
   417  		state.pointers[i], evts = q.deliverTransferCancelEvent(handlers, p, evts)
   418  		break
   419  	}
   420  	return state, evts
   421  }
   422  
   423  func (c *pointerCollector) Reset() {
   424  	c.q.reset()
   425  	c.resetState()
   426  	c.ensureRoot()
   427  }
   428  
   429  // Ensure implicit root area for semantic descriptions to hang onto.
   430  func (c *pointerCollector) ensureRoot() {
   431  	if len(c.q.areas) > 0 {
   432  		return
   433  	}
   434  	c.pushArea(areaRect, image.Rect(-1e6, -1e6, 1e6, 1e6))
   435  	// Make it semantic to ensure a single semantic root.
   436  	c.q.areas[0].semantic.valid = true
   437  }
   438  
   439  func (q *pointerQueue) assignSemIDs() {
   440  	if q.semantic.idsAssigned {
   441  		return
   442  	}
   443  	q.semantic.idsAssigned = true
   444  	for i, a := range q.areas {
   445  		if a.semantic.valid {
   446  			q.areas[i].semantic.id = q.semanticIDFor(a.semantic.content)
   447  		}
   448  	}
   449  }
   450  
   451  func (q *pointerQueue) AppendSemantics(nodes []SemanticNode) []SemanticNode {
   452  	q.assignSemIDs()
   453  	nodes = q.appendSemanticChildren(nodes, 0)
   454  	nodes = q.appendSemanticArea(nodes, 0, 0)
   455  	return nodes
   456  }
   457  
   458  func (q *pointerQueue) appendSemanticArea(nodes []SemanticNode, parentID SemanticID, nodeIdx int) []SemanticNode {
   459  	areaIdx := nodes[nodeIdx].areaIdx
   460  	a := q.areas[areaIdx]
   461  	childStart := len(nodes)
   462  	nodes = q.appendSemanticChildren(nodes, a.firstChild)
   463  	childEnd := len(nodes)
   464  	for i := childStart; i < childEnd; i++ {
   465  		nodes = q.appendSemanticArea(nodes, a.semantic.id, i)
   466  	}
   467  	n := &nodes[nodeIdx]
   468  	n.ParentID = parentID
   469  	n.Children = nodes[childStart:childEnd]
   470  	return nodes
   471  }
   472  
   473  func (q *pointerQueue) appendSemanticChildren(nodes []SemanticNode, areaIdx int) []SemanticNode {
   474  	if areaIdx == -1 {
   475  		return nodes
   476  	}
   477  	a := q.areas[areaIdx]
   478  	if semID := a.semantic.id; semID != 0 {
   479  		cnt := a.semantic.content
   480  		nodes = append(nodes, SemanticNode{
   481  			ID: semID,
   482  			Desc: SemanticDesc{
   483  				Bounds:      a.bounds(),
   484  				Label:       cnt.label,
   485  				Description: cnt.desc,
   486  				Class:       cnt.class,
   487  				Gestures:    cnt.gestures,
   488  				Selected:    cnt.selected,
   489  				Disabled:    cnt.disabled,
   490  			},
   491  			areaIdx: areaIdx,
   492  		})
   493  	} else {
   494  		nodes = q.appendSemanticChildren(nodes, a.firstChild)
   495  	}
   496  	return q.appendSemanticChildren(nodes, a.sibling)
   497  }
   498  
   499  func (q *pointerQueue) semanticIDFor(content semanticContent) SemanticID {
   500  	ids := q.semantic.contentIDs[content]
   501  	for i, id := range ids {
   502  		if !id.used {
   503  			ids[i].used = true
   504  			return id.id
   505  		}
   506  	}
   507  	// No prior assigned ID; allocate a new one.
   508  	q.semantic.lastID++
   509  	id := semanticID{id: q.semantic.lastID, used: true}
   510  	if q.semantic.contentIDs == nil {
   511  		q.semantic.contentIDs = make(map[semanticContent][]semanticID)
   512  	}
   513  	q.semantic.contentIDs[content] = append(q.semantic.contentIDs[content], id)
   514  	return id.id
   515  }
   516  
   517  func (q *pointerQueue) ActionAt(pos f32.Point) (action system.Action, hasAction bool) {
   518  	q.hitTest(pos, func(n *hitNode) bool {
   519  		area := q.areas[n.area]
   520  		if area.action != 0 {
   521  			action = area.action
   522  			hasAction = true
   523  			return false
   524  		}
   525  		return true
   526  	})
   527  	return action, hasAction
   528  }
   529  
   530  func (q *pointerQueue) SemanticAt(pos f32.Point) (semID SemanticID, hasSemID bool) {
   531  	q.assignSemIDs()
   532  	q.hitTest(pos, func(n *hitNode) bool {
   533  		area := q.areas[n.area]
   534  		if area.semantic.id != 0 {
   535  			semID = area.semantic.id
   536  			hasSemID = true
   537  			return false
   538  		}
   539  		return true
   540  	})
   541  	return semID, hasSemID
   542  }
   543  
   544  // hitTest searches the hit tree for nodes matching pos. Any node matching pos will
   545  // have the onNode func invoked on it to allow the caller to extract whatever information
   546  // is necessary for further processing. onNode may return false to terminate the walk of
   547  // the hit tree, or true to continue. Providing this algorithm in this generic way
   548  // allows normal event routing and system action event routing to share the same traversal
   549  // logic even though they are interested in different aspects of hit nodes.
   550  func (q *pointerQueue) hitTest(pos f32.Point, onNode func(*hitNode) bool) pointer.Cursor {
   551  	// Track whether we're passing through hits.
   552  	pass := true
   553  	idx := len(q.hitTree) - 1
   554  	cursor := pointer.CursorDefault
   555  	for idx >= 0 {
   556  		n := &q.hitTree[idx]
   557  		hit, c := q.hit(n.area, pos)
   558  		if !hit {
   559  			idx--
   560  			continue
   561  		}
   562  		if cursor == pointer.CursorDefault {
   563  			cursor = c
   564  		}
   565  		pass = pass && n.pass
   566  		if pass {
   567  			idx--
   568  		} else {
   569  			idx = n.next
   570  		}
   571  		if !onNode(n) {
   572  			break
   573  		}
   574  	}
   575  	return cursor
   576  }
   577  
   578  func (q *pointerQueue) invTransform(areaIdx int, p f32.Point) f32.Point {
   579  	if areaIdx == -1 {
   580  		return p
   581  	}
   582  	return q.areas[areaIdx].trans.Invert().Transform(p)
   583  }
   584  
   585  func (q *pointerQueue) hit(areaIdx int, p f32.Point) (bool, pointer.Cursor) {
   586  	c := pointer.CursorDefault
   587  	for areaIdx != -1 {
   588  		a := &q.areas[areaIdx]
   589  		if c == pointer.CursorDefault {
   590  			c = a.cursor
   591  		}
   592  		p := a.trans.Invert().Transform(p)
   593  		if !a.area.Hit(p) {
   594  			return false, c
   595  		}
   596  		areaIdx = a.parent
   597  	}
   598  	return true, c
   599  }
   600  
   601  func (q *pointerQueue) reset() {
   602  	q.hitTree = q.hitTree[:0]
   603  	q.areas = q.areas[:0]
   604  	q.semantic.idsAssigned = false
   605  	for k, ids := range q.semantic.contentIDs {
   606  		for i := len(ids) - 1; i >= 0; i-- {
   607  			if !ids[i].used {
   608  				ids = append(ids[:i], ids[i+1:]...)
   609  			} else {
   610  				ids[i].used = false
   611  			}
   612  		}
   613  		if len(ids) > 0 {
   614  			q.semantic.contentIDs[k] = ids
   615  		} else {
   616  			delete(q.semantic.contentIDs, k)
   617  		}
   618  	}
   619  }
   620  
   621  func (q *pointerQueue) Frame(handlers map[event.Tag]*handler, state pointerState) (pointerState, []taggedEvent) {
   622  	for _, h := range handlers {
   623  		if h.pointer.areaPlusOne != 0 {
   624  			area := &q.areas[h.pointer.areaPlusOne-1]
   625  			if h.filter.pointer.kinds&(pointer.Press|pointer.Release) != 0 {
   626  				area.semantic.content.gestures |= ClickGesture
   627  			}
   628  			if h.filter.pointer.kinds&pointer.Scroll != 0 {
   629  				area.semantic.content.gestures |= ScrollGesture
   630  			}
   631  			area.semantic.valid = area.semantic.content.gestures != 0
   632  		}
   633  	}
   634  	var evts []taggedEvent
   635  	for i, p := range state.pointers {
   636  		changed := false
   637  		p, evts, state.cursor, changed = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, p.last)
   638  		if changed {
   639  			state.pointers = append([]pointerInfo{}, state.pointers...)
   640  			state.pointers[i] = p
   641  		}
   642  	}
   643  	return state, evts
   644  }
   645  
   646  func dropHandler(state pointerState, tag event.Tag) pointerState {
   647  	pointers := state.pointers
   648  	state.pointers = nil
   649  	for _, p := range pointers {
   650  		handlers := p.handlers
   651  		p.handlers = nil
   652  		for _, h := range handlers {
   653  			if h != tag {
   654  				p.handlers = append(p.handlers, h)
   655  			}
   656  		}
   657  		entered := p.entered
   658  		p.entered = nil
   659  		for _, h := range entered {
   660  			if h != tag {
   661  				p.entered = append(p.entered, h)
   662  			}
   663  		}
   664  		state.pointers = append(state.pointers, p)
   665  	}
   666  	return state
   667  }
   668  
   669  // pointerOf returns the pointerInfo index corresponding to the pointer in e.
   670  func (s pointerState) pointerOf(e pointer.Event) (pointerState, int) {
   671  	for i, p := range s.pointers {
   672  		if p.id == e.PointerID {
   673  			return s, i
   674  		}
   675  	}
   676  	n := len(s.pointers)
   677  	s.pointers = append(s.pointers[:n:n], pointerInfo{id: e.PointerID})
   678  	return s, len(s.pointers) - 1
   679  }
   680  
   681  // Deliver is like Push, but delivers an event to a particular area.
   682  func (q *pointerQueue) Deliver(handlers map[event.Tag]*handler, areaIdx int, e pointer.Event) []taggedEvent {
   683  	scroll := e.Scroll
   684  	idx := len(q.hitTree) - 1
   685  	// Locate first potential receiver.
   686  	for idx != -1 {
   687  		n := &q.hitTree[idx]
   688  		if n.area == areaIdx {
   689  			break
   690  		}
   691  		idx--
   692  	}
   693  	var evts []taggedEvent
   694  	for idx != -1 {
   695  		n := &q.hitTree[idx]
   696  		idx = n.next
   697  		h, ok := handlers[n.tag]
   698  		if !ok || !h.filter.pointer.Matches(e) {
   699  			continue
   700  		}
   701  		e := e
   702  		if e.Kind == pointer.Scroll {
   703  			if scroll == (f32.Point{}) {
   704  				break
   705  			}
   706  			scroll, e.Scroll = h.filter.pointer.clampScroll(scroll)
   707  		}
   708  		e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position)
   709  		evts = append(evts, taggedEvent{tag: n.tag, event: e})
   710  		if e.Kind != pointer.Scroll {
   711  			break
   712  		}
   713  	}
   714  	return evts
   715  }
   716  
   717  // SemanticArea returns the sematic content for area, and its parent area.
   718  func (q *pointerQueue) SemanticArea(areaIdx int) (semanticContent, int) {
   719  	for areaIdx != -1 {
   720  		a := &q.areas[areaIdx]
   721  		areaIdx = a.parent
   722  		if !a.semantic.valid {
   723  			continue
   724  		}
   725  		return a.semantic.content, areaIdx
   726  	}
   727  	return semanticContent{}, -1
   728  }
   729  
   730  func (q *pointerQueue) Push(handlers map[event.Tag]*handler, state pointerState, e pointer.Event) (pointerState, []taggedEvent) {
   731  	var evts []taggedEvent
   732  	if e.Kind == pointer.Cancel {
   733  		for k := range handlers {
   734  			evts = append(evts, taggedEvent{
   735  				event: pointer.Event{Kind: pointer.Cancel},
   736  				tag:   k,
   737  			})
   738  		}
   739  		state.pointers = nil
   740  		return state, evts
   741  	}
   742  	state, pidx := state.pointerOf(e)
   743  	p := state.pointers[pidx]
   744  
   745  	switch e.Kind {
   746  	case pointer.Press:
   747  		p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
   748  		p.pressed = true
   749  		evts = q.deliverEvent(handlers, p, evts, e)
   750  	case pointer.Move:
   751  		if p.pressed {
   752  			e.Kind = pointer.Drag
   753  		}
   754  		p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
   755  		evts = q.deliverEvent(handlers, p, evts, e)
   756  		if p.pressed {
   757  			p, evts = q.deliverDragEvent(handlers, p, evts)
   758  		}
   759  	case pointer.Release:
   760  		evts = q.deliverEvent(handlers, p, evts, e)
   761  		p.pressed = false
   762  		p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
   763  		p, evts = q.deliverDropEvent(handlers, p, evts)
   764  	case pointer.Scroll:
   765  		p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
   766  		evts = q.deliverEvent(handlers, p, evts, e)
   767  	default:
   768  		panic("unsupported pointer event type")
   769  	}
   770  
   771  	p.last = e
   772  
   773  	if !p.pressed && len(p.entered) == 0 {
   774  		// No longer need to track pointer.
   775  		state.pointers = append(state.pointers[:pidx:pidx], state.pointers[pidx+1:]...)
   776  	} else {
   777  		state.pointers = append([]pointerInfo{}, state.pointers...)
   778  		state.pointers[pidx] = p
   779  	}
   780  	return state, evts
   781  }
   782  
   783  func (q *pointerQueue) deliverEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent, e pointer.Event) []taggedEvent {
   784  	foremost := true
   785  	if p.pressed && len(p.handlers) == 1 {
   786  		e.Priority = pointer.Grabbed
   787  		foremost = false
   788  	}
   789  	scroll := e.Scroll
   790  	for _, k := range p.handlers {
   791  		h, ok := handlers[k]
   792  		if !ok {
   793  			continue
   794  		}
   795  		f := h.filter.pointer
   796  		if !f.Matches(e) {
   797  			continue
   798  		}
   799  		if e.Kind == pointer.Scroll {
   800  			if scroll == (f32.Point{}) {
   801  				return evts
   802  			}
   803  			scroll, e.Scroll = f.clampScroll(scroll)
   804  		}
   805  		e := e
   806  		if foremost {
   807  			foremost = false
   808  			e.Priority = pointer.Foremost
   809  		}
   810  		e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position)
   811  		evts = append(evts, taggedEvent{event: e, tag: k})
   812  	}
   813  	return evts
   814  }
   815  
   816  func (q *pointerQueue) deliverEnterLeaveEvents(handlers map[event.Tag]*handler, cursor pointer.Cursor, p pointerInfo, evts []taggedEvent, e pointer.Event) (pointerInfo, []taggedEvent, pointer.Cursor, bool) {
   817  	changed := false
   818  	var hits []event.Tag
   819  	if e.Source != pointer.Mouse && !p.pressed && e.Kind != pointer.Press {
   820  		// Consider non-mouse pointers leaving when they're released.
   821  	} else {
   822  		var transSrc *pointerFilter
   823  		if p.dataSource != nil {
   824  			transSrc = &handlers[p.dataSource].filter.pointer
   825  		}
   826  		cursor = q.hitTest(e.Position, func(n *hitNode) bool {
   827  			h, ok := handlers[n.tag]
   828  			if !ok {
   829  				return true
   830  			}
   831  			add := true
   832  			if p.pressed {
   833  				add = false
   834  				// Filter out non-participating handlers,
   835  				// except potential transfer targets when a transfer has been initiated.
   836  				if _, found := searchTag(p.handlers, n.tag); found {
   837  					add = true
   838  				}
   839  				if transSrc != nil {
   840  					if _, ok := firstMimeMatch(transSrc, &h.filter.pointer); ok {
   841  						add = true
   842  					}
   843  				}
   844  			}
   845  			if add {
   846  				hits = addHandler(hits, n.tag)
   847  			}
   848  			return true
   849  		})
   850  		if !p.pressed {
   851  			changed = true
   852  			p.handlers = hits
   853  		}
   854  	}
   855  	// Deliver Leave events.
   856  	for _, k := range p.entered {
   857  		if _, found := searchTag(hits, k); found {
   858  			continue
   859  		}
   860  		h, ok := handlers[k]
   861  		if !ok {
   862  			continue
   863  		}
   864  		changed = true
   865  		e := e
   866  		e.Kind = pointer.Leave
   867  
   868  		if h.filter.pointer.Matches(e) {
   869  			e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position)
   870  			evts = append(evts, taggedEvent{tag: k, event: e})
   871  		}
   872  	}
   873  	// Deliver Enter events.
   874  	for _, k := range hits {
   875  		if _, found := searchTag(p.entered, k); found {
   876  			continue
   877  		}
   878  		h, ok := handlers[k]
   879  		if !ok {
   880  			continue
   881  		}
   882  		changed = true
   883  		e := e
   884  		e.Kind = pointer.Enter
   885  
   886  		if h.filter.pointer.Matches(e) {
   887  			e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position)
   888  			evts = append(evts, taggedEvent{tag: k, event: e})
   889  		}
   890  	}
   891  	p.entered = hits
   892  	return p, evts, cursor, changed
   893  }
   894  
   895  func (q *pointerQueue) deliverDragEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) {
   896  	if p.dataSource != nil {
   897  		return p, evts
   898  	}
   899  	// Identify the data source.
   900  	for _, k := range p.entered {
   901  		src := &handlers[k].filter.pointer
   902  		if len(src.sourceMimes) == 0 {
   903  			continue
   904  		}
   905  		// One data source handler per pointer.
   906  		p.dataSource = k
   907  		// Notify all potential targets.
   908  		for k, tgt := range handlers {
   909  			if _, ok := firstMimeMatch(src, &tgt.filter.pointer); ok {
   910  				evts = append(evts, taggedEvent{tag: k, event: transfer.InitiateEvent{}})
   911  			}
   912  		}
   913  		break
   914  	}
   915  	return p, evts
   916  }
   917  
   918  func (q *pointerQueue) deliverDropEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) {
   919  	if p.dataSource == nil {
   920  		return p, evts
   921  	}
   922  	// Request data from the source.
   923  	src := &handlers[p.dataSource].filter.pointer
   924  	for _, k := range p.entered {
   925  		h := handlers[k]
   926  		if m, ok := firstMimeMatch(src, &h.filter.pointer); ok {
   927  			p.dataTarget = k
   928  			evts = append(evts, taggedEvent{tag: p.dataSource, event: transfer.RequestEvent{Type: m}})
   929  			return p, evts
   930  		}
   931  	}
   932  	// No valid target found, abort.
   933  	return q.deliverTransferCancelEvent(handlers, p, evts)
   934  }
   935  
   936  func (q *pointerQueue) deliverTransferCancelEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) {
   937  	evts = append(evts, taggedEvent{tag: p.dataSource, event: transfer.CancelEvent{}})
   938  	// Cancel all potential targets.
   939  	src := &handlers[p.dataSource].filter.pointer
   940  	for k, h := range handlers {
   941  		if _, ok := firstMimeMatch(src, &h.filter.pointer); ok {
   942  			evts = append(evts, taggedEvent{tag: k, event: transfer.CancelEvent{}})
   943  		}
   944  	}
   945  	p.dataSource = nil
   946  	p.dataTarget = nil
   947  	return p, evts
   948  }
   949  
   950  // ClipFor clips r to the parents of area.
   951  func (q *pointerQueue) ClipFor(area int, r image.Rectangle) image.Rectangle {
   952  	a := &q.areas[area]
   953  	parent := a.parent
   954  	for parent != -1 {
   955  		a := &q.areas[parent]
   956  		r = r.Intersect(a.bounds())
   957  		parent = a.parent
   958  	}
   959  	return r
   960  }
   961  
   962  func searchTag(tags []event.Tag, tag event.Tag) (int, bool) {
   963  	for i, t := range tags {
   964  		if t == tag {
   965  			return i, true
   966  		}
   967  	}
   968  	return 0, false
   969  }
   970  
   971  // addHandler adds tag to the slice if not present.
   972  func addHandler(tags []event.Tag, tag event.Tag) []event.Tag {
   973  	for _, t := range tags {
   974  		if t == tag {
   975  			return tags
   976  		}
   977  	}
   978  	return append(tags, tag)
   979  }
   980  
   981  // firstMimeMatch returns the first type match between src and tgt.
   982  func firstMimeMatch(src, tgt *pointerFilter) (first string, matched bool) {
   983  	for _, m1 := range tgt.targetMimes {
   984  		for _, m2 := range src.sourceMimes {
   985  			if m1 == m2 {
   986  				return m1, true
   987  			}
   988  		}
   989  	}
   990  	return "", false
   991  }
   992  
   993  func (op *areaOp) Hit(pos f32.Point) bool {
   994  	pos = pos.Sub(f32internal.FPt(op.rect.Min))
   995  	size := f32internal.FPt(op.rect.Size())
   996  	switch op.kind {
   997  	case areaRect:
   998  		return 0 <= pos.X && pos.X < size.X &&
   999  			0 <= pos.Y && pos.Y < size.Y
  1000  	case areaEllipse:
  1001  		rx := size.X / 2
  1002  		ry := size.Y / 2
  1003  		xh := pos.X - rx
  1004  		yk := pos.Y - ry
  1005  		// The ellipse function works in all cases because
  1006  		// 0/0 is not <= 1.
  1007  		return (xh*xh)/(rx*rx)+(yk*yk)/(ry*ry) <= 1
  1008  	default:
  1009  		panic("invalid area kind")
  1010  	}
  1011  }
  1012  
  1013  func (a *areaNode) bounds() image.Rectangle {
  1014  	return f32internal.Rectangle{
  1015  		Min: a.trans.Transform(f32internal.FPt(a.area.rect.Min)),
  1016  		Max: a.trans.Transform(f32internal.FPt(a.area.rect.Max)),
  1017  	}.Round()
  1018  }