ui: merge package input

Event handling is as fundamental as operations, so move the input
package declarations to package ui.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-09-24 22:11:44 +02:00
parent 2782436ffc
commit 3944ef4b2e
15 changed files with 108 additions and 122 deletions
+3 -3
View File
@@ -57,9 +57,9 @@ For example, to display a blank but otherwise functional window:
Event queue Event queue
A Window's Queue method returns an input.Queue implementation that distributes A Window's Queue method returns an ui.Queue implementation that distributes
incoming events to the input handlers declared in the latest call to Update. incoming events to the event handlers declared in the latest call to Update.
See the gioui.org/ui/input package for more information about input handlers. See the gioui.org/ui package for more information about event handlers.
*/ */
package app package app
+8 -9
View File
@@ -4,7 +4,6 @@ package input
import ( import (
"gioui.org/ui" "gioui.org/ui"
"gioui.org/ui/input"
"gioui.org/ui/internal/opconst" "gioui.org/ui/internal/opconst"
"gioui.org/ui/internal/ops" "gioui.org/ui/internal/ops"
"gioui.org/ui/key" "gioui.org/ui/key"
@@ -13,8 +12,8 @@ import (
type TextInputState uint8 type TextInputState uint8
type keyQueue struct { type keyQueue struct {
focus input.Key focus ui.Key
handlers map[input.Key]*keyHandler handlers map[ui.Key]*keyHandler
reader ops.Reader reader ops.Reader
state TextInputState state TextInputState
} }
@@ -46,7 +45,7 @@ func (q *keyQueue) InputState() TextInputState {
func (q *keyQueue) Frame(root *ui.Ops, events *handlerEvents) { func (q *keyQueue) Frame(root *ui.Ops, events *handlerEvents) {
if q.handlers == nil { if q.handlers == nil {
q.handlers = make(map[input.Key]*keyHandler) q.handlers = make(map[ui.Key]*keyHandler)
} }
for _, h := range q.handlers { for _, h := range q.handlers {
h.active = false h.active = false
@@ -83,14 +82,14 @@ func (q *keyQueue) Frame(root *ui.Ops, events *handlerEvents) {
} }
} }
func (q *keyQueue) Push(e input.Event, events *handlerEvents) { func (q *keyQueue) Push(e ui.Event, events *handlerEvents) {
if q.focus != nil { if q.focus != nil {
events.Add(q.focus, e) events.Add(q.focus, e)
} }
} }
func (q *keyQueue) resolveFocus(events *handlerEvents) (input.Key, listenerPriority, bool) { func (q *keyQueue) resolveFocus(events *handlerEvents) (ui.Key, listenerPriority, bool) {
var k input.Key var k ui.Key
var pri listenerPriority var pri listenerPriority
var hide bool var hide bool
loop: loop:
@@ -116,7 +115,7 @@ loop:
h = new(keyHandler) h = new(keyHandler)
q.handlers[op.Key] = h q.handlers[op.Key] = h
// Reset the handler on (each) first appearance. // Reset the handler on (each) first appearance.
events.Set(op.Key, []input.Event{key.FocusEvent{Focus: false}}) events.Set(op.Key, []ui.Event{key.FocusEvent{Focus: false}})
} }
h.active = true h.active = true
case opconst.TypeHideInput: case opconst.TypeHideInput:
@@ -145,6 +144,6 @@ func decodeKeyInputOp(d []byte, refs []interface{}) key.InputOp {
} }
return key.InputOp{ return key.InputOp{
Focus: d[1] != 0, Focus: d[1] != 0,
Key: refs[0].(input.Key), Key: refs[0].(ui.Key),
} }
} }
+9 -10
View File
@@ -8,7 +8,6 @@ import (
"gioui.org/ui" "gioui.org/ui"
"gioui.org/ui/f32" "gioui.org/ui/f32"
"gioui.org/ui/input"
"gioui.org/ui/internal/opconst" "gioui.org/ui/internal/opconst"
"gioui.org/ui/internal/ops" "gioui.org/ui/internal/ops"
"gioui.org/ui/pointer" "gioui.org/ui/pointer"
@@ -17,10 +16,10 @@ import (
type pointerQueue struct { type pointerQueue struct {
hitTree []hitNode hitTree []hitNode
areas []areaNode areas []areaNode
handlers map[input.Key]*pointerHandler handlers map[ui.Key]*pointerHandler
pointers []pointerInfo pointers []pointerInfo
reader ops.Reader reader ops.Reader
scratch []input.Key scratch []ui.Key
} }
type hitNode struct { type hitNode struct {
@@ -30,13 +29,13 @@ type hitNode struct {
pass bool pass bool
// For handler nodes. // For handler nodes.
key input.Key key ui.Key
} }
type pointerInfo struct { type pointerInfo struct {
id pointer.ID id pointer.ID
pressed bool pressed bool
handlers []input.Key handlers []ui.Key
} }
type pointerHandler struct { type pointerHandler struct {
@@ -101,7 +100,7 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t u
if !ok { if !ok {
h = new(pointerHandler) h = new(pointerHandler)
q.handlers[op.Key] = h q.handlers[op.Key] = h
events.Set(op.Key, []input.Event{pointer.Event{Type: pointer.Cancel}}) events.Set(op.Key, []ui.Event{pointer.Event{Type: pointer.Cancel}})
} }
h.active = true h.active = true
h.area = area h.area = area
@@ -111,7 +110,7 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t u
} }
} }
func (q *pointerQueue) opHit(handlers *[]input.Key, pos f32.Point) { func (q *pointerQueue) opHit(handlers *[]ui.Key, pos f32.Point) {
// Track whether we're passing through hits. // Track whether we're passing through hits.
pass := true pass := true
idx := len(q.hitTree) - 1 idx := len(q.hitTree) - 1
@@ -154,7 +153,7 @@ func (a *areaNode) hit(p f32.Point) bool {
func (q *pointerQueue) init() { func (q *pointerQueue) init() {
if q.handlers == nil { if q.handlers == nil {
q.handlers = make(map[input.Key]*pointerHandler) q.handlers = make(map[ui.Key]*pointerHandler)
} }
} }
@@ -176,7 +175,7 @@ func (q *pointerQueue) Frame(root *ui.Ops, events *handlerEvents) {
} }
} }
func (q *pointerQueue) dropHandler(k input.Key) { func (q *pointerQueue) dropHandler(k ui.Key) {
for i := range q.pointers { for i := range q.pointers {
p := &q.pointers[i] p := &q.pointers[i]
for i := len(p.handlers) - 1; i >= 0; i-- { for i := len(p.handlers) - 1; i >= 0; i-- {
@@ -320,7 +319,7 @@ func decodePointerInputOp(d []byte, refs []interface{}) pointer.InputOp {
} }
return pointer.InputOp{ return pointer.InputOp{
Grab: d[1] != 0, Grab: d[1] != 0,
Key: refs[0].(input.Key), Key: refs[0].(ui.Key),
} }
} }
+9 -10
View File
@@ -7,7 +7,6 @@ import (
"time" "time"
"gioui.org/ui" "gioui.org/ui"
"gioui.org/ui/input"
"gioui.org/ui/internal/opconst" "gioui.org/ui/internal/opconst"
"gioui.org/ui/internal/ops" "gioui.org/ui/internal/ops"
"gioui.org/ui/key" "gioui.org/ui/key"
@@ -30,15 +29,15 @@ type Router struct {
wakeupTime time.Time wakeupTime time.Time
// ProfileOp summary. // ProfileOp summary.
profHandlers []input.Key profHandlers []ui.Key
} }
type handlerEvents struct { type handlerEvents struct {
handlers map[input.Key][]input.Event handlers map[ui.Key][]ui.Event
updated bool updated bool
} }
func (q *Router) Next(k input.Key) (input.Event, bool) { func (q *Router) Next(k ui.Key) (ui.Event, bool) {
return q.handlers.Next(k) return q.handlers.Next(k)
} }
@@ -57,7 +56,7 @@ func (q *Router) Frame(ops *ui.Ops) {
} }
} }
func (q *Router) Add(e input.Event) bool { func (q *Router) Add(e ui.Event) bool {
switch e := e.(type) { switch e := e.(type) {
case pointer.Event: case pointer.Event:
q.pqueue.Push(e, &q.handlers) q.pqueue.Push(e, &q.handlers)
@@ -103,17 +102,17 @@ func (q *Router) WakeupTime() (time.Time, bool) {
func (h *handlerEvents) init() { func (h *handlerEvents) init() {
if h.handlers == nil { if h.handlers == nil {
h.handlers = make(map[input.Key][]input.Event) h.handlers = make(map[ui.Key][]ui.Event)
} }
} }
func (h *handlerEvents) Set(k input.Key, evts []input.Event) { func (h *handlerEvents) Set(k ui.Key, evts []ui.Event) {
h.init() h.init()
h.handlers[k] = evts h.handlers[k] = evts
h.updated = true h.updated = true
} }
func (h *handlerEvents) Add(k input.Key, e input.Event) { func (h *handlerEvents) Add(k ui.Key, e ui.Event) {
h.init() h.init()
h.handlers[k] = append(h.handlers[k], e) h.handlers[k] = append(h.handlers[k], e)
h.updated = true h.updated = true
@@ -125,7 +124,7 @@ func (h *handlerEvents) Updated() bool {
return u return u
} }
func (h *handlerEvents) Next(k input.Key) (input.Event, bool) { func (h *handlerEvents) Next(k ui.Key) (ui.Event, bool) {
events := h.handlers[k] events := h.handlers[k]
if len(events) == 0 { if len(events) == 0 {
return nil, false return nil, false
@@ -146,7 +145,7 @@ func decodeProfileOp(d []byte, refs []interface{}) system.ProfileOp {
panic("invalid op") panic("invalid op")
} }
return system.ProfileOp{ return system.ProfileOp{
Key: refs[0].(input.Key), Key: refs[0].(ui.Key),
} }
} }
+15 -17
View File
@@ -10,8 +10,7 @@ import (
"gioui.org/ui" "gioui.org/ui"
"gioui.org/ui/app/internal/gpu" "gioui.org/ui/app/internal/gpu"
iinput "gioui.org/ui/app/internal/input" "gioui.org/ui/app/internal/input"
"gioui.org/ui/input"
"gioui.org/ui/system" "gioui.org/ui/system"
) )
@@ -32,8 +31,8 @@ type Window struct {
drawStart time.Time drawStart time.Time
gpu *gpu.GPU gpu *gpu.GPU
out chan input.Event out chan ui.Event
in chan input.Event in chan ui.Event
ack chan struct{} ack chan struct{}
invalidates chan struct{} invalidates chan struct{}
frames chan *ui.Ops frames chan *ui.Ops
@@ -47,11 +46,10 @@ type Window struct {
queue Queue queue Queue
} }
// Queue is an input.Queue implementation that distributes // Queue is an ui.Queue implementation that distributes system events
// system input events to the input handlers declared in the // to the input handlers declared in the most recent call to Update.
// most recent call to Update.
type Queue struct { type Queue struct {
q iinput.Router q input.Router
} }
// driverEvent is sent when a new native driver // driverEvent is sent when a new native driver
@@ -71,7 +69,7 @@ var _ interface {
} = (*window)(nil) } = (*window)(nil)
// Pre-allocate the ack event to avoid garbage. // Pre-allocate the ack event to avoid garbage.
var ackEvent input.Event var ackEvent ui.Event
// NewWindow creates a new window for a set of window // NewWindow creates a new window for a set of window
// options. The options are hints; the platform is free to // options. The options are hints; the platform is free to
@@ -96,8 +94,8 @@ func NewWindow(options ...WindowOption) *Window {
} }
w := &Window{ w := &Window{
in: make(chan input.Event), in: make(chan ui.Event),
out: make(chan input.Event), out: make(chan ui.Event),
ack: make(chan struct{}), ack: make(chan struct{}),
invalidates: make(chan struct{}, 1), invalidates: make(chan struct{}, 1),
frames: make(chan *ui.Ops), frames: make(chan *ui.Ops),
@@ -107,7 +105,7 @@ func NewWindow(options ...WindowOption) *Window {
} }
// Events returns the channel where events are delivered. // Events returns the channel where events are delivered.
func (w *Window) Events() <-chan input.Event { func (w *Window) Events() <-chan ui.Event {
return w.out return w.out
} }
@@ -135,9 +133,9 @@ func (w *Window) draw(size image.Point, frame *ui.Ops) {
w.queue.q.Frame(frame) w.queue.q.Frame(frame)
now := time.Now() now := time.Now()
switch w.queue.q.TextInputState() { switch w.queue.q.TextInputState() {
case iinput.TextInputOpen: case input.TextInputOpen:
w.driver.showTextInput(true) w.driver.showTextInput(true)
case iinput.TextInputClose: case input.TextInputClose:
w.driver.showTextInput(false) w.driver.showTextInput(false)
} }
frameDur := now.Sub(w.lastFrame) frameDur := now.Sub(w.lastFrame)
@@ -196,7 +194,7 @@ func (w *Window) setDriver(d *window) {
w.event(driverEvent{d}) w.event(driverEvent{d})
} }
func (w *Window) event(e input.Event) { func (w *Window) event(e ui.Event) {
w.in <- e w.in <- e
<-w.ack <-w.ack
} }
@@ -313,7 +311,7 @@ func (w *Window) run(opts *windowOptions) {
w.out <- e2 w.out <- e2
w.ack <- struct{}{} w.ack <- struct{}{}
return return
case input.Event: case ui.Event:
if w.queue.q.Add(e2) { if w.queue.q.Add(e2) {
w.setNextFrame(time.Time{}) w.setNextFrame(time.Time{})
w.updateAnimation() w.updateAnimation()
@@ -325,7 +323,7 @@ func (w *Window) run(opts *windowOptions) {
} }
} }
func (q *Queue) Next(k input.Key) (input.Event, bool) { func (q *Queue) Next(k ui.Key) (ui.Event, bool) {
return q.q.Next(k) return q.q.Next(k)
} }
+27
View File
@@ -80,5 +80,32 @@ size vary between platforms and displays.
To maintain a constant visual size across platforms and displays, always To maintain a constant visual size across platforms and displays, always
use dps or sps to define user interfaces. Only use pixels for derived use dps or sps to define user interfaces. Only use pixels for derived
values. values.
Events
The Queue interface is the protocol for receiving external events.
For example:
var queue ui.Queue = ...
for e, ok := queue.Next(h); ok; e, ok = queue.Next(h) {
switch e.(type) {
...
}
}
In general, handlers must be declared before events become
available. Other packages such as pointer and key provide
the means for declaring handlers for specific event types.
The following example declares a handler ready for key input:
import gioui.org/ui/key
ops := new(ui.Ops)
var h *Handler = ...
key.InputOp{Key: h}.Add(ops)
*/ */
package ui package ui
+3 -4
View File
@@ -3,7 +3,7 @@
/* /*
Package gesture implements common pointer gestures. Package gesture implements common pointer gestures.
Gestures accept low level pointer Events from an input Gestures accept low level pointer Events from an event
Queue and detect higher level actions such as clicks Queue and detect higher level actions such as clicks
and scrolling. and scrolling.
*/ */
@@ -14,7 +14,6 @@ import (
"gioui.org/ui" "gioui.org/ui"
"gioui.org/ui/f32" "gioui.org/ui/f32"
"gioui.org/ui/input"
"gioui.org/ui/internal/fling" "gioui.org/ui/internal/fling"
"gioui.org/ui/pointer" "gioui.org/ui/pointer"
) )
@@ -106,7 +105,7 @@ func (c *Click) State() ClickState {
} }
// Next returns the next click event, if any. // Next returns the next click event, if any.
func (c *Click) Next(q input.Queue) (ClickEvent, bool) { func (c *Click) Next(q ui.Queue) (ClickEvent, bool) {
for evt, ok := q.Next(c); ok; evt, ok = q.Next(c) { for evt, ok := q.Next(c); ok; evt, ok = q.Next(c) {
e, ok := evt.(pointer.Event) e, ok := evt.(pointer.Event)
if !ok { if !ok {
@@ -154,7 +153,7 @@ func (s *Scroll) Stop() {
// Scroll detects the scrolling distance from the available events and // Scroll detects the scrolling distance from the available events and
// ongoing fling gestures. // ongoing fling gestures.
func (s *Scroll) Scroll(cfg ui.Config, q input.Queue, axis Axis) int { func (s *Scroll) Scroll(cfg ui.Config, q ui.Queue, axis Axis) int {
if s.axis != axis { if s.axis != axis {
s.axis = axis s.axis = axis
return 0 return 0
+20
View File
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: Unlicense OR MIT
package ui
// Queue maps an event handler key to the events
// available to the handler.
type Queue interface {
// Next returns the next available event, or
// false if none are available.
Next(k Key) (Event, bool)
}
// 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 events.
type Event interface {
ImplementsEvent()
}
-48
View File
@@ -1,48 +0,0 @@
// SPDX-License-Identifier: Unlicense OR MIT
/*
Package input exposes a unified interface for receiving input
events.
For example:
var queue input.Queue = ...
for e, ok := queue.Next(h); ok; e, ok = queue.Next(h) {
switch e.(type) {
...
}
}
In general, handlers must be declared before events become
available. Other packages such as pointer and key provide
the means for declaring handlers for specific input types.
The following example marks a handler ready for key input:
import gioui.org/ui/input
import gioui.org/ui/key
ops := new(ui.Ops)
var h *Handler = ...
key.InputOp{Key: h}.Add(ops)
*/
package input
// Queue maps an event handler key to the events
// available to the handler.
type Queue interface {
// Next returns the next available event, or
// false if none are available.
Next(k Key) (Event, bool)
}
// 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 events.
type Event interface {
ImplementsEvent()
}
+5 -7
View File
@@ -1,18 +1,16 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
/* /*
Package key implements key and text events and Package key implements key and text events and operations.
operations.
The InputOp operations is used for declaring key The InputOp operations is used for declaring key input handlers. Use
input handlers. Use the Queue interface from package an implementation of the Queue interface from package ui to receive
input to receive events. events.
*/ */
package key package key
import ( import (
"gioui.org/ui" "gioui.org/ui"
"gioui.org/ui/input"
"gioui.org/ui/internal/opconst" "gioui.org/ui/internal/opconst"
) )
@@ -21,7 +19,7 @@ import (
// focused key handler. Set the Focus flag to request // focused key handler. Set the Focus flag to request
// the focus. // the focus.
type InputOp struct { type InputOp struct {
Key input.Key Key ui.Key
Focus bool Focus bool
} }
+2 -3
View File
@@ -6,7 +6,6 @@ import (
"image" "image"
"gioui.org/ui" "gioui.org/ui"
"gioui.org/ui/input"
) )
// Constraints represent a set of acceptable ranges for // Constraints represent a set of acceptable ranges for
@@ -38,7 +37,7 @@ type Alignment uint8
// space. // space.
type Direction uint8 type Direction uint8
// Widget is a function scope for drawing, processing input and // Widget is a function scope for drawing, processing events and
// computing dimensions for a user interface element. // computing dimensions for a user interface element.
type Widget func() type Widget func()
@@ -52,7 +51,7 @@ type Context struct {
Dimensions Dimensions Dimensions Dimensions
ui.Config ui.Config
input.Queue ui.Queue
*ui.Ops *ui.Ops
} }
+1 -2
View File
@@ -6,7 +6,6 @@ import (
"time" "time"
"gioui.org/ui" "gioui.org/ui"
"gioui.org/ui/input"
"gioui.org/ui/layout" "gioui.org/ui/layout"
) )
@@ -150,6 +149,6 @@ func (config) Px(v ui.Value) int {
return int(v.V + .5) return int(v.V + .5)
} }
func (queue) Next(k input.Key) (input.Event, bool) { func (queue) Next(k ui.Key) (ui.Event, bool) {
return nil, false return nil, false
} }
+3 -4
View File
@@ -5,9 +5,8 @@ Package pointer implements pointer events and operations.
A pointer is either a mouse controlled cursor or a touch A pointer is either a mouse controlled cursor or a touch
object such as a finger. object such as a finger.
The InputOp operation is used to declare a handler ready The InputOp operation is used to declare a handler ready for pointer
for pointer events. Use a Queue from package input to events. Use a ui.Queue to receive events.
receive events.
Areas Areas
@@ -72,7 +71,7 @@ drawer handle and the interface below should receive pointer events.
Disambiguation Disambiguation
When more than one handler matches a pointer event, the input queue When more than one handler matches a pointer event, the event queue
follows a set of rules for distributing the event. follows a set of rules for distributing the event.
As long as the pointer has not received a Press event, all As long as the pointer has not received a Press event, all
+2 -3
View File
@@ -9,7 +9,6 @@ import (
"gioui.org/ui" "gioui.org/ui"
"gioui.org/ui/f32" "gioui.org/ui/f32"
"gioui.org/ui/input"
"gioui.org/ui/internal/opconst" "gioui.org/ui/internal/opconst"
) )
@@ -64,7 +63,7 @@ type areaOp struct {
// InputOp declares an input handler ready for pointer // InputOp declares an input handler ready for pointer
// events. // events.
type InputOp struct { type InputOp struct {
Key input.Key Key ui.Key
// Grab, if set, request that the handler get // Grab, if set, request that the handler get
// Grabbed priority. // Grabbed priority.
Grab bool Grab bool
@@ -86,7 +85,7 @@ type Priority uint8
// Source of an Event. // Source of an Event.
type Source uint8 type Source uint8
// Must match input.areaKind // Must match app/internal/input.areaKind
type areaKind uint8 type areaKind uint8
const ( const (
+1 -2
View File
@@ -6,14 +6,13 @@ package system
import ( import (
"gioui.org/ui" "gioui.org/ui"
"gioui.org/ui/input"
"gioui.org/ui/internal/opconst" "gioui.org/ui/internal/opconst"
) )
// ProfileOp registers a handler for receiving // ProfileOp registers a handler for receiving
// ProfileEvents. // ProfileEvents.
type ProfileOp struct { type ProfileOp struct {
Key input.Key Key ui.Key
} }
// ProfileEvent contain profile data from a single // ProfileEvent contain profile data from a single