diff --git a/app/doc.go b/app/doc.go index 7b78b5ca..620a4107 100644 --- a/app/doc.go +++ b/app/doc.go @@ -57,7 +57,7 @@ For example, to display a blank but otherwise functional window: Event queue -A Window's Queue method returns an ui.Queue implementation that distributes +A Window's Queue method returns an event.Queue implementation that distributes incoming events to the event handlers declared in the latest call to Update. See the gioui.org/ui package for more information about event handlers. diff --git a/app/internal/input/key.go b/app/internal/input/key.go index 03b13476..7e607862 100644 --- a/app/internal/input/key.go +++ b/app/internal/input/key.go @@ -5,6 +5,7 @@ package input import ( "gioui.org/internal/opconst" "gioui.org/internal/ops" + "gioui.org/io/event" "gioui.org/io/key" "gioui.org/ui" ) @@ -12,8 +13,8 @@ import ( type TextInputState uint8 type keyQueue struct { - focus ui.Key - handlers map[ui.Key]*keyHandler + focus event.Key + handlers map[event.Key]*keyHandler reader ops.Reader state TextInputState } @@ -45,7 +46,7 @@ func (q *keyQueue) InputState() TextInputState { func (q *keyQueue) Frame(root *ui.Ops, events *handlerEvents) { if q.handlers == nil { - q.handlers = make(map[ui.Key]*keyHandler) + q.handlers = make(map[event.Key]*keyHandler) } for _, h := range q.handlers { h.active = false @@ -82,14 +83,14 @@ func (q *keyQueue) Frame(root *ui.Ops, events *handlerEvents) { } } -func (q *keyQueue) Push(e ui.Event, events *handlerEvents) { +func (q *keyQueue) Push(e event.Event, events *handlerEvents) { if q.focus != nil { events.Add(q.focus, e) } } -func (q *keyQueue) resolveFocus(events *handlerEvents) (ui.Key, listenerPriority, bool) { - var k ui.Key +func (q *keyQueue) resolveFocus(events *handlerEvents) (event.Key, listenerPriority, bool) { + var k event.Key var pri listenerPriority var hide bool loop: @@ -115,7 +116,7 @@ loop: h = new(keyHandler) q.handlers[op.Key] = h // Reset the handler on (each) first appearance. - events.Set(op.Key, []ui.Event{key.FocusEvent{Focus: false}}) + events.Set(op.Key, []event.Event{key.FocusEvent{Focus: false}}) } h.active = true case opconst.TypeHideInput: @@ -144,6 +145,6 @@ func decodeKeyInputOp(d []byte, refs []interface{}) key.InputOp { } return key.InputOp{ Focus: d[1] != 0, - Key: refs[0].(ui.Key), + Key: refs[0].(event.Key), } } diff --git a/app/internal/input/pointer.go b/app/internal/input/pointer.go index b8442215..6be1e87d 100644 --- a/app/internal/input/pointer.go +++ b/app/internal/input/pointer.go @@ -9,6 +9,7 @@ import ( "gioui.org/f32" "gioui.org/internal/opconst" "gioui.org/internal/ops" + "gioui.org/io/event" "gioui.org/io/pointer" "gioui.org/ui" ) @@ -16,10 +17,10 @@ import ( type pointerQueue struct { hitTree []hitNode areas []areaNode - handlers map[ui.Key]*pointerHandler + handlers map[event.Key]*pointerHandler pointers []pointerInfo reader ops.Reader - scratch []ui.Key + scratch []event.Key } type hitNode struct { @@ -29,13 +30,13 @@ type hitNode struct { pass bool // For handler nodes. - key ui.Key + key event.Key } type pointerInfo struct { id pointer.ID pressed bool - handlers []ui.Key + handlers []event.Key } type pointerHandler struct { @@ -100,7 +101,7 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t u if !ok { h = new(pointerHandler) q.handlers[op.Key] = h - events.Set(op.Key, []ui.Event{pointer.Event{Type: pointer.Cancel}}) + events.Set(op.Key, []event.Event{pointer.Event{Type: pointer.Cancel}}) } h.active = true h.area = area @@ -110,7 +111,7 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t u } } -func (q *pointerQueue) opHit(handlers *[]ui.Key, pos f32.Point) { +func (q *pointerQueue) opHit(handlers *[]event.Key, pos f32.Point) { // Track whether we're passing through hits. pass := true idx := len(q.hitTree) - 1 @@ -153,7 +154,7 @@ func (a *areaNode) hit(p f32.Point) bool { func (q *pointerQueue) init() { if q.handlers == nil { - q.handlers = make(map[ui.Key]*pointerHandler) + q.handlers = make(map[event.Key]*pointerHandler) } } @@ -175,7 +176,7 @@ func (q *pointerQueue) Frame(root *ui.Ops, events *handlerEvents) { } } -func (q *pointerQueue) dropHandler(k ui.Key) { +func (q *pointerQueue) dropHandler(k event.Key) { for i := range q.pointers { p := &q.pointers[i] for i := len(p.handlers) - 1; i >= 0; i-- { @@ -319,7 +320,7 @@ func decodePointerInputOp(d []byte, refs []interface{}) pointer.InputOp { } return pointer.InputOp{ Grab: d[1] != 0, - Key: refs[0].(ui.Key), + Key: refs[0].(event.Key), } } diff --git a/app/internal/input/router.go b/app/internal/input/router.go index 7a4b8320..c24488bb 100644 --- a/app/internal/input/router.go +++ b/app/internal/input/router.go @@ -8,6 +8,7 @@ import ( "gioui.org/internal/opconst" "gioui.org/internal/ops" + "gioui.org/io/event" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/profile" @@ -38,15 +39,15 @@ type Router struct { wakeupTime time.Time // ProfileOp summary. - profHandlers []ui.Key + profHandlers []event.Key } type handlerEvents struct { - handlers map[ui.Key][]ui.Event + handlers map[event.Key][]event.Event hadEvents bool } -func (q *Router) Events(k ui.Key) []ui.Event { +func (q *Router) Events(k event.Key) []event.Event { events := q.handlers.Events(k) q.deliveredEvents = q.deliveredEvents || len(events) > 0 return events @@ -68,7 +69,7 @@ func (q *Router) Frame(ops *ui.Ops) { } } -func (q *Router) Add(e ui.Event) bool { +func (q *Router) Add(e event.Event) bool { switch e := e.(type) { case pointer.Event: q.pqueue.Push(e, &q.handlers) @@ -114,17 +115,17 @@ func (q *Router) WakeupTime() (time.Time, bool) { func (h *handlerEvents) init() { if h.handlers == nil { - h.handlers = make(map[ui.Key][]ui.Event) + h.handlers = make(map[event.Key][]event.Event) } } -func (h *handlerEvents) Set(k ui.Key, evts []ui.Event) { +func (h *handlerEvents) Set(k event.Key, evts []event.Event) { h.init() h.handlers[k] = evts h.hadEvents = true } -func (h *handlerEvents) Add(k ui.Key, e ui.Event) { +func (h *handlerEvents) Add(k event.Key, e event.Event) { h.init() h.handlers[k] = append(h.handlers[k], e) h.hadEvents = true @@ -136,7 +137,7 @@ func (h *handlerEvents) HadEvents() bool { return u } -func (h *handlerEvents) Events(k ui.Key) []ui.Event { +func (h *handlerEvents) Events(k event.Key) []event.Event { if events, ok := h.handlers[k]; ok { h.handlers[k] = h.handlers[k][:0] return events @@ -155,7 +156,7 @@ func decodeProfileOp(d []byte, refs []interface{}) profile.Op { panic("invalid op") } return profile.Op{ - Key: refs[0].(ui.Key), + Key: refs[0].(event.Key), } } diff --git a/app/window.go b/app/window.go index 79da79c5..fac67c58 100644 --- a/app/window.go +++ b/app/window.go @@ -10,6 +10,7 @@ import ( "gioui.org/app/internal/gpu" "gioui.org/app/internal/input" + "gioui.org/io/event" "gioui.org/io/profile" "gioui.org/ui" ) @@ -31,8 +32,8 @@ type Window struct { drawStart time.Time gpu *gpu.GPU - out chan ui.Event - in chan ui.Event + out chan event.Event + in chan event.Event ack chan struct{} invalidates chan struct{} frames chan *ui.Ops @@ -46,7 +47,7 @@ type Window struct { queue Queue } -// Queue is an ui.Queue implementation that distributes system events +// Queue is an event.Queue implementation that distributes system events // to the input handlers declared in the most recent call to Update. type Queue struct { q input.Router @@ -69,7 +70,7 @@ var _ interface { } = (*window)(nil) // Pre-allocate the ack event to avoid garbage. -var ackEvent ui.Event +var ackEvent event.Event // NewWindow creates a new window for a set of window // options. The options are hints; the platform is free to @@ -94,8 +95,8 @@ func NewWindow(options ...WindowOption) *Window { } w := &Window{ - in: make(chan ui.Event), - out: make(chan ui.Event), + in: make(chan event.Event), + out: make(chan event.Event), ack: make(chan struct{}), invalidates: make(chan struct{}, 1), frames: make(chan *ui.Ops), @@ -105,7 +106,7 @@ func NewWindow(options ...WindowOption) *Window { } // Events returns the channel where events are delivered. -func (w *Window) Events() <-chan ui.Event { +func (w *Window) Events() <-chan event.Event { return w.out } @@ -194,7 +195,7 @@ func (w *Window) setDriver(d *window) { w.event(driverEvent{d}) } -func (w *Window) event(e ui.Event) { +func (w *Window) event(e event.Event) { w.in <- e <-w.ack } @@ -311,7 +312,7 @@ func (w *Window) run(opts *windowOptions) { w.out <- e2 w.ack <- struct{}{} return - case ui.Event: + case event.Event: if w.queue.q.Add(e2) { w.setNextFrame(time.Time{}) w.updateAnimation() @@ -323,7 +324,7 @@ func (w *Window) run(opts *windowOptions) { } } -func (q *Queue) Events(k ui.Key) []ui.Event { +func (q *Queue) Events(k event.Key) []event.Event { return q.q.Events(k) } diff --git a/gesture/gesture.go b/gesture/gesture.go index 1d6832c9..69283859 100644 --- a/gesture/gesture.go +++ b/gesture/gesture.go @@ -14,6 +14,7 @@ import ( "gioui.org/f32" "gioui.org/internal/fling" + "gioui.org/io/event" "gioui.org/io/pointer" "gioui.org/ui" ) @@ -105,7 +106,7 @@ func (c *Click) State() ClickState { } // Events returns the next click event, if any. -func (c *Click) Events(q ui.Queue) []ClickEvent { +func (c *Click) Events(q event.Queue) []ClickEvent { var events []ClickEvent for _, evt := range q.Events(c) { e, ok := evt.(pointer.Event) @@ -154,7 +155,7 @@ func (s *Scroll) Stop() { // Scroll detects the scrolling distance from the available events and // ongoing fling gestures. -func (s *Scroll) Scroll(cfg ui.Config, q ui.Queue, axis Axis) int { +func (s *Scroll) Scroll(cfg ui.Config, q event.Queue, axis Axis) int { if s.axis != axis { s.axis = axis return 0 diff --git a/io/event/event.go b/io/event/event.go new file mode 100644 index 00000000..e91c2776 --- /dev/null +++ b/io/event/event.go @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +/* +Package event contains the types for event handling. + +The Queue interface is the protocol for receiving external events. + +For example: + + var queue event.Queue = ... + + for _, e := range queue.Events(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/io/key + + ops := new(ui.Ops) + var h *Handler = ... + key.InputOp{Key: h}.Add(ops) + +*/ +package event + +// Queue maps an event handler key to the events +// available to the handler. +type Queue interface { + // Events returns the available events for a + // Key. + Events(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 events. +type Event interface { + ImplementsEvent() +} diff --git a/io/key/key.go b/io/key/key.go index ba3dfb62..12ae46dd 100644 --- a/io/key/key.go +++ b/io/key/key.go @@ -10,8 +10,9 @@ events. package key import ( - "gioui.org/ui" "gioui.org/internal/opconst" + "gioui.org/io/event" + "gioui.org/ui" ) // InputOp declares a handler ready for key events. @@ -19,7 +20,7 @@ import ( // focused key handler. Set the Focus flag to request // the focus. type InputOp struct { - Key ui.Key + Key event.Key Focus bool } diff --git a/io/pointer/doc.go b/io/pointer/doc.go index 94585baf..f023e74d 100644 --- a/io/pointer/doc.go +++ b/io/pointer/doc.go @@ -6,7 +6,7 @@ A pointer is either a mouse controlled cursor or a touch object such as a finger. The InputOp operation is used to declare a handler ready for pointer -events. Use a ui.Queue to receive events. +events. Use an event.Queue to receive events. Areas diff --git a/io/pointer/pointer.go b/io/pointer/pointer.go index 6b763af6..fb784adb 100644 --- a/io/pointer/pointer.go +++ b/io/pointer/pointer.go @@ -7,9 +7,10 @@ import ( "image" "time" - "gioui.org/ui" "gioui.org/f32" "gioui.org/internal/opconst" + "gioui.org/io/event" + "gioui.org/ui" ) // Event is a pointer event. @@ -63,7 +64,7 @@ type areaOp struct { // InputOp declares an input handler ready for pointer // events. type InputOp struct { - Key ui.Key + Key event.Key // Grab, if set, request that the handler get // Grabbed priority. Grab bool diff --git a/io/profile/profile.go b/io/profile/profile.go index fc06ebb4..d7ab2ce0 100644 --- a/io/profile/profile.go +++ b/io/profile/profile.go @@ -6,13 +6,14 @@ package profile import ( "gioui.org/internal/opconst" + "gioui.org/io/event" "gioui.org/ui" ) // Op registers a handler for receiving // Events. type Op struct { - Key ui.Key + Key event.Key } // Event contains profile data from a single diff --git a/layout/layout.go b/layout/layout.go index 9ea371df..e9048f04 100644 --- a/layout/layout.go +++ b/layout/layout.go @@ -5,6 +5,7 @@ package layout import ( "image" + "gioui.org/io/event" "gioui.org/ui" ) @@ -51,7 +52,7 @@ type Context struct { Dimensions Dimensions ui.Config - ui.Queue + event.Queue *ui.Ops } diff --git a/layout/layout_test.go b/layout/layout_test.go index 5acb6bb7..5c5a8dd5 100644 --- a/layout/layout_test.go +++ b/layout/layout_test.go @@ -5,6 +5,7 @@ import ( "image" "time" + "gioui.org/io/event" "gioui.org/layout" "gioui.org/ui" ) @@ -149,6 +150,6 @@ func (config) Px(v ui.Value) int { return int(v.V + .5) } -func (queue) Events(k ui.Key) []ui.Event { +func (queue) Events(k event.Key) []event.Event { return nil } diff --git a/text/editor.go b/text/editor.go index 76c558c3..d0117027 100644 --- a/text/editor.go +++ b/text/editor.go @@ -10,6 +10,7 @@ import ( "unicode/utf8" "gioui.org/gesture" + "gioui.org/io/event" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/layout" @@ -64,7 +65,7 @@ type Editor struct { clicker gesture.Click // events is the list of events not yet processed. - events []ui.Event + events []event.Event } type EditorEvent interface { diff --git a/ui/doc.go b/ui/doc.go index 570e2f6f..5e7fd7e5 100644 --- a/ui/doc.go +++ b/ui/doc.go @@ -81,31 +81,5 @@ To maintain a constant visual size across platforms and displays, always use dps or sps to define user interfaces. Only use pixels for derived values. -Events - -The Queue interface is the protocol for receiving external events. - -For example: - - var queue ui.Queue = ... - - for _, e := range queue.Events(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/io/key - - ops := new(ui.Ops) - var h *Handler = ... - key.InputOp{Key: h}.Add(ops) - */ package ui diff --git a/ui/input.go b/ui/input.go deleted file mode 100644 index 57be0ef1..00000000 --- a/ui/input.go +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package ui - -// Queue maps an event handler key to the events -// available to the handler. -type Queue interface { - // Events returns the available events for a - // Key. - Events(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 events. -type Event interface { - ImplementsEvent() -}