github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/gio/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  	"github.com/gop9/olt/gio/f32"
    10  	"github.com/gop9/olt/gio/internal/opconst"
    11  	"github.com/gop9/olt/gio/internal/ops"
    12  	"github.com/gop9/olt/gio/io/event"
    13  	"github.com/gop9/olt/gio/io/pointer"
    14  	"github.com/gop9/olt/gio/op"
    15  )
    16  
    17  type pointerQueue struct {
    18  	hitTree  []hitNode
    19  	areas    []areaNode
    20  	handlers map[event.Key]*pointerHandler
    21  	pointers []pointerInfo
    22  	reader   ops.Reader
    23  	scratch  []event.Key
    24  }
    25  
    26  type hitNode struct {
    27  	next int
    28  	area int
    29  	// Pass tracks the most recent PassOp mode.
    30  	pass bool
    31  
    32  	// For handler nodes.
    33  	key event.Key
    34  }
    35  
    36  type pointerInfo struct {
    37  	id       pointer.ID
    38  	pressed  bool
    39  	handlers []event.Key
    40  }
    41  
    42  type pointerHandler struct {
    43  	area      int
    44  	active    bool
    45  	transform op.TransformOp
    46  	wantsGrab bool
    47  }
    48  
    49  type areaOp struct {
    50  	kind areaKind
    51  	rect image.Rectangle
    52  }
    53  
    54  type areaNode struct {
    55  	trans op.TransformOp
    56  	next  int
    57  	area  areaOp
    58  }
    59  
    60  type areaKind uint8
    61  
    62  const (
    63  	areaRect areaKind = iota
    64  	areaEllipse
    65  )
    66  
    67  func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t op.TransformOp, area, node int, pass bool) {
    68  	for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() {
    69  		switch opconst.OpType(encOp.Data[0]) {
    70  		case opconst.TypePush:
    71  			q.collectHandlers(r, events, t, area, node, pass)
    72  		case opconst.TypePop:
    73  			return
    74  		case opconst.TypePass:
    75  			op := decodePassOp(encOp.Data)
    76  			pass = op.Pass
    77  		case opconst.TypeArea:
    78  			var op areaOp
    79  			op.Decode(encOp.Data)
    80  			q.areas = append(q.areas, areaNode{trans: t, next: area, area: op})
    81  			area = len(q.areas) - 1
    82  			q.hitTree = append(q.hitTree, hitNode{
    83  				next: node,
    84  				area: area,
    85  				pass: pass,
    86  			})
    87  			node = len(q.hitTree) - 1
    88  		case opconst.TypeTransform:
    89  			dop := ops.DecodeTransformOp(encOp.Data)
    90  			t = t.Multiply(op.TransformOp(dop))
    91  		case opconst.TypePointerInput:
    92  			op := decodePointerInputOp(encOp.Data, encOp.Refs)
    93  			q.hitTree = append(q.hitTree, hitNode{
    94  				next: node,
    95  				area: area,
    96  				pass: pass,
    97  				key:  op.Key,
    98  			})
    99  			node = len(q.hitTree) - 1
   100  			h, ok := q.handlers[op.Key]
   101  			if !ok {
   102  				h = new(pointerHandler)
   103  				q.handlers[op.Key] = h
   104  				events.Set(op.Key, []event.Event{pointer.Event{Type: pointer.Cancel}})
   105  			}
   106  			h.active = true
   107  			h.area = area
   108  			h.transform = t
   109  			h.wantsGrab = h.wantsGrab || op.Grab
   110  		}
   111  	}
   112  }
   113  
   114  func (q *pointerQueue) opHit(handlers *[]event.Key, pos f32.Point) {
   115  	// Track whether we're passing through hits.
   116  	pass := true
   117  	idx := len(q.hitTree) - 1
   118  	for idx >= 0 {
   119  		n := &q.hitTree[idx]
   120  		if !q.hit(n.area, pos) {
   121  			idx--
   122  			continue
   123  		}
   124  		pass = pass && n.pass
   125  		if pass {
   126  			idx--
   127  		} else {
   128  			idx = n.next
   129  		}
   130  		if n.key != nil {
   131  			if _, exists := q.handlers[n.key]; exists {
   132  				*handlers = append(*handlers, n.key)
   133  			}
   134  
   135  		}
   136  	}
   137  }
   138  
   139  func (q *pointerQueue) hit(areaIdx int, p f32.Point) bool {
   140  	for areaIdx != -1 {
   141  		a := &q.areas[areaIdx]
   142  		if !a.hit(p) {
   143  			return false
   144  		}
   145  		areaIdx = a.next
   146  	}
   147  	return true
   148  }
   149  
   150  func (a *areaNode) hit(p f32.Point) bool {
   151  	p = a.trans.Invert().Transform(p)
   152  	return a.area.Hit(p)
   153  }
   154  
   155  func (q *pointerQueue) init() {
   156  	if q.handlers == nil {
   157  		q.handlers = make(map[event.Key]*pointerHandler)
   158  	}
   159  }
   160  
   161  func (q *pointerQueue) Frame(root *op.Ops, events *handlerEvents) {
   162  	q.init()
   163  	for _, h := range q.handlers {
   164  		// Reset handler.
   165  		h.active = false
   166  	}
   167  	q.hitTree = q.hitTree[:0]
   168  	q.areas = q.areas[:0]
   169  	q.reader.Reset(root)
   170  	q.collectHandlers(&q.reader, events, op.TransformOp{}, -1, -1, false)
   171  	for k, h := range q.handlers {
   172  		if !h.active {
   173  			q.dropHandler(k, events)
   174  			delete(q.handlers, k)
   175  		}
   176  	}
   177  }
   178  
   179  func (q *pointerQueue) dropHandler(k event.Key, events *handlerEvents) {
   180  	events.Add(k, pointer.Event{Type: pointer.Cancel})
   181  	q.handlers[k].wantsGrab = false
   182  	for i := range q.pointers {
   183  		p := &q.pointers[i]
   184  		for i := len(p.handlers) - 1; i >= 0; i-- {
   185  			if p.handlers[i] == k {
   186  				p.handlers = append(p.handlers[:i], p.handlers[i+1:]...)
   187  			}
   188  		}
   189  	}
   190  }
   191  
   192  func (q *pointerQueue) Push(e pointer.Event, events *handlerEvents) {
   193  	q.init()
   194  	if e.Type == pointer.Cancel {
   195  		q.pointers = q.pointers[:0]
   196  		for k := range q.handlers {
   197  			q.dropHandler(k, events)
   198  		}
   199  		return
   200  	}
   201  	pidx := -1
   202  	for i, p := range q.pointers {
   203  		if p.id == e.PointerID {
   204  			pidx = i
   205  			break
   206  		}
   207  	}
   208  	if pidx == -1 {
   209  		q.pointers = append(q.pointers, pointerInfo{id: e.PointerID})
   210  		pidx = len(q.pointers) - 1
   211  	}
   212  	p := &q.pointers[pidx]
   213  	if !p.pressed && (e.Type == pointer.Move || e.Type == pointer.Press) {
   214  		p.handlers, q.scratch = q.scratch[:0], p.handlers
   215  		q.opHit(&p.handlers, e.Position)
   216  		if e.Type == pointer.Press {
   217  			p.pressed = true
   218  		}
   219  	}
   220  	if p.pressed {
   221  		// Resolve grabs.
   222  		q.scratch = q.scratch[:0]
   223  		for i, k := range p.handlers {
   224  			h := q.handlers[k]
   225  			if h.wantsGrab {
   226  				q.scratch = append(q.scratch, p.handlers[:i]...)
   227  				q.scratch = append(q.scratch, p.handlers[i+1:]...)
   228  				break
   229  			}
   230  		}
   231  		// Drop handlers that lost their grab.
   232  		for _, k := range q.scratch {
   233  			q.dropHandler(k, events)
   234  		}
   235  	}
   236  	if e.Type == pointer.Release {
   237  		q.pointers = append(q.pointers[:pidx], q.pointers[pidx+1:]...)
   238  	}
   239  	for _, k := range p.handlers {
   240  		h := q.handlers[k]
   241  		e := e
   242  		if p.pressed && len(p.handlers) == 1 {
   243  			e.Priority = pointer.Grabbed
   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].(event.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  }