gioui.org/ui@v0.0.0-20190926171558-ce74bc0cbaea/app/internal/input/pointer.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package input
     4  
     5  import (
     6  	"encoding/binary"
     7  	"image"
     8  
     9  	"gioui.org/ui"
    10  	"gioui.org/ui/f32"
    11  	"gioui.org/ui/internal/opconst"
    12  	"gioui.org/ui/internal/ops"
    13  	"gioui.org/ui/pointer"
    14  )
    15  
    16  type pointerQueue struct {
    17  	hitTree  []hitNode
    18  	areas    []areaNode
    19  	handlers map[ui.Key]*pointerHandler
    20  	pointers []pointerInfo
    21  	reader   ops.Reader
    22  	scratch  []ui.Key
    23  }
    24  
    25  type hitNode struct {
    26  	next int
    27  	area int
    28  	// Pass tracks the most recent PassOp mode.
    29  	pass bool
    30  
    31  	// For handler nodes.
    32  	key ui.Key
    33  }
    34  
    35  type pointerInfo struct {
    36  	id       pointer.ID
    37  	pressed  bool
    38  	handlers []ui.Key
    39  }
    40  
    41  type pointerHandler struct {
    42  	area      int
    43  	active    bool
    44  	transform ui.TransformOp
    45  	wantsGrab bool
    46  }
    47  
    48  type areaOp struct {
    49  	kind areaKind
    50  	rect image.Rectangle
    51  }
    52  
    53  type areaNode struct {
    54  	trans ui.TransformOp
    55  	next  int
    56  	area  areaOp
    57  }
    58  
    59  type areaKind uint8
    60  
    61  const (
    62  	areaRect areaKind = iota
    63  	areaEllipse
    64  )
    65  
    66  func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t ui.TransformOp, area, node int, pass bool) {
    67  	for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() {
    68  		switch opconst.OpType(encOp.Data[0]) {
    69  		case opconst.TypePush:
    70  			q.collectHandlers(r, events, t, area, node, pass)
    71  		case opconst.TypePop:
    72  			return
    73  		case opconst.TypePass:
    74  			op := decodePassOp(encOp.Data)
    75  			pass = op.Pass
    76  		case opconst.TypeArea:
    77  			var op areaOp
    78  			op.Decode(encOp.Data)
    79  			q.areas = append(q.areas, areaNode{trans: t, next: area, area: op})
    80  			area = len(q.areas) - 1
    81  			q.hitTree = append(q.hitTree, hitNode{
    82  				next: node,
    83  				area: area,
    84  				pass: pass,
    85  			})
    86  			node = len(q.hitTree) - 1
    87  		case opconst.TypeTransform:
    88  			op := ops.DecodeTransformOp(encOp.Data)
    89  			t = t.Multiply(ui.TransformOp(op))
    90  		case opconst.TypePointerInput:
    91  			op := decodePointerInputOp(encOp.Data, encOp.Refs)
    92  			q.hitTree = append(q.hitTree, hitNode{
    93  				next: node,
    94  				area: area,
    95  				pass: pass,
    96  				key:  op.Key,
    97  			})
    98  			node = len(q.hitTree) - 1
    99  			h, ok := q.handlers[op.Key]
   100  			if !ok {
   101  				h = new(pointerHandler)
   102  				q.handlers[op.Key] = h
   103  				events.Set(op.Key, []ui.Event{pointer.Event{Type: pointer.Cancel}})
   104  			}
   105  			h.active = true
   106  			h.area = area
   107  			h.transform = t
   108  			h.wantsGrab = h.wantsGrab || op.Grab
   109  		}
   110  	}
   111  }
   112  
   113  func (q *pointerQueue) opHit(handlers *[]ui.Key, pos f32.Point) {
   114  	// Track whether we're passing through hits.
   115  	pass := true
   116  	idx := len(q.hitTree) - 1
   117  	for idx >= 0 {
   118  		n := &q.hitTree[idx]
   119  		if !q.hit(n.area, pos) {
   120  			idx--
   121  			continue
   122  		}
   123  		pass = pass && n.pass
   124  		if pass {
   125  			idx--
   126  		} else {
   127  			idx = n.next
   128  		}
   129  		if n.key != nil {
   130  			if _, exists := q.handlers[n.key]; exists {
   131  				*handlers = append(*handlers, n.key)
   132  			}
   133  
   134  		}
   135  	}
   136  }
   137  
   138  func (q *pointerQueue) hit(areaIdx int, p f32.Point) bool {
   139  	for areaIdx != -1 {
   140  		a := &q.areas[areaIdx]
   141  		if !a.hit(p) {
   142  			return false
   143  		}
   144  		areaIdx = a.next
   145  	}
   146  	return true
   147  }
   148  
   149  func (a *areaNode) hit(p f32.Point) bool {
   150  	p = a.trans.Invert().Transform(p)
   151  	return a.area.Hit(p)
   152  }
   153  
   154  func (q *pointerQueue) init() {
   155  	if q.handlers == nil {
   156  		q.handlers = make(map[ui.Key]*pointerHandler)
   157  	}
   158  }
   159  
   160  func (q *pointerQueue) Frame(root *ui.Ops, events *handlerEvents) {
   161  	q.init()
   162  	for _, h := range q.handlers {
   163  		// Reset handler.
   164  		h.active = false
   165  	}
   166  	q.hitTree = q.hitTree[:0]
   167  	q.areas = q.areas[:0]
   168  	q.reader.Reset(root)
   169  	q.collectHandlers(&q.reader, events, ui.TransformOp{}, -1, -1, false)
   170  	for k, h := range q.handlers {
   171  		if !h.active {
   172  			q.dropHandler(k)
   173  			delete(q.handlers, k)
   174  		}
   175  	}
   176  }
   177  
   178  func (q *pointerQueue) dropHandler(k ui.Key) {
   179  	for i := range q.pointers {
   180  		p := &q.pointers[i]
   181  		for i := len(p.handlers) - 1; i >= 0; i-- {
   182  			if p.handlers[i] == k {
   183  				p.handlers = append(p.handlers[:i], p.handlers[i+1:]...)
   184  			}
   185  		}
   186  	}
   187  }
   188  
   189  func (q *pointerQueue) Push(e pointer.Event, events *handlerEvents) {
   190  	q.init()
   191  	if e.Type == pointer.Cancel {
   192  		q.pointers = q.pointers[:0]
   193  		for k := range q.handlers {
   194  			q.dropHandler(k)
   195  		}
   196  		return
   197  	}
   198  	pidx := -1
   199  	for i, p := range q.pointers {
   200  		if p.id == e.PointerID {
   201  			pidx = i
   202  			break
   203  		}
   204  	}
   205  	if pidx == -1 {
   206  		q.pointers = append(q.pointers, pointerInfo{id: e.PointerID})
   207  		pidx = len(q.pointers) - 1
   208  	}
   209  	p := &q.pointers[pidx]
   210  	if !p.pressed && (e.Type == pointer.Move || e.Type == pointer.Press) {
   211  		p.handlers, q.scratch = q.scratch[:0], p.handlers
   212  		q.opHit(&p.handlers, e.Position)
   213  		if e.Type == pointer.Press {
   214  			p.pressed = true
   215  		}
   216  	}
   217  	if p.pressed {
   218  		// Resolve grabs.
   219  		q.scratch = q.scratch[:0]
   220  		for i, k := range p.handlers {
   221  			h := q.handlers[k]
   222  			if h.wantsGrab {
   223  				q.scratch = append(q.scratch, p.handlers[:i]...)
   224  				q.scratch = append(q.scratch, p.handlers[i+1:]...)
   225  				break
   226  			}
   227  		}
   228  		// Drop handlers that lost their grab.
   229  		for _, k := range q.scratch {
   230  			q.dropHandler(k)
   231  		}
   232  	}
   233  	if e.Type == pointer.Release {
   234  		q.pointers = append(q.pointers[:pidx], q.pointers[pidx+1:]...)
   235  	}
   236  	for i, k := range p.handlers {
   237  		h := q.handlers[k]
   238  		e := e
   239  		switch {
   240  		case p.pressed && len(p.handlers) == 1:
   241  			e.Priority = pointer.Grabbed
   242  		case i == 0:
   243  			e.Priority = pointer.Foremost
   244  		}
   245  		e.Hit = q.hit(h.area, e.Position)
   246  		e.Position = h.transform.Invert().Transform(e.Position)
   247  		events.Add(k, e)
   248  		if e.Type == pointer.Release {
   249  			// Release grab when the number of grabs reaches zero.
   250  			grabs := 0
   251  			for _, p := range q.pointers {
   252  				if p.pressed && len(p.handlers) == 1 && p.handlers[0] == k {
   253  					grabs++
   254  				}
   255  			}
   256  			if grabs == 0 {
   257  				h.wantsGrab = false
   258  			}
   259  		}
   260  	}
   261  }
   262  
   263  func (op *areaOp) Decode(d []byte) {
   264  	if opconst.OpType(d[0]) != opconst.TypeArea {
   265  		panic("invalid op")
   266  	}
   267  	bo := binary.LittleEndian
   268  	rect := image.Rectangle{
   269  		Min: image.Point{
   270  			X: int(int32(bo.Uint32(d[2:]))),
   271  			Y: int(int32(bo.Uint32(d[6:]))),
   272  		},
   273  		Max: image.Point{
   274  			X: int(int32(bo.Uint32(d[10:]))),
   275  			Y: int(int32(bo.Uint32(d[14:]))),
   276  		},
   277  	}
   278  	*op = areaOp{
   279  		kind: areaKind(d[1]),
   280  		rect: rect,
   281  	}
   282  }
   283  
   284  func (op *areaOp) Hit(pos f32.Point) bool {
   285  	min := f32.Point{
   286  		X: float32(op.rect.Min.X),
   287  		Y: float32(op.rect.Min.Y),
   288  	}
   289  	pos = pos.Sub(min)
   290  	size := op.rect.Size()
   291  	switch op.kind {
   292  	case areaRect:
   293  		if 0 <= pos.X && pos.X < float32(size.X) &&
   294  			0 <= pos.Y && pos.Y < float32(size.Y) {
   295  			return true
   296  		} else {
   297  			return false
   298  		}
   299  	case areaEllipse:
   300  		rx := float32(size.X) / 2
   301  		ry := float32(size.Y) / 2
   302  		rx2 := rx * rx
   303  		ry2 := ry * ry
   304  		xh := pos.X - rx
   305  		yk := pos.Y - ry
   306  		if xh*xh*ry2+yk*yk*rx2 <= rx2*ry2 {
   307  			return true
   308  		} else {
   309  			return false
   310  		}
   311  	default:
   312  		panic("invalid area kind")
   313  	}
   314  }
   315  
   316  func decodePointerInputOp(d []byte, refs []interface{}) pointer.InputOp {
   317  	if opconst.OpType(d[0]) != opconst.TypePointerInput {
   318  		panic("invalid op")
   319  	}
   320  	return pointer.InputOp{
   321  		Grab: d[1] != 0,
   322  		Key:  refs[0].(ui.Key),
   323  	}
   324  }
   325  
   326  func decodePassOp(d []byte) pointer.PassOp {
   327  	if opconst.OpType(d[0]) != opconst.TypePass {
   328  		panic("invalid op")
   329  	}
   330  	return pointer.PassOp{
   331  		Pass: d[1] != 0,
   332  	}
   333  }