github.com/utopiagio/gio@v0.0.8/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  	"github.com/utopiagio/gio/f32"
    10  	f32internal "github.com/utopiagio/gio/internal/f32"
    11  	"github.com/utopiagio/gio/internal/ops"
    12  	"github.com/utopiagio/gio/io/event"
    13  	"github.com/utopiagio/gio/io/pointer"
    14  	"github.com/utopiagio/gio/io/semantic"
    15  	"github.com/utopiagio/gio/io/system"
    16  	"github.com/utopiagio/gio/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  	scrollRange image.Rectangle
    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.scrollRange = p.scrollRange.Union(f.ScrollBounds)
   301  	}
   302  }
   303  
   304  func (p *pointerFilter) Matches(e event.Event) bool {
   305  	switch e := e.(type) {
   306  	case pointer.Event:
   307  		return e.Kind&p.kinds == e.Kind
   308  	case transfer.CancelEvent, transfer.InitiateEvent:
   309  		return len(p.sourceMimes) > 0 || len(p.targetMimes) > 0
   310  	case transfer.RequestEvent:
   311  		for _, t := range p.sourceMimes {
   312  			if t == e.Type {
   313  				return true
   314  			}
   315  		}
   316  	case transfer.DataEvent:
   317  		for _, t := range p.targetMimes {
   318  			if t == e.Type {
   319  				return true
   320  			}
   321  		}
   322  	}
   323  	return false
   324  }
   325  
   326  func (p *pointerFilter) Merge(p2 pointerFilter) {
   327  	p.kinds = p.kinds | p2.kinds
   328  	p.scrollRange = p.scrollRange.Union(p2.scrollRange)
   329  	p.sourceMimes = append(p.sourceMimes, p2.sourceMimes...)
   330  	p.targetMimes = append(p.targetMimes, p2.targetMimes...)
   331  }
   332  
   333  // clampScroll splits a scroll distance in the remaining scroll and the
   334  // scroll accepted by the filter.
   335  func (p *pointerFilter) clampScroll(scroll f32.Point) (left, scrolled f32.Point) {
   336  	left.X, scrolled.X = clampSplit(scroll.X, p.scrollRange.Min.X, p.scrollRange.Max.X)
   337  	left.Y, scrolled.Y = clampSplit(scroll.Y, p.scrollRange.Min.Y, p.scrollRange.Max.Y)
   338  	return
   339  }
   340  
   341  func clampSplit(v float32, min, max int) (float32, float32) {
   342  	if m := float32(max); v > m {
   343  		return v - m, m
   344  	}
   345  	if m := float32(min); v < m {
   346  		return v - m, m
   347  	}
   348  	return 0, v
   349  }
   350  
   351  func (s *pointerHandler) ResetEvent() (event.Event, bool) {
   352  	if s.setup {
   353  		return nil, false
   354  	}
   355  	s.setup = true
   356  	return pointer.Event{Kind: pointer.Cancel}, true
   357  }
   358  
   359  func (c *pointerCollector) semanticLabel(lbl string) {
   360  	areaID := c.currentArea()
   361  	area := &c.q.areas[areaID]
   362  	area.semantic.valid = true
   363  	area.semantic.content.label = lbl
   364  }
   365  
   366  func (c *pointerCollector) semanticDesc(desc string) {
   367  	areaID := c.currentArea()
   368  	area := &c.q.areas[areaID]
   369  	area.semantic.valid = true
   370  	area.semantic.content.desc = desc
   371  }
   372  
   373  func (c *pointerCollector) semanticClass(class semantic.ClassOp) {
   374  	areaID := c.currentArea()
   375  	area := &c.q.areas[areaID]
   376  	area.semantic.valid = true
   377  	area.semantic.content.class = class
   378  }
   379  
   380  func (c *pointerCollector) semanticSelected(selected bool) {
   381  	areaID := c.currentArea()
   382  	area := &c.q.areas[areaID]
   383  	area.semantic.valid = true
   384  	area.semantic.content.selected = selected
   385  }
   386  
   387  func (c *pointerCollector) semanticEnabled(enabled bool) {
   388  	areaID := c.currentArea()
   389  	area := &c.q.areas[areaID]
   390  	area.semantic.valid = true
   391  	area.semantic.content.disabled = !enabled
   392  }
   393  
   394  func (c *pointerCollector) cursor(cursor pointer.Cursor) {
   395  	areaID := c.currentArea()
   396  	area := &c.q.areas[areaID]
   397  	area.cursor = cursor
   398  }
   399  
   400  func (q *pointerQueue) offerData(handlers map[event.Tag]*handler, state pointerState, req transfer.OfferCmd) (pointerState, []taggedEvent) {
   401  	var evts []taggedEvent
   402  	for i, p := range state.pointers {
   403  		if p.dataSource != req.Tag {
   404  			continue
   405  		}
   406  		if p.dataTarget != nil {
   407  			evts = append(evts, taggedEvent{tag: p.dataTarget, event: transfer.DataEvent{
   408  				Type: req.Type,
   409  				Open: func() io.ReadCloser {
   410  					return req.Data
   411  				},
   412  			}})
   413  		}
   414  		state.pointers = append([]pointerInfo{}, state.pointers...)
   415  		state.pointers[i], evts = q.deliverTransferCancelEvent(handlers, p, evts)
   416  		break
   417  	}
   418  	return state, evts
   419  }
   420  
   421  func (c *pointerCollector) Reset() {
   422  	c.q.reset()
   423  	c.resetState()
   424  	c.ensureRoot()
   425  }
   426  
   427  // Ensure implicit root area for semantic descriptions to hang onto.
   428  func (c *pointerCollector) ensureRoot() {
   429  	if len(c.q.areas) > 0 {
   430  		return
   431  	}
   432  	c.pushArea(areaRect, image.Rect(-1e6, -1e6, 1e6, 1e6))
   433  	// Make it semantic to ensure a single semantic root.
   434  	c.q.areas[0].semantic.valid = true
   435  }
   436  
   437  func (q *pointerQueue) assignSemIDs() {
   438  	if q.semantic.idsAssigned {
   439  		return
   440  	}
   441  	q.semantic.idsAssigned = true
   442  	for i, a := range q.areas {
   443  		if a.semantic.valid {
   444  			q.areas[i].semantic.id = q.semanticIDFor(a.semantic.content)
   445  		}
   446  	}
   447  }
   448  
   449  func (q *pointerQueue) AppendSemantics(nodes []SemanticNode) []SemanticNode {
   450  	q.assignSemIDs()
   451  	nodes = q.appendSemanticChildren(nodes, 0)
   452  	nodes = q.appendSemanticArea(nodes, 0, 0)
   453  	return nodes
   454  }
   455  
   456  func (q *pointerQueue) appendSemanticArea(nodes []SemanticNode, parentID SemanticID, nodeIdx int) []SemanticNode {
   457  	areaIdx := nodes[nodeIdx].areaIdx
   458  	a := q.areas[areaIdx]
   459  	childStart := len(nodes)
   460  	nodes = q.appendSemanticChildren(nodes, a.firstChild)
   461  	childEnd := len(nodes)
   462  	for i := childStart; i < childEnd; i++ {
   463  		nodes = q.appendSemanticArea(nodes, a.semantic.id, i)
   464  	}
   465  	n := &nodes[nodeIdx]
   466  	n.ParentID = parentID
   467  	n.Children = nodes[childStart:childEnd]
   468  	return nodes
   469  }
   470  
   471  func (q *pointerQueue) appendSemanticChildren(nodes []SemanticNode, areaIdx int) []SemanticNode {
   472  	if areaIdx == -1 {
   473  		return nodes
   474  	}
   475  	a := q.areas[areaIdx]
   476  	if semID := a.semantic.id; semID != 0 {
   477  		cnt := a.semantic.content
   478  		nodes = append(nodes, SemanticNode{
   479  			ID: semID,
   480  			Desc: SemanticDesc{
   481  				Bounds:      a.bounds(),
   482  				Label:       cnt.label,
   483  				Description: cnt.desc,
   484  				Class:       cnt.class,
   485  				Gestures:    cnt.gestures,
   486  				Selected:    cnt.selected,
   487  				Disabled:    cnt.disabled,
   488  			},
   489  			areaIdx: areaIdx,
   490  		})
   491  	} else {
   492  		nodes = q.appendSemanticChildren(nodes, a.firstChild)
   493  	}
   494  	return q.appendSemanticChildren(nodes, a.sibling)
   495  }
   496  
   497  func (q *pointerQueue) semanticIDFor(content semanticContent) SemanticID {
   498  	ids := q.semantic.contentIDs[content]
   499  	for i, id := range ids {
   500  		if !id.used {
   501  			ids[i].used = true
   502  			return id.id
   503  		}
   504  	}
   505  	// No prior assigned ID; allocate a new one.
   506  	q.semantic.lastID++
   507  	id := semanticID{id: q.semantic.lastID, used: true}
   508  	if q.semantic.contentIDs == nil {
   509  		q.semantic.contentIDs = make(map[semanticContent][]semanticID)
   510  	}
   511  	q.semantic.contentIDs[content] = append(q.semantic.contentIDs[content], id)
   512  	return id.id
   513  }
   514  
   515  func (q *pointerQueue) ActionAt(pos f32.Point) (action system.Action, hasAction bool) {
   516  	q.hitTest(pos, func(n *hitNode) bool {
   517  		area := q.areas[n.area]
   518  		if area.action != 0 {
   519  			action = area.action
   520  			hasAction = true
   521  			return false
   522  		}
   523  		return true
   524  	})
   525  	return action, hasAction
   526  }
   527  
   528  func (q *pointerQueue) SemanticAt(pos f32.Point) (semID SemanticID, hasSemID bool) {
   529  	q.assignSemIDs()
   530  	q.hitTest(pos, func(n *hitNode) bool {
   531  		area := q.areas[n.area]
   532  		if area.semantic.id != 0 {
   533  			semID = area.semantic.id
   534  			hasSemID = true
   535  			return false
   536  		}
   537  		return true
   538  	})
   539  	return semID, hasSemID
   540  }
   541  
   542  // hitTest searches the hit tree for nodes matching pos. Any node matching pos will
   543  // have the onNode func invoked on it to allow the caller to extract whatever information
   544  // is necessary for further processing. onNode may return false to terminate the walk of
   545  // the hit tree, or true to continue. Providing this algorithm in this generic way
   546  // allows normal event routing and system action event routing to share the same traversal
   547  // logic even though they are interested in different aspects of hit nodes.
   548  func (q *pointerQueue) hitTest(pos f32.Point, onNode func(*hitNode) bool) pointer.Cursor {
   549  	// Track whether we're passing through hits.
   550  	pass := true
   551  	idx := len(q.hitTree) - 1
   552  	cursor := pointer.CursorDefault
   553  	for idx >= 0 {
   554  		n := &q.hitTree[idx]
   555  		hit, c := q.hit(n.area, pos)
   556  		if !hit {
   557  			idx--
   558  			continue
   559  		}
   560  		if cursor == pointer.CursorDefault {
   561  			cursor = c
   562  		}
   563  		pass = pass && n.pass
   564  		if pass {
   565  			idx--
   566  		} else {
   567  			idx = n.next
   568  		}
   569  		if !onNode(n) {
   570  			break
   571  		}
   572  	}
   573  	return cursor
   574  }
   575  
   576  func (q *pointerQueue) invTransform(areaIdx int, p f32.Point) f32.Point {
   577  	if areaIdx == -1 {
   578  		return p
   579  	}
   580  	return q.areas[areaIdx].trans.Invert().Transform(p)
   581  }
   582  
   583  func (q *pointerQueue) hit(areaIdx int, p f32.Point) (bool, pointer.Cursor) {
   584  	c := pointer.CursorDefault
   585  	for areaIdx != -1 {
   586  		a := &q.areas[areaIdx]
   587  		if c == pointer.CursorDefault {
   588  			c = a.cursor
   589  		}
   590  		p := a.trans.Invert().Transform(p)
   591  		if !a.area.Hit(p) {
   592  			return false, c
   593  		}
   594  		areaIdx = a.parent
   595  	}
   596  	return true, c
   597  }
   598  
   599  func (q *pointerQueue) reset() {
   600  	q.hitTree = q.hitTree[:0]
   601  	q.areas = q.areas[:0]
   602  	q.semantic.idsAssigned = false
   603  	for k, ids := range q.semantic.contentIDs {
   604  		for i := len(ids) - 1; i >= 0; i-- {
   605  			if !ids[i].used {
   606  				ids = append(ids[:i], ids[i+1:]...)
   607  			} else {
   608  				ids[i].used = false
   609  			}
   610  		}
   611  		if len(ids) > 0 {
   612  			q.semantic.contentIDs[k] = ids
   613  		} else {
   614  			delete(q.semantic.contentIDs, k)
   615  		}
   616  	}
   617  }
   618  
   619  func (q *pointerQueue) Frame(handlers map[event.Tag]*handler, state pointerState) (pointerState, []taggedEvent) {
   620  	for _, h := range handlers {
   621  		if h.pointer.areaPlusOne != 0 {
   622  			area := &q.areas[h.pointer.areaPlusOne-1]
   623  			if h.filter.pointer.kinds&(pointer.Press|pointer.Release) != 0 {
   624  				area.semantic.content.gestures |= ClickGesture
   625  			}
   626  			if h.filter.pointer.kinds&pointer.Scroll != 0 {
   627  				area.semantic.content.gestures |= ScrollGesture
   628  			}
   629  			area.semantic.valid = area.semantic.content.gestures != 0
   630  		}
   631  	}
   632  	var evts []taggedEvent
   633  	for i, p := range state.pointers {
   634  		changed := false
   635  		p, evts, state.cursor, changed = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, p.last)
   636  		if changed {
   637  			state.pointers = append([]pointerInfo{}, state.pointers...)
   638  			state.pointers[i] = p
   639  		}
   640  	}
   641  	return state, evts
   642  }
   643  
   644  func dropHandler(state pointerState, tag event.Tag) pointerState {
   645  	pointers := state.pointers
   646  	state.pointers = nil
   647  	for _, p := range pointers {
   648  		handlers := p.handlers
   649  		p.handlers = nil
   650  		for _, h := range handlers {
   651  			if h != tag {
   652  				p.handlers = append(p.handlers, h)
   653  			}
   654  		}
   655  		entered := p.entered
   656  		p.entered = nil
   657  		for _, h := range entered {
   658  			if h != tag {
   659  				p.entered = append(p.entered, h)
   660  			}
   661  		}
   662  		state.pointers = append(state.pointers, p)
   663  	}
   664  	return state
   665  }
   666  
   667  // pointerOf returns the pointerInfo index corresponding to the pointer in e.
   668  func (s pointerState) pointerOf(e pointer.Event) (pointerState, int) {
   669  	for i, p := range s.pointers {
   670  		if p.id == e.PointerID {
   671  			return s, i
   672  		}
   673  	}
   674  	n := len(s.pointers)
   675  	s.pointers = append(s.pointers[:n:n], pointerInfo{id: e.PointerID})
   676  	return s, len(s.pointers) - 1
   677  }
   678  
   679  // Deliver is like Push, but delivers an event to a particular area.
   680  func (q *pointerQueue) Deliver(handlers map[event.Tag]*handler, areaIdx int, e pointer.Event) []taggedEvent {
   681  	scroll := e.Scroll
   682  	idx := len(q.hitTree) - 1
   683  	// Locate first potential receiver.
   684  	for idx != -1 {
   685  		n := &q.hitTree[idx]
   686  		if n.area == areaIdx {
   687  			break
   688  		}
   689  		idx--
   690  	}
   691  	var evts []taggedEvent
   692  	for idx != -1 {
   693  		n := &q.hitTree[idx]
   694  		idx = n.next
   695  		h, ok := handlers[n.tag]
   696  		if !ok || !h.filter.pointer.Matches(e) {
   697  			continue
   698  		}
   699  		e := e
   700  		if e.Kind == pointer.Scroll {
   701  			if scroll == (f32.Point{}) {
   702  				break
   703  			}
   704  			scroll, e.Scroll = h.filter.pointer.clampScroll(scroll)
   705  		}
   706  		e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position)
   707  		evts = append(evts, taggedEvent{tag: n.tag, event: e})
   708  		if e.Kind != pointer.Scroll {
   709  			break
   710  		}
   711  	}
   712  	return evts
   713  }
   714  
   715  // SemanticArea returns the sematic content for area, and its parent area.
   716  func (q *pointerQueue) SemanticArea(areaIdx int) (semanticContent, int) {
   717  	for areaIdx != -1 {
   718  		a := &q.areas[areaIdx]
   719  		areaIdx = a.parent
   720  		if !a.semantic.valid {
   721  			continue
   722  		}
   723  		return a.semantic.content, areaIdx
   724  	}
   725  	return semanticContent{}, -1
   726  }
   727  
   728  func (q *pointerQueue) Push(handlers map[event.Tag]*handler, state pointerState, e pointer.Event) (pointerState, []taggedEvent) {
   729  	var evts []taggedEvent
   730  	if e.Kind == pointer.Cancel {
   731  		for k := range handlers {
   732  			evts = append(evts, taggedEvent{
   733  				event: pointer.Event{Kind: pointer.Cancel},
   734  				tag:   k,
   735  			})
   736  		}
   737  		state.pointers = nil
   738  		return state, evts
   739  	}
   740  	state, pidx := state.pointerOf(e)
   741  	p := state.pointers[pidx]
   742  
   743  	switch e.Kind {
   744  	case pointer.Press:
   745  		p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
   746  		p.pressed = true
   747  		evts = q.deliverEvent(handlers, p, evts, e)
   748  	case pointer.Move:
   749  		if p.pressed {
   750  			e.Kind = pointer.Drag
   751  		}
   752  		p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
   753  		evts = q.deliverEvent(handlers, p, evts, e)
   754  		if p.pressed {
   755  			p, evts = q.deliverDragEvent(handlers, p, evts)
   756  		}
   757  	case pointer.Release:
   758  		evts = q.deliverEvent(handlers, p, evts, e)
   759  		p.pressed = false
   760  		p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
   761  		p, evts = q.deliverDropEvent(handlers, p, evts)
   762  	case pointer.Scroll:
   763  		p, evts, state.cursor, _ = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, e)
   764  		evts = q.deliverEvent(handlers, p, evts, e)
   765  	default:
   766  		panic("unsupported pointer event type")
   767  	}
   768  
   769  	p.last = e
   770  
   771  	if !p.pressed && len(p.entered) == 0 {
   772  		// No longer need to track pointer.
   773  		state.pointers = append(state.pointers[:pidx:pidx], state.pointers[pidx+1:]...)
   774  	} else {
   775  		state.pointers = append([]pointerInfo{}, state.pointers...)
   776  		state.pointers[pidx] = p
   777  	}
   778  	return state, evts
   779  }
   780  
   781  func (q *pointerQueue) deliverEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent, e pointer.Event) []taggedEvent {
   782  	foremost := true
   783  	if p.pressed && len(p.handlers) == 1 {
   784  		e.Priority = pointer.Grabbed
   785  		foremost = false
   786  	}
   787  	scroll := e.Scroll
   788  	for _, k := range p.handlers {
   789  		h, ok := handlers[k]
   790  		if !ok {
   791  			continue
   792  		}
   793  		f := h.filter.pointer
   794  		if !f.Matches(e) {
   795  			continue
   796  		}
   797  		if e.Kind == pointer.Scroll {
   798  			if scroll == (f32.Point{}) {
   799  				return evts
   800  			}
   801  			scroll, e.Scroll = f.clampScroll(scroll)
   802  		}
   803  		e := e
   804  		if foremost {
   805  			foremost = false
   806  			e.Priority = pointer.Foremost
   807  		}
   808  		e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position)
   809  		evts = append(evts, taggedEvent{event: e, tag: k})
   810  	}
   811  	return evts
   812  }
   813  
   814  func (q *pointerQueue) deliverEnterLeaveEvents(handlers map[event.Tag]*handler, cursor pointer.Cursor, p pointerInfo, evts []taggedEvent, e pointer.Event) (pointerInfo, []taggedEvent, pointer.Cursor, bool) {
   815  	changed := false
   816  	var hits []event.Tag
   817  	if e.Source != pointer.Mouse && !p.pressed && e.Kind != pointer.Press {
   818  		// Consider non-mouse pointers leaving when they're released.
   819  	} else {
   820  		var transSrc *pointerFilter
   821  		if p.dataSource != nil {
   822  			transSrc = &handlers[p.dataSource].filter.pointer
   823  		}
   824  		cursor = q.hitTest(e.Position, func(n *hitNode) bool {
   825  			h, ok := handlers[n.tag]
   826  			if !ok {
   827  				return true
   828  			}
   829  			add := true
   830  			if p.pressed {
   831  				add = false
   832  				// Filter out non-participating handlers,
   833  				// except potential transfer targets when a transfer has been initiated.
   834  				if _, found := searchTag(p.handlers, n.tag); found {
   835  					add = true
   836  				}
   837  				if transSrc != nil {
   838  					if _, ok := firstMimeMatch(transSrc, &h.filter.pointer); ok {
   839  						add = true
   840  					}
   841  				}
   842  			}
   843  			if add {
   844  				hits = addHandler(hits, n.tag)
   845  			}
   846  			return true
   847  		})
   848  		if !p.pressed {
   849  			changed = true
   850  			p.handlers = hits
   851  		}
   852  	}
   853  	// Deliver Leave events.
   854  	for _, k := range p.entered {
   855  		if _, found := searchTag(hits, k); found {
   856  			continue
   857  		}
   858  		h, ok := handlers[k]
   859  		if !ok {
   860  			continue
   861  		}
   862  		changed = true
   863  		e := e
   864  		e.Kind = pointer.Leave
   865  
   866  		if h.filter.pointer.Matches(e) {
   867  			e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position)
   868  			evts = append(evts, taggedEvent{tag: k, event: e})
   869  		}
   870  	}
   871  	// Deliver Enter events.
   872  	for _, k := range hits {
   873  		if _, found := searchTag(p.entered, k); found {
   874  			continue
   875  		}
   876  		h, ok := handlers[k]
   877  		if !ok {
   878  			continue
   879  		}
   880  		changed = true
   881  		e := e
   882  		e.Kind = pointer.Enter
   883  
   884  		if h.filter.pointer.Matches(e) {
   885  			e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position)
   886  			evts = append(evts, taggedEvent{tag: k, event: e})
   887  		}
   888  	}
   889  	p.entered = hits
   890  	return p, evts, cursor, changed
   891  }
   892  
   893  func (q *pointerQueue) deliverDragEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) {
   894  	if p.dataSource != nil {
   895  		return p, evts
   896  	}
   897  	// Identify the data source.
   898  	for _, k := range p.entered {
   899  		src := &handlers[k].filter.pointer
   900  		if len(src.sourceMimes) == 0 {
   901  			continue
   902  		}
   903  		// One data source handler per pointer.
   904  		p.dataSource = k
   905  		// Notify all potential targets.
   906  		for k, tgt := range handlers {
   907  			if _, ok := firstMimeMatch(src, &tgt.filter.pointer); ok {
   908  				evts = append(evts, taggedEvent{tag: k, event: transfer.InitiateEvent{}})
   909  			}
   910  		}
   911  		break
   912  	}
   913  	return p, evts
   914  }
   915  
   916  func (q *pointerQueue) deliverDropEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) {
   917  	if p.dataSource == nil {
   918  		return p, evts
   919  	}
   920  	// Request data from the source.
   921  	src := &handlers[p.dataSource].filter.pointer
   922  	for _, k := range p.entered {
   923  		h := handlers[k]
   924  		if m, ok := firstMimeMatch(src, &h.filter.pointer); ok {
   925  			p.dataTarget = k
   926  			evts = append(evts, taggedEvent{tag: p.dataSource, event: transfer.RequestEvent{Type: m}})
   927  			return p, evts
   928  		}
   929  	}
   930  	// No valid target found, abort.
   931  	return q.deliverTransferCancelEvent(handlers, p, evts)
   932  }
   933  
   934  func (q *pointerQueue) deliverTransferCancelEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent) (pointerInfo, []taggedEvent) {
   935  	evts = append(evts, taggedEvent{tag: p.dataSource, event: transfer.CancelEvent{}})
   936  	// Cancel all potential targets.
   937  	src := &handlers[p.dataSource].filter.pointer
   938  	for k, h := range handlers {
   939  		if _, ok := firstMimeMatch(src, &h.filter.pointer); ok {
   940  			evts = append(evts, taggedEvent{tag: k, event: transfer.CancelEvent{}})
   941  		}
   942  	}
   943  	p.dataSource = nil
   944  	p.dataTarget = nil
   945  	return p, evts
   946  }
   947  
   948  // ClipFor clips r to the parents of area.
   949  func (q *pointerQueue) ClipFor(area int, r image.Rectangle) image.Rectangle {
   950  	a := &q.areas[area]
   951  	parent := a.parent
   952  	for parent != -1 {
   953  		a := &q.areas[parent]
   954  		r = r.Intersect(a.bounds())
   955  		parent = a.parent
   956  	}
   957  	return r
   958  }
   959  
   960  func searchTag(tags []event.Tag, tag event.Tag) (int, bool) {
   961  	for i, t := range tags {
   962  		if t == tag {
   963  			return i, true
   964  		}
   965  	}
   966  	return 0, false
   967  }
   968  
   969  // addHandler adds tag to the slice if not present.
   970  func addHandler(tags []event.Tag, tag event.Tag) []event.Tag {
   971  	for _, t := range tags {
   972  		if t == tag {
   973  			return tags
   974  		}
   975  	}
   976  	return append(tags, tag)
   977  }
   978  
   979  // firstMimeMatch returns the first type match between src and tgt.
   980  func firstMimeMatch(src, tgt *pointerFilter) (first string, matched bool) {
   981  	for _, m1 := range tgt.targetMimes {
   982  		for _, m2 := range src.sourceMimes {
   983  			if m1 == m2 {
   984  				return m1, true
   985  			}
   986  		}
   987  	}
   988  	return "", false
   989  }
   990  
   991  func (op *areaOp) Hit(pos f32.Point) bool {
   992  	pos = pos.Sub(f32internal.FPt(op.rect.Min))
   993  	size := f32internal.FPt(op.rect.Size())
   994  	switch op.kind {
   995  	case areaRect:
   996  		return 0 <= pos.X && pos.X < size.X &&
   997  			0 <= pos.Y && pos.Y < size.Y
   998  	case areaEllipse:
   999  		rx := size.X / 2
  1000  		ry := size.Y / 2
  1001  		xh := pos.X - rx
  1002  		yk := pos.Y - ry
  1003  		// The ellipse function works in all cases because
  1004  		// 0/0 is not <= 1.
  1005  		return (xh*xh)/(rx*rx)+(yk*yk)/(ry*ry) <= 1
  1006  	default:
  1007  		panic("invalid area kind")
  1008  	}
  1009  }
  1010  
  1011  func (a *areaNode) bounds() image.Rectangle {
  1012  	return f32internal.Rectangle{
  1013  		Min: a.trans.Transform(f32internal.FPt(a.area.rect.Min)),
  1014  		Max: a.trans.Transform(f32internal.FPt(a.area.rect.Max)),
  1015  	}.Round()
  1016  }