From 7c5bcd3db8204eb8809ae92f9e625a9f6cb7b6fc Mon Sep 17 00:00:00 2001 From: pierre Date: Tue, 8 Dec 2020 18:54:50 +0100 Subject: [PATCH] io/pointer: added CursorNameOp The cursor can now be customized for a given area. Signed-off-by: pierre --- app/window.go | 7 ++++++- internal/opconst/ops.go | 5 ++++- io/pointer/pointer.go | 10 ++++++++++ io/router/pointer.go | 28 ++++++++++++++++++++++++++++ io/router/router.go | 5 +++++ widget/editor.go | 5 ++++- 6 files changed, 57 insertions(+), 3 deletions(-) diff --git a/app/window.go b/app/window.go index a2b5c0ec..641e1441 100644 --- a/app/window.go +++ b/app/window.go @@ -48,7 +48,8 @@ type Window struct { nextFrame time.Time delayedDraw *time.Timer - queue queue + queue queue + cursor pointer.CursorName callbacks callbacks } @@ -414,6 +415,10 @@ func (w *Window) run(opts *window.Options) { w.setNextFrame(time.Time{}) w.updateAnimation() } + if c := w.queue.q.Cursor(); c != w.cursor { + w.cursor = c + w.SetCursorName(c) + } w.out <- e } w.ack <- struct{}{} diff --git a/internal/opconst/ops.go b/internal/opconst/ops.go index a1d1e62c..4c607538 100644 --- a/internal/opconst/ops.go +++ b/internal/opconst/ops.go @@ -30,6 +30,7 @@ const ( TypeAux TypeClip TypeProfile + TypeCursor ) const ( @@ -55,6 +56,7 @@ const ( TypeAuxLen = 1 TypeClipLen = 1 + 4*4 + 4 + 2 + 4 TypeProfileLen = 1 + TypeCursorLen = 1 + 1 ) func (t OpType) Size() int { @@ -81,12 +83,13 @@ func (t OpType) Size() int { TypeAuxLen, TypeClipLen, TypeProfileLen, + TypeCursorLen, }[t-firstOpIndex] } func (t OpType) NumRefs() int { switch t { - case TypeKeyInput, TypePointerInput, TypeProfile, TypeCall, TypeClipboardRead, TypeClipboardWrite: + case TypeKeyInput, TypePointerInput, TypeProfile, TypeCall, TypeClipboardRead, TypeClipboardWrite, TypeCursor: return 1 case TypeImage: return 2 diff --git a/io/pointer/pointer.go b/io/pointer/pointer.go index a5b6c2cc..9e054fbc 100644 --- a/io/pointer/pointer.go +++ b/io/pointer/pointer.go @@ -49,6 +49,11 @@ type AreaOp struct { rect image.Rectangle } +// CursorNameOp sets the cursor for the current area. +type CursorNameOp struct { + Name CursorName +} + // InputOp declares an input handler ready for pointer // events. type InputOp struct { @@ -178,6 +183,11 @@ func (op AreaOp) Add(o *op.Ops) { bo.PutUint32(data[14:], uint32(op.rect.Max.Y)) } +func (op CursorNameOp) Add(o *op.Ops) { + data := o.Write1(opconst.TypeCursorLen, op.Name) + data[0] = byte(opconst.TypeCursor) +} + func (h InputOp) Add(o *op.Ops) { data := o.Write1(opconst.TypePointerInputLen, h.Tag) data[0] = byte(opconst.TypePointerInput) diff --git a/io/router/pointer.go b/io/router/pointer.go index afc41fce..bd8a9e89 100644 --- a/io/router/pointer.go +++ b/io/router/pointer.go @@ -17,6 +17,8 @@ import ( type pointerQueue struct { hitTree []hitNode areas []areaNode + cursors []cursorNode + cursor pointer.CursorName handlers map[event.Tag]*pointerHandler pointers []pointerInfo reader ops.Reader @@ -34,6 +36,11 @@ type hitNode struct { tag event.Tag } +type cursorNode struct { + name pointer.CursorName + area int +} + type pointerInfo struct { id pointer.ID pressed bool @@ -114,6 +121,11 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t f h.area = area h.wantsGrab = h.wantsGrab || op.Grab h.types = h.types | op.Types + case opconst.TypeCursor: + q.cursors = append(q.cursors, cursorNode{ + name: encOp.Refs[0].(pointer.CursorName), + area: len(q.areas) - 1, + }) } } } @@ -177,6 +189,7 @@ func (q *pointerQueue) Frame(root *op.Ops, events *handlerEvents) { } q.hitTree = q.hitTree[:0] q.areas = q.areas[:0] + q.cursors = q.cursors[:0] q.reader.Reset(root) q.collectHandlers(&q.reader, events, f32.Affine2D{}, -1, -1, false) for k, h := range q.handlers { @@ -314,6 +327,7 @@ func (q *pointerQueue) deliverEnterLeaveEvents(p *pointerInfo, hits []event.Tag, if e.Type&h.types == e.Type { events.Add(k, e) + q.cursor = pointer.CursorDefault } } // Deliver Enter events. @@ -327,11 +341,25 @@ func (q *pointerQueue) deliverEnterLeaveEvents(p *pointerInfo, hits []event.Tag, if e.Type&h.types == e.Type { events.Add(k, e) + q.hitCursor(h.area) } } p.entered = append(p.entered[:0], hits...) } +func (q *pointerQueue) hitCursor(want int) { + for _, c := range q.cursors { + idx := c.area + for idx != -1 { + if idx == want { + q.cursor = c.name + return + } + idx = q.areas[idx].next + } + } +} + func searchTag(tags []event.Tag, tag event.Tag) (int, bool) { for i, t := range tags { if t == tag { diff --git a/io/router/router.go b/io/router/router.go index 803efa7c..485ec0c6 100644 --- a/io/router/router.go +++ b/io/router/router.go @@ -115,6 +115,11 @@ func (q *Router) ReadClipboard() bool { return q.cqueue.ReadClipboard() } +// Cursor returns the last cursor set. +func (q *Router) Cursor() pointer.CursorName { + return q.pqueue.cursor +} + func (q *Router) collect() { for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() { switch opconst.OpType(encOp.Data[0]) { diff --git a/widget/editor.go b/widget/editor.go index c0abeefb..75709679 100644 --- a/widget/editor.go +++ b/widget/editor.go @@ -379,7 +379,10 @@ func (e *Editor) Layout(gtx layout.Context, sh text.Shaper, font text.Font, size } e.makeValid() - return e.layout(gtx) + dims := e.layout(gtx) + pointer.Rect(image.Rectangle{Max: dims.Size}).Add(gtx.Ops) + pointer.CursorNameOp{Name: pointer.CursorText}.Add(gtx.Ops) + return dims } func (e *Editor) layout(gtx layout.Context) layout.Dimensions {