mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-08 10:55:35 +00:00
ui: add package input for merged input
To avoid passing a queue type for each kind of input (pointer, key), introduce package input for mapping a handler key to all input events. Future input sources can be added without changes to programs, and as an added bonus, event ordering is preserved across input sources. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+2
-4
@@ -11,9 +11,9 @@ import (
|
|||||||
|
|
||||||
"gioui.org/ui"
|
"gioui.org/ui"
|
||||||
"gioui.org/ui/app/internal/gpu"
|
"gioui.org/ui/app/internal/gpu"
|
||||||
|
"gioui.org/ui/input"
|
||||||
"gioui.org/ui/internal/ops"
|
"gioui.org/ui/internal/ops"
|
||||||
"gioui.org/ui/key"
|
"gioui.org/ui/key"
|
||||||
"gioui.org/ui/pointer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type WindowOptions struct {
|
type WindowOptions struct {
|
||||||
@@ -238,9 +238,7 @@ func (w *Window) event(e Event) {
|
|||||||
needAck := false
|
needAck := false
|
||||||
needRedraw := false
|
needRedraw := false
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case pointer.Event:
|
case input.Event:
|
||||||
needRedraw = true
|
|
||||||
case key.Event:
|
|
||||||
needRedraw = true
|
needRedraw = true
|
||||||
case *Command:
|
case *Command:
|
||||||
needAck = true
|
needAck = true
|
||||||
|
|||||||
+11
-2
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gioui.org/ui"
|
"gioui.org/ui"
|
||||||
"gioui.org/ui/f32"
|
"gioui.org/ui/f32"
|
||||||
|
"gioui.org/ui/input"
|
||||||
"gioui.org/ui/pointer"
|
"gioui.org/ui/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -80,9 +81,13 @@ func (c *Click) Add(ops *ui.Ops) {
|
|||||||
op.Add(ops)
|
op.Add(ops)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Click) Update(q pointer.Events) []ClickEvent {
|
func (c *Click) Update(q input.Events) []ClickEvent {
|
||||||
var events []ClickEvent
|
var events []ClickEvent
|
||||||
for _, e := range q.For(c) {
|
for _, e := range q.For(c) {
|
||||||
|
e, ok := e.(pointer.Event)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
switch e.Type {
|
switch e.Type {
|
||||||
case pointer.Release:
|
case pointer.Release:
|
||||||
if c.State == StatePressed {
|
if c.State == StatePressed {
|
||||||
@@ -124,13 +129,17 @@ func (s *Scroll) Dragging() bool {
|
|||||||
return s.dragging
|
return s.dragging
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scroll) Update(cfg *ui.Config, q pointer.Events, axis Axis) int {
|
func (s *Scroll) Update(cfg *ui.Config, q input.Events, axis Axis) int {
|
||||||
if s.axis != axis {
|
if s.axis != axis {
|
||||||
s.axis = axis
|
s.axis = axis
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
total := 0
|
total := 0
|
||||||
for _, e := range q.For(s) {
|
for _, e := range q.For(s) {
|
||||||
|
e, ok := e.(pointer.Event)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
switch e.Type {
|
switch e.Type {
|
||||||
case pointer.Press:
|
case pointer.Press:
|
||||||
if s.dragging || e.Source != pointer.Touch {
|
if s.dragging || e.Source != pointer.Touch {
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// SPDX-License-Identifier: Unlicense OR MIT
|
||||||
|
|
||||||
|
// Package input exposes a unified interface to input sources. Subpackages
|
||||||
|
// such as pointer and key provide the interfaces for specific input types.
|
||||||
|
package input
|
||||||
|
|
||||||
|
// Events maps an event handler key to the events
|
||||||
|
// available to the handler.
|
||||||
|
type Events interface {
|
||||||
|
For(k Key) []Event
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key is the stable identifier for an event handler. For a handler h, the
|
||||||
|
// key is typically &h.
|
||||||
|
type Key interface{}
|
||||||
|
|
||||||
|
// Event is the marker interface for input events.
|
||||||
|
type Event interface {
|
||||||
|
ImplementsInputEvent()
|
||||||
|
}
|
||||||
@@ -1,22 +1,22 @@
|
|||||||
// SPDX-License-Identifier: Unlicense OR MIT
|
// SPDX-License-Identifier: Unlicense OR MIT
|
||||||
|
|
||||||
package key
|
package input
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gioui.org/ui"
|
"gioui.org/ui"
|
||||||
"gioui.org/ui/internal/ops"
|
"gioui.org/ui/internal/ops"
|
||||||
|
"gioui.org/ui/key"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Queue struct {
|
type keyQueue struct {
|
||||||
focus Key
|
focus Key
|
||||||
handlers map[Key]*handler
|
handlers map[Key]*keyHandler
|
||||||
reader ui.OpsReader
|
reader ui.OpsReader
|
||||||
state TextInputState
|
state key.TextInputState
|
||||||
}
|
}
|
||||||
|
|
||||||
type handler struct {
|
type keyHandler struct {
|
||||||
active bool
|
active bool
|
||||||
events []Event
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type listenerPriority uint8
|
type listenerPriority uint8
|
||||||
@@ -30,20 +30,19 @@ const (
|
|||||||
|
|
||||||
// InputState returns the last text input state as
|
// InputState returns the last text input state as
|
||||||
// determined in Frame.
|
// determined in Frame.
|
||||||
func (q *Queue) InputState() TextInputState {
|
func (q *keyQueue) InputState() key.TextInputState {
|
||||||
return q.state
|
return q.state
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) Frame(root *ui.Ops) {
|
func (q *keyQueue) Frame(root *ui.Ops, events handlerEvents) {
|
||||||
if q.handlers == nil {
|
if q.handlers == nil {
|
||||||
q.handlers = make(map[Key]*handler)
|
q.handlers = make(map[Key]*keyHandler)
|
||||||
}
|
}
|
||||||
for _, h := range q.handlers {
|
for _, h := range q.handlers {
|
||||||
h.active = false
|
h.active = false
|
||||||
h.events = h.events[:0]
|
|
||||||
}
|
}
|
||||||
q.reader.Reset(root)
|
q.reader.Reset(root)
|
||||||
focus, pri, hide := q.resolveFocus()
|
focus, pri, hide := q.resolveFocus(events)
|
||||||
for k, h := range q.handlers {
|
for k, h := range q.handlers {
|
||||||
if !h.active {
|
if !h.active {
|
||||||
delete(q.handlers, k)
|
delete(q.handlers, k)
|
||||||
@@ -55,46 +54,33 @@ func (q *Queue) Frame(root *ui.Ops) {
|
|||||||
changed := focus != nil && focus != q.focus
|
changed := focus != nil && focus != q.focus
|
||||||
if focus != q.focus {
|
if focus != q.focus {
|
||||||
if q.focus != nil {
|
if q.focus != nil {
|
||||||
if h, ok := q.handlers[q.focus]; ok {
|
events[q.focus] = append(events[q.focus], key.Focus{Focus: false})
|
||||||
h.events = append(h.events, Focus{Focus: false})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
q.focus = focus
|
q.focus = focus
|
||||||
if q.focus != nil {
|
if q.focus != nil {
|
||||||
// A new focus always exists in the handler map.
|
events[q.focus] = append(events[q.focus], key.Focus{Focus: true})
|
||||||
h := q.handlers[q.focus]
|
|
||||||
h.events = append(h.events, Focus{Focus: true})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case pri == priNewFocus:
|
case pri == priNewFocus:
|
||||||
q.state = TextInputOpen
|
q.state = key.TextInputOpen
|
||||||
case hide:
|
case hide:
|
||||||
q.state = TextInputClosed
|
q.state = key.TextInputClosed
|
||||||
case changed:
|
case changed:
|
||||||
q.state = TextInputFocus
|
q.state = key.TextInputFocus
|
||||||
default:
|
default:
|
||||||
q.state = TextInputKeep
|
q.state = key.TextInputKeep
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) Push(e Event) {
|
func (q *keyQueue) Push(e Event, events handlerEvents) {
|
||||||
if q.focus == nil {
|
if q.focus == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h := q.handlers[q.focus]
|
events[q.focus] = append(events[q.focus], e)
|
||||||
h.events = append(h.events, e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) For(k Key) []Event {
|
func (q *keyQueue) resolveFocus(events handlerEvents) (Key, listenerPriority, bool) {
|
||||||
h := q.handlers[k]
|
|
||||||
if h == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return h.events
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) resolveFocus() (Key, listenerPriority, bool) {
|
|
||||||
var k Key
|
var k Key
|
||||||
var pri listenerPriority
|
var pri listenerPriority
|
||||||
var hide bool
|
var hide bool
|
||||||
@@ -106,7 +92,7 @@ loop:
|
|||||||
}
|
}
|
||||||
switch ops.OpType(encOp.Data[0]) {
|
switch ops.OpType(encOp.Data[0]) {
|
||||||
case ops.TypeKeyHandler:
|
case ops.TypeKeyHandler:
|
||||||
var op OpHandler
|
var op key.OpHandler
|
||||||
op.Decode(encOp.Data, encOp.Refs)
|
op.Decode(encOp.Data, encOp.Refs)
|
||||||
var newPri listenerPriority
|
var newPri listenerPriority
|
||||||
switch {
|
switch {
|
||||||
@@ -122,17 +108,16 @@ loop:
|
|||||||
}
|
}
|
||||||
h, ok := q.handlers[op.Key]
|
h, ok := q.handlers[op.Key]
|
||||||
if !ok {
|
if !ok {
|
||||||
h = &handler{
|
h = new(keyHandler)
|
||||||
// Reset the handler on (each) first appearance.
|
|
||||||
events: []Event{Focus{Focus: false}},
|
|
||||||
}
|
|
||||||
q.handlers[op.Key] = h
|
q.handlers[op.Key] = h
|
||||||
|
// Reset the handler on (each) first appearance.
|
||||||
|
events[op.Key] = []Event{key.Focus{Focus: false}}
|
||||||
}
|
}
|
||||||
h.active = true
|
h.active = true
|
||||||
case ops.TypeHideInput:
|
case ops.TypeHideInput:
|
||||||
hide = true
|
hide = true
|
||||||
case ops.TypePush:
|
case ops.TypePush:
|
||||||
newK, newPri, h := q.resolveFocus()
|
newK, newPri, h := q.resolveFocus(events)
|
||||||
hide = hide || h
|
hide = hide || h
|
||||||
if newPri >= pri {
|
if newPri >= pri {
|
||||||
k, pri = newK, newPri
|
k, pri = newK, newPri
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
// SPDX-License-Identifier: Unlicense OR MIT
|
// SPDX-License-Identifier: Unlicense OR MIT
|
||||||
|
|
||||||
package pointer
|
package input
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gioui.org/ui"
|
"gioui.org/ui"
|
||||||
"gioui.org/ui/f32"
|
"gioui.org/ui/f32"
|
||||||
"gioui.org/ui/internal/ops"
|
"gioui.org/ui/internal/ops"
|
||||||
|
"gioui.org/ui/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Queue struct {
|
type pointerQueue struct {
|
||||||
hitTree []hitNode
|
hitTree []hitNode
|
||||||
handlers map[Key]*handler
|
handlers map[Key]*pointerHandler
|
||||||
pointers []pointerInfo
|
pointers []pointerInfo
|
||||||
reader ui.OpsReader
|
reader ui.OpsReader
|
||||||
scratch []Key
|
scratch []Key
|
||||||
@@ -25,22 +26,21 @@ type hitNode struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type pointerInfo struct {
|
type pointerInfo struct {
|
||||||
id ID
|
id pointer.ID
|
||||||
pressed bool
|
pressed bool
|
||||||
handlers []Key
|
handlers []Key
|
||||||
}
|
}
|
||||||
|
|
||||||
type handler struct {
|
type pointerHandler struct {
|
||||||
area areaIntersection
|
area areaIntersection
|
||||||
active bool
|
active bool
|
||||||
transform ui.Transform
|
transform ui.Transform
|
||||||
events []Event
|
|
||||||
wantsGrab bool
|
wantsGrab bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type area struct {
|
type area struct {
|
||||||
trans ui.Transform
|
trans ui.Transform
|
||||||
area OpArea
|
area pointer.OpArea
|
||||||
}
|
}
|
||||||
|
|
||||||
type areaIntersection []area
|
type areaIntersection []area
|
||||||
@@ -51,7 +51,7 @@ type areaStack struct {
|
|||||||
backing []area
|
backing []area
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) collectHandlers(r *ui.OpsReader, t ui.Transform, layer int) {
|
func (q *pointerQueue) collectHandlers(r *ui.OpsReader, t ui.Transform, layer int, events handlerEvents) {
|
||||||
for {
|
for {
|
||||||
encOp, ok := r.Decode()
|
encOp, ok := r.Decode()
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -60,7 +60,7 @@ func (q *Queue) collectHandlers(r *ui.OpsReader, t ui.Transform, layer int) {
|
|||||||
switch ops.OpType(encOp.Data[0]) {
|
switch ops.OpType(encOp.Data[0]) {
|
||||||
case ops.TypePush:
|
case ops.TypePush:
|
||||||
q.areas.push()
|
q.areas.push()
|
||||||
q.collectHandlers(r, t, layer)
|
q.collectHandlers(r, t, layer, events)
|
||||||
case ops.TypePop:
|
case ops.TypePop:
|
||||||
q.areas.pop()
|
q.areas.pop()
|
||||||
return
|
return
|
||||||
@@ -68,24 +68,22 @@ func (q *Queue) collectHandlers(r *ui.OpsReader, t ui.Transform, layer int) {
|
|||||||
layer++
|
layer++
|
||||||
q.hitTree = append(q.hitTree, hitNode{level: layer})
|
q.hitTree = append(q.hitTree, hitNode{level: layer})
|
||||||
case ops.TypeArea:
|
case ops.TypeArea:
|
||||||
var op OpArea
|
var op pointer.OpArea
|
||||||
op.decode(encOp.Data)
|
op.Decode(encOp.Data)
|
||||||
q.areas.add(t, op)
|
q.areas.add(t, op)
|
||||||
case ops.TypeTransform:
|
case ops.TypeTransform:
|
||||||
var op ui.OpTransform
|
var op ui.OpTransform
|
||||||
op.Decode(encOp.Data)
|
op.Decode(encOp.Data)
|
||||||
t = t.Mul(op.Transform)
|
t = t.Mul(op.Transform)
|
||||||
case ops.TypePointerHandler:
|
case ops.TypePointerHandler:
|
||||||
var op OpHandler
|
var op pointer.OpHandler
|
||||||
op.Decode(encOp.Data, encOp.Refs)
|
op.Decode(encOp.Data, encOp.Refs)
|
||||||
q.hitTree = append(q.hitTree, hitNode{level: layer, key: op.Key})
|
q.hitTree = append(q.hitTree, hitNode{level: layer, key: op.Key})
|
||||||
h, ok := q.handlers[op.Key]
|
h, ok := q.handlers[op.Key]
|
||||||
if !ok {
|
if !ok {
|
||||||
h = &handler{
|
h = new(pointerHandler)
|
||||||
// Reset the handler on (each) first appearance.
|
|
||||||
events: []Event{Event{Type: Cancel}},
|
|
||||||
}
|
|
||||||
q.handlers[op.Key] = h
|
q.handlers[op.Key] = h
|
||||||
|
events[op.Key] = []Event{pointer.Event{Type: pointer.Cancel}}
|
||||||
}
|
}
|
||||||
h.active = true
|
h.active = true
|
||||||
h.area = q.areas.intersection()
|
h.area = q.areas.intersection()
|
||||||
@@ -95,7 +93,7 @@ func (q *Queue) collectHandlers(r *ui.OpsReader, t ui.Transform, layer int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) opHit(handlers *[]Key, pos f32.Point) {
|
func (q *pointerQueue) opHit(handlers *[]Key, pos f32.Point) {
|
||||||
level := 1 << 30
|
level := 1 << 30
|
||||||
opaque := false
|
opaque := false
|
||||||
for i := len(q.hitTree) - 1; i >= 0; i-- {
|
for i := len(q.hitTree) - 1; i >= 0; i-- {
|
||||||
@@ -114,31 +112,30 @@ func (q *Queue) opHit(handlers *[]Key, pos f32.Point) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
res := h.area.hit(pos)
|
res := h.area.hit(pos)
|
||||||
opaque = opaque || res == hitOpaque
|
opaque = opaque || res == pointer.HitOpaque
|
||||||
if res != hitNone {
|
if res != pointer.HitNone {
|
||||||
*handlers = append(*handlers, n.key)
|
*handlers = append(*handlers, n.key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) init() {
|
func (q *pointerQueue) init() {
|
||||||
if q.handlers == nil {
|
if q.handlers == nil {
|
||||||
q.handlers = make(map[Key]*handler)
|
q.handlers = make(map[Key]*pointerHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) Frame(root *ui.Ops) {
|
func (q *pointerQueue) Frame(root *ui.Ops, events handlerEvents) {
|
||||||
q.init()
|
q.init()
|
||||||
for _, h := range q.handlers {
|
for _, h := range q.handlers {
|
||||||
// Reset handler.
|
// Reset handler.
|
||||||
h.active = false
|
h.active = false
|
||||||
h.events = h.events[:0]
|
|
||||||
}
|
}
|
||||||
q.hitTree = q.hitTree[:0]
|
q.hitTree = q.hitTree[:0]
|
||||||
q.areas.reset()
|
q.areas.reset()
|
||||||
q.reader.Reset(root)
|
q.reader.Reset(root)
|
||||||
q.collectHandlers(&q.reader, ui.Transform{}, 0)
|
q.collectHandlers(&q.reader, ui.Transform{}, 0, events)
|
||||||
for k, h := range q.handlers {
|
for k, h := range q.handlers {
|
||||||
if !h.active {
|
if !h.active {
|
||||||
q.dropHandler(k)
|
q.dropHandler(k)
|
||||||
@@ -146,18 +143,7 @@ func (q *Queue) Frame(root *ui.Ops) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) For(k Key) []Event {
|
func (q *pointerQueue) dropHandler(k Key) {
|
||||||
if k == nil {
|
|
||||||
panic("nil handler")
|
|
||||||
}
|
|
||||||
h := q.handlers[k]
|
|
||||||
if h == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return h.events
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue) dropHandler(k Key) {
|
|
||||||
delete(q.handlers, k)
|
delete(q.handlers, k)
|
||||||
for i := range q.pointers {
|
for i := range q.pointers {
|
||||||
p := &q.pointers[i]
|
p := &q.pointers[i]
|
||||||
@@ -169,9 +155,9 @@ func (q *Queue) dropHandler(k Key) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queue) Push(e Event) {
|
func (q *pointerQueue) Push(e pointer.Event, events handlerEvents) {
|
||||||
q.init()
|
q.init()
|
||||||
if e.Type == Cancel {
|
if e.Type == pointer.Cancel {
|
||||||
q.pointers = q.pointers[:0]
|
q.pointers = q.pointers[:0]
|
||||||
for k := range q.handlers {
|
for k := range q.handlers {
|
||||||
q.dropHandler(k)
|
q.dropHandler(k)
|
||||||
@@ -190,7 +176,7 @@ func (q *Queue) Push(e Event) {
|
|||||||
pidx = len(q.pointers) - 1
|
pidx = len(q.pointers) - 1
|
||||||
}
|
}
|
||||||
p := &q.pointers[pidx]
|
p := &q.pointers[pidx]
|
||||||
if !p.pressed && (e.Type == Move || e.Type == Press) {
|
if !p.pressed && (e.Type == pointer.Move || e.Type == pointer.Press) {
|
||||||
p.handlers, q.scratch = q.scratch[:0], p.handlers
|
p.handlers, q.scratch = q.scratch[:0], p.handlers
|
||||||
q.opHit(&p.handlers, e.Position)
|
q.opHit(&p.handlers, e.Position)
|
||||||
// Drop handlers no longer hit.
|
// Drop handlers no longer hit.
|
||||||
@@ -203,7 +189,7 @@ func (q *Queue) Push(e Event) {
|
|||||||
}
|
}
|
||||||
q.dropHandler(h)
|
q.dropHandler(h)
|
||||||
}
|
}
|
||||||
if e.Type == Press {
|
if e.Type == pointer.Press {
|
||||||
p.pressed = true
|
p.pressed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +209,7 @@ func (q *Queue) Push(e Event) {
|
|||||||
q.dropHandler(k)
|
q.dropHandler(k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if e.Type == Release {
|
if e.Type == pointer.Release {
|
||||||
q.pointers = append(q.pointers[:pidx], q.pointers[pidx+1:]...)
|
q.pointers = append(q.pointers[:pidx], q.pointers[pidx+1:]...)
|
||||||
}
|
}
|
||||||
for i, k := range p.handlers {
|
for i, k := range p.handlers {
|
||||||
@@ -231,14 +217,14 @@ func (q *Queue) Push(e Event) {
|
|||||||
e := e
|
e := e
|
||||||
switch {
|
switch {
|
||||||
case p.pressed && len(p.handlers) == 1:
|
case p.pressed && len(p.handlers) == 1:
|
||||||
e.Priority = Grabbed
|
e.Priority = pointer.Grabbed
|
||||||
case i == 0:
|
case i == 0:
|
||||||
e.Priority = Foremost
|
e.Priority = pointer.Foremost
|
||||||
}
|
}
|
||||||
e.Hit = h.area.hit(e.Position) != hitNone
|
e.Hit = h.area.hit(e.Position) != pointer.HitNone
|
||||||
e.Position = h.transform.InvTransform(e.Position)
|
e.Position = h.transform.InvTransform(e.Position)
|
||||||
h.events = append(h.events, e)
|
events[k] = append(events[k], e)
|
||||||
if e.Type == Release {
|
if e.Type == pointer.Release {
|
||||||
// Release grab when the number of grabs reaches zero.
|
// Release grab when the number of grabs reaches zero.
|
||||||
grabs := 0
|
grabs := 0
|
||||||
for _, p := range q.pointers {
|
for _, p := range q.pointers {
|
||||||
@@ -253,19 +239,19 @@ func (q *Queue) Push(e Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a areaIntersection) hit(p f32.Point) hitResult {
|
func (a areaIntersection) hit(p f32.Point) pointer.HitResult {
|
||||||
res := hitNone
|
res := pointer.HitNone
|
||||||
for _, area := range a {
|
for _, area := range a {
|
||||||
tp := area.trans.InvTransform(p)
|
tp := area.trans.InvTransform(p)
|
||||||
res = area.area.hit(tp)
|
res = area.area.Hit(tp)
|
||||||
if res == hitNone {
|
if res == pointer.HitNone {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *areaStack) add(t ui.Transform, a OpArea) {
|
func (s *areaStack) add(t ui.Transform, a pointer.OpArea) {
|
||||||
s.areas = append(s.areas, area{t, a})
|
s.areas = append(s.areas, area{t, a})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// SPDX-License-Identifier: Unlicense OR MIT
|
||||||
|
|
||||||
|
package input
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gioui.org/ui"
|
||||||
|
"gioui.org/ui/key"
|
||||||
|
"gioui.org/ui/pointer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Queue is an Events implementation that merges events from
|
||||||
|
// all available input sources.
|
||||||
|
type Queue struct {
|
||||||
|
pqueue pointerQueue
|
||||||
|
kqueue keyQueue
|
||||||
|
|
||||||
|
handlers handlerEvents
|
||||||
|
}
|
||||||
|
|
||||||
|
type handlerEvents map[Key][]Event
|
||||||
|
|
||||||
|
func (q *Queue) For(k Key) []Event {
|
||||||
|
return q.handlers[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) Frame(ops *ui.Ops) {
|
||||||
|
q.init()
|
||||||
|
for k := range q.handlers {
|
||||||
|
delete(q.handlers, k)
|
||||||
|
}
|
||||||
|
q.pqueue.Frame(ops, q.handlers)
|
||||||
|
q.kqueue.Frame(ops, q.handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) Add(e Event) {
|
||||||
|
q.init()
|
||||||
|
switch e := e.(type) {
|
||||||
|
case pointer.Event:
|
||||||
|
q.pqueue.Push(e, q.handlers)
|
||||||
|
case key.Edit, key.Chord, key.Focus:
|
||||||
|
q.kqueue.Push(e, q.handlers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) InputState() key.TextInputState {
|
||||||
|
return q.kqueue.InputState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) init() {
|
||||||
|
if q.handlers == nil {
|
||||||
|
q.handlers = make(handlerEvents)
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-14
@@ -16,14 +16,6 @@ type OpHideInput struct{}
|
|||||||
|
|
||||||
type Key interface{}
|
type Key interface{}
|
||||||
|
|
||||||
type Events interface {
|
|
||||||
For(k Key) []Event
|
|
||||||
}
|
|
||||||
|
|
||||||
type Event interface {
|
|
||||||
isKeyEvent()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Focus struct {
|
type Focus struct {
|
||||||
Focus bool
|
Focus bool
|
||||||
}
|
}
|
||||||
@@ -93,9 +85,9 @@ func (h OpHideInput) Add(o *ui.Ops) {
|
|||||||
o.Write(data)
|
o.Write(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Edit) ImplementsEvent() {}
|
func (Edit) ImplementsEvent() {}
|
||||||
func (Chord) ImplementsEvent() {}
|
func (Chord) ImplementsEvent() {}
|
||||||
func (Focus) ImplementsEvent() {}
|
func (Focus) ImplementsEvent() {}
|
||||||
func (Edit) isKeyEvent() {}
|
func (Edit) ImplementsInputEvent() {}
|
||||||
func (Chord) isKeyEvent() {}
|
func (Chord) ImplementsInputEvent() {}
|
||||||
func (Focus) isKeyEvent() {}
|
func (Focus) ImplementsInputEvent() {}
|
||||||
|
|||||||
+2
-1
@@ -8,6 +8,7 @@ import (
|
|||||||
"gioui.org/ui"
|
"gioui.org/ui"
|
||||||
"gioui.org/ui/draw"
|
"gioui.org/ui/draw"
|
||||||
"gioui.org/ui/gesture"
|
"gioui.org/ui/gesture"
|
||||||
|
"gioui.org/ui/input"
|
||||||
"gioui.org/ui/pointer"
|
"gioui.org/ui/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ func (l *List) Dragging() bool {
|
|||||||
return l.scroll.Dragging()
|
return l.scroll.Dragging()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *List) Update(c *ui.Config, q pointer.Events) {
|
func (l *List) Update(c *ui.Config, q input.Events) {
|
||||||
l.Distance = 0
|
l.Distance = 0
|
||||||
d := l.scroll.Update(c, q, gesture.Axis(l.Axis))
|
d := l.scroll.Update(c, q, gesture.Axis(l.Axis))
|
||||||
l.scrollDir = d
|
l.scrollDir = d
|
||||||
|
|||||||
+12
-15
@@ -37,16 +37,12 @@ type OpHandler struct {
|
|||||||
|
|
||||||
type Key interface{}
|
type Key interface{}
|
||||||
|
|
||||||
type Events interface {
|
type HitResult uint8
|
||||||
For(k Key) []Event
|
|
||||||
}
|
|
||||||
|
|
||||||
type hitResult uint8
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
hitNone hitResult = iota
|
HitNone HitResult = iota
|
||||||
hitTransparent
|
HitTransparent
|
||||||
hitOpaque
|
HitOpaque
|
||||||
)
|
)
|
||||||
|
|
||||||
type ID uint16
|
type ID uint16
|
||||||
@@ -103,7 +99,7 @@ func (op OpArea) Add(o *ui.Ops) {
|
|||||||
o.Write(data)
|
o.Write(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (op *OpArea) decode(d []byte) {
|
func (op *OpArea) Decode(d []byte) {
|
||||||
if ops.OpType(d[0]) != ops.TypeArea {
|
if ops.OpType(d[0]) != ops.TypeArea {
|
||||||
panic("invalid op")
|
panic("invalid op")
|
||||||
}
|
}
|
||||||
@@ -118,10 +114,10 @@ func (op *OpArea) decode(d []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (op *OpArea) hit(pos f32.Point) hitResult {
|
func (op *OpArea) Hit(pos f32.Point) HitResult {
|
||||||
res := hitOpaque
|
res := HitOpaque
|
||||||
if op.Transparent {
|
if op.Transparent {
|
||||||
res = hitTransparent
|
res = HitTransparent
|
||||||
}
|
}
|
||||||
switch op.kind {
|
switch op.kind {
|
||||||
case areaRect:
|
case areaRect:
|
||||||
@@ -129,7 +125,7 @@ func (op *OpArea) hit(pos f32.Point) hitResult {
|
|||||||
0 <= pos.Y && pos.Y < float32(op.size.Y) {
|
0 <= pos.Y && pos.Y < float32(op.size.Y) {
|
||||||
return res
|
return res
|
||||||
} else {
|
} else {
|
||||||
return hitNone
|
return HitNone
|
||||||
}
|
}
|
||||||
case areaEllipse:
|
case areaEllipse:
|
||||||
rx := float32(op.size.X) / 2
|
rx := float32(op.size.X) / 2
|
||||||
@@ -141,7 +137,7 @@ func (op *OpArea) hit(pos f32.Point) hitResult {
|
|||||||
if xh*xh*ry2+yk*yk*rx2 <= rx2*ry2 {
|
if xh*xh*ry2+yk*yk*rx2 <= rx2*ry2 {
|
||||||
return res
|
return res
|
||||||
} else {
|
} else {
|
||||||
return hitNone
|
return HitNone
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("invalid area kind")
|
panic("invalid area kind")
|
||||||
@@ -206,4 +202,5 @@ func (s Source) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Event) ImplementsEvent() {}
|
func (Event) ImplementsEvent() {}
|
||||||
|
func (Event) ImplementsInputEvent() {}
|
||||||
|
|||||||
+5
-4
@@ -11,6 +11,7 @@ import (
|
|||||||
"gioui.org/ui"
|
"gioui.org/ui"
|
||||||
"gioui.org/ui/draw"
|
"gioui.org/ui/draw"
|
||||||
"gioui.org/ui/gesture"
|
"gioui.org/ui/gesture"
|
||||||
|
"gioui.org/ui/input"
|
||||||
"gioui.org/ui/key"
|
"gioui.org/ui/key"
|
||||||
"gioui.org/ui/layout"
|
"gioui.org/ui/layout"
|
||||||
"gioui.org/ui/pointer"
|
"gioui.org/ui/pointer"
|
||||||
@@ -53,7 +54,7 @@ const (
|
|||||||
maxBlinkDuration = 10 * time.Second
|
maxBlinkDuration = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *Editor) Update(c *ui.Config, pq pointer.Events, kq key.Events) {
|
func (e *Editor) Update(c *ui.Config, q input.Events) {
|
||||||
if e.cfg == nil || c.PxPerDp != e.cfg.PxPerDp || c.PxPerSp != e.cfg.PxPerSp {
|
if e.cfg == nil || c.PxPerDp != e.cfg.PxPerDp || c.PxPerSp != e.cfg.PxPerSp {
|
||||||
e.invalidate()
|
e.invalidate()
|
||||||
}
|
}
|
||||||
@@ -68,7 +69,7 @@ func (e *Editor) Update(c *ui.Config, pq pointer.Events, kq key.Events) {
|
|||||||
axis = gesture.Vertical
|
axis = gesture.Vertical
|
||||||
smin, smax = sbounds.Min.Y, sbounds.Max.Y
|
smin, smax = sbounds.Min.Y, sbounds.Max.Y
|
||||||
}
|
}
|
||||||
sdist := e.scroller.Update(c, pq, axis)
|
sdist := e.scroller.Update(c, q, axis)
|
||||||
var soff int
|
var soff int
|
||||||
if e.SingleLine {
|
if e.SingleLine {
|
||||||
e.scrollOff.X += sdist
|
e.scrollOff.X += sdist
|
||||||
@@ -78,7 +79,7 @@ func (e *Editor) Update(c *ui.Config, pq pointer.Events, kq key.Events) {
|
|||||||
soff = e.scrollOff.Y
|
soff = e.scrollOff.Y
|
||||||
}
|
}
|
||||||
scrollTo := false
|
scrollTo := false
|
||||||
for _, evt := range e.clicker.Update(pq) {
|
for _, evt := range e.clicker.Update(q) {
|
||||||
switch {
|
switch {
|
||||||
case evt.Type == gesture.TypePress && evt.Source == pointer.Mouse,
|
case evt.Type == gesture.TypePress && evt.Source == pointer.Mouse,
|
||||||
evt.Type == gesture.TypeClick && evt.Source == pointer.Touch:
|
evt.Type == gesture.TypeClick && evt.Source == pointer.Touch:
|
||||||
@@ -92,7 +93,7 @@ func (e *Editor) Update(c *ui.Config, pq pointer.Events, kq key.Events) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stop := (sdist > 0 && soff >= smax) || (sdist < 0 && soff <= smin)
|
stop := (sdist > 0 && soff >= smax) || (sdist < 0 && soff <= smin)
|
||||||
for _, ke := range kq.For(e) {
|
for _, ke := range q.For(e) {
|
||||||
e.blinkStart = c.Now
|
e.blinkStart = c.Now
|
||||||
switch ke := ke.(type) {
|
switch ke := ke.(type) {
|
||||||
case key.Focus:
|
case key.Focus:
|
||||||
|
|||||||
Reference in New Issue
Block a user