From dc6fedc163d55806e9fce73709d2bdbf0852e7a4 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Thu, 26 Sep 2019 15:50:51 +0200 Subject: [PATCH] ui: change Queue to return all events at once The Queue interface was changed from type Queue interface { Events(k Key) []Event } to the more complex single-step protocol type Queue interface { Next(k Key) (Event, bool) } to cater for a particular use case: Editor's SubmitEvent. When a SubmitEvent is passed to a caller of Editor.Next, the Editor state, in particular the current text, must not have changed by edits later in the command stream. For example, pressing the keys , , should result in a SubmitEvent where the Editor has a single 'e' in Text(), not two. However, there is no reason to push the more complex Queue to every user. Rather, store remaining input events inside Editor and process them as Editor.Event (or Layout) is called. Finally, revert the Queue interface to the simpler Events method. Signed-off-by: Elias Naur --- ui/app/internal/input/router.go | 16 +++++++--------- ui/app/window.go | 4 ++-- ui/doc.go | 2 +- ui/gesture/gesture.go | 13 +++++++------ ui/input.go | 6 +++--- ui/text/editor.go | 25 ++++++++++++++++++------- 6 files changed, 38 insertions(+), 28 deletions(-) diff --git a/ui/app/internal/input/router.go b/ui/app/internal/input/router.go index a70cdfa6..a663a66b 100644 --- a/ui/app/internal/input/router.go +++ b/ui/app/internal/input/router.go @@ -37,8 +37,8 @@ type handlerEvents struct { updated bool } -func (q *Router) Next(k ui.Key) (ui.Event, bool) { - return q.handlers.Next(k) +func (q *Router) Events(k ui.Key) []ui.Event { + return q.handlers.Events(k) } func (q *Router) Frame(ops *ui.Ops) { @@ -124,14 +124,12 @@ func (h *handlerEvents) Updated() bool { return u } -func (h *handlerEvents) Next(k ui.Key) (ui.Event, bool) { - events := h.handlers[k] - if len(events) == 0 { - return nil, false +func (h *handlerEvents) Events(k ui.Key) []ui.Event { + if events, ok := h.handlers[k]; ok { + h.handlers[k] = h.handlers[k][:0] + return events } - e := events[0] - h.handlers[k] = events[1:] - return e, true + return nil } func (h *handlerEvents) Clear() { diff --git a/ui/app/window.go b/ui/app/window.go index 3f90a7d8..d08b3874 100644 --- a/ui/app/window.go +++ b/ui/app/window.go @@ -323,8 +323,8 @@ func (w *Window) run(opts *windowOptions) { } } -func (q *Queue) Next(k ui.Key) (ui.Event, bool) { - return q.q.Next(k) +func (q *Queue) Events(k ui.Key) []ui.Event { + return q.q.Events(k) } // WithTitle returns an option that sets the window title. diff --git a/ui/doc.go b/ui/doc.go index f46d9681..533282b5 100644 --- a/ui/doc.go +++ b/ui/doc.go @@ -89,7 +89,7 @@ For example: var queue ui.Queue = ... - for e, ok := queue.Next(h); ok; e, ok = queue.Next(h) { + for _, e := range queue.Events(h) { switch e.(type) { ... } diff --git a/ui/gesture/gesture.go b/ui/gesture/gesture.go index 5e58638c..78064628 100644 --- a/ui/gesture/gesture.go +++ b/ui/gesture/gesture.go @@ -105,8 +105,9 @@ func (c *Click) State() ClickState { } // Next returns the next click event, if any. -func (c *Click) Next(q ui.Queue) (ClickEvent, bool) { - for evt, ok := q.Next(c); ok; evt, ok = q.Next(c) { +func (c *Click) Events(q ui.Queue) []ClickEvent { + var events []ClickEvent + for _, evt := range q.Events(c) { e, ok := evt.(pointer.Event) if !ok { continue @@ -116,7 +117,7 @@ func (c *Click) Next(q ui.Queue) (ClickEvent, bool) { wasPressed := c.state == StatePressed c.state = StateNormal if wasPressed { - return ClickEvent{Type: TypeClick, Position: e.Position, Source: e.Source}, true + events = append(events, ClickEvent{Type: TypeClick, Position: e.Position, Source: e.Source}) } case pointer.Cancel: c.state = StateNormal @@ -125,7 +126,7 @@ func (c *Click) Next(q ui.Queue) (ClickEvent, bool) { break } c.state = StatePressed - return ClickEvent{Type: TypePress, Position: e.Position, Source: e.Source}, true + events = append(events, ClickEvent{Type: TypePress, Position: e.Position, Source: e.Source}) case pointer.Move: if c.state == StatePressed && !e.Hit { c.state = StateNormal @@ -134,7 +135,7 @@ func (c *Click) Next(q ui.Queue) (ClickEvent, bool) { } } } - return ClickEvent{}, false + return events } // Add the handler to the operation list to receive scroll events. @@ -159,7 +160,7 @@ func (s *Scroll) Scroll(cfg ui.Config, q ui.Queue, axis Axis) int { return 0 } total := 0 - for evt, ok := q.Next(s); ok; evt, ok = q.Next(s) { + for _, evt := range q.Events(s) { e, ok := evt.(pointer.Event) if !ok { continue diff --git a/ui/input.go b/ui/input.go index 9b85a6f7..57be0ef1 100644 --- a/ui/input.go +++ b/ui/input.go @@ -5,9 +5,9 @@ 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) + // Events returns the available events for a + // Key. + Events(k Key) []Event } // Key is the stable identifier for an event handler. diff --git a/ui/text/editor.go b/ui/text/editor.go index bb417887..cb68d9c9 100644 --- a/ui/text/editor.go +++ b/ui/text/editor.go @@ -62,6 +62,9 @@ type Editor struct { scrollOff image.Point clicker gesture.Click + + // events is the list of events not yet processed. + events []ui.Event } type EditorEvent interface { @@ -71,7 +74,7 @@ type EditorEvent interface { // A ChangeEvent is generated for every user change to the text. type ChangeEvent struct{} -// A SubmitEvent is generated when and Editor's Submit is set +// A SubmitEvent is generated when Submit is set // and a carriage return key is pressed. type SubmitEvent struct{} @@ -80,8 +83,8 @@ const ( maxBlinkDuration = 10 * time.Second ) -// Next returns the next available editor event, or false if none are available. -func (e *Editor) Next(gtx *layout.Context) (EditorEvent, bool) { +// Event returns the next available editor event, or false if none are available. +func (e *Editor) Event(gtx *layout.Context) (EditorEvent, bool) { // Crude configuration change detection. if scale := gtx.Px(ui.Sp(100)); scale != e.oldScale { e.invalidate() @@ -106,7 +109,7 @@ func (e *Editor) Next(gtx *layout.Context) (EditorEvent, bool) { e.scrollOff.Y += sdist soff = e.scrollOff.Y } - for evt, ok := e.clicker.Next(gtx.Queue); ok; evt, ok = e.clicker.Next(gtx.Queue) { + for _, evt := range e.clicker.Events(gtx.Queue) { switch { case evt.Type == gesture.TypePress && evt.Source == pointer.Mouse, evt.Type == gesture.TypeClick && evt.Source == pointer.Touch: @@ -124,7 +127,15 @@ func (e *Editor) Next(gtx *layout.Context) (EditorEvent, bool) { if (sdist > 0 && soff >= smax) || (sdist < 0 && soff <= smin) { e.scroller.Stop() } - for ke, ok := gtx.Queue.Next(e); ok; ke, ok = gtx.Queue.Next(e) { + e.events = append(e.events, gtx.Queue.Events(e)...) + return e.editorEvent(gtx) +} + +func (e *Editor) editorEvent(gtx *layout.Context) (EditorEvent, bool) { + for len(e.events) > 0 { + ke := e.events[0] + copy(e.events, e.events[1:]) + e.events = e.events[:len(e.events)-1] e.blinkStart = gtx.Now() switch ke := ke.(type) { case key.FocusEvent: @@ -133,7 +144,7 @@ func (e *Editor) Next(gtx *layout.Context) (EditorEvent, bool) { if !e.focused { break } - if e.Submit && (ke.Name == key.NameReturn || ke.Name == key.NameEnter) { + if e.Submit && ke.Name == key.NameReturn || ke.Name == key.NameEnter { if !ke.Modifiers.Contain(key.ModShift) { return SubmitEvent{}, true } @@ -166,7 +177,7 @@ func (e *Editor) Focus() { func (e *Editor) Layout(gtx *layout.Context) { cs := gtx.Constraints - for _, ok := e.Next(gtx); ok; _, ok = e.Next(gtx) { + for _, ok := e.Event(gtx); ok; _, ok = e.Event(gtx) { } twoDp := gtx.Px(ui.Dp(2)) e.padLeft, e.padRight = twoDp, twoDp