text: replace Editor.Event with Editor.Event and add Text to SubmitEvent

THe single-stepping Event was done so that SubmitEvents didn't have to
copy the text out if it wasn't needed.

This is a dubious optimization, because SubmitEvents are only generated
when Editor.Submit is true, in which case it is highly likely the user
wants the text.

Furthermore, I expect Editors with Submit enabled to be relatively short,
in the order of a message field in a chat program.

Finally, Editor will need support for undo at some point. Supporting undo
means we can support a lightweight SubmitEvent that points to an older Editor
state.

All in all (1) we gain a simpler Events method, and (2) we can allow the user
to call it before or after Editor.Layout. Before this change any the events were
gone after Layout was called.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-10-11 18:11:47 +02:00
parent d8e3d1077c
commit a5b22860ac
+32 -20
View File
@@ -10,7 +10,6 @@ import (
"gioui.org/f32" "gioui.org/f32"
"gioui.org/gesture" "gioui.org/gesture"
"gioui.org/io/event"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/layout" "gioui.org/layout"
@@ -32,6 +31,7 @@ type Editor struct {
// If not enabled, carriage returns are inserted as newlines in the text. // If not enabled, carriage returns are inserted as newlines in the text.
Submit bool Submit bool
eventKey int
scale int scale int
font Font font Font
blinkStart time.Time blinkStart time.Time
@@ -58,7 +58,7 @@ type Editor struct {
clicker gesture.Click clicker gesture.Click
// events is the list of events not yet processed. // events is the list of events not yet processed.
events []event.Event events []EditorEvent
} }
type EditorEvent interface { type EditorEvent interface {
@@ -70,7 +70,9 @@ type ChangeEvent struct{}
// A SubmitEvent is generated when Submit is set // A SubmitEvent is generated when Submit is set
// and a carriage return key is pressed. // and a carriage return key is pressed.
type SubmitEvent struct{} type SubmitEvent struct {
Text string
}
type line struct { type line struct {
offset f32.Point offset f32.Point
@@ -82,8 +84,20 @@ const (
maxBlinkDuration = 10 * time.Second maxBlinkDuration = 10 * time.Second
) )
// Event returns the next available editor event, or false if none are available. // Events returns available editor events.
func (e *Editor) Event(gtx *layout.Context) (EditorEvent, bool) { func (e *Editor) Events(gtx *layout.Context) []EditorEvent {
e.processEvents(gtx)
events := e.events
e.events = nil
return events
}
func (e *Editor) processEvents(gtx *layout.Context) {
e.processPointer(gtx)
e.processKey(gtx)
}
func (e *Editor) processPointer(gtx *layout.Context) {
sbounds := e.scrollBounds() sbounds := e.scrollBounds()
var smin, smax int var smin, smax int
var axis gesture.Axis var axis gesture.Axis
@@ -103,7 +117,7 @@ func (e *Editor) Event(gtx *layout.Context) (EditorEvent, bool) {
e.scrollRel(0, sdist) e.scrollRel(0, sdist)
soff = e.scrollOff.Y soff = e.scrollOff.Y
} }
for _, evt := range e.clicker.Events(gtx.Queue) { for _, evt := range e.clicker.Events(gtx) {
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:
@@ -121,15 +135,10 @@ func (e *Editor) Event(gtx *layout.Context) (EditorEvent, bool) {
if (sdist > 0 && soff >= smax) || (sdist < 0 && soff <= smin) { if (sdist > 0 && soff >= smax) || (sdist < 0 && soff <= smin) {
e.scroller.Stop() e.scroller.Stop()
} }
e.events = append(e.events, gtx.Queue.Events(e)...)
return e.editorEvent(gtx)
} }
func (e *Editor) editorEvent(gtx *layout.Context) (EditorEvent, bool) { func (e *Editor) processKey(gtx *layout.Context) {
for len(e.events) > 0 { for _, ke := range gtx.Events(&e.eventKey) {
ke := e.events[0]
copy(e.events, e.events[1:])
e.events = e.events[:len(e.events)-1]
e.blinkStart = gtx.Now() e.blinkStart = gtx.Now()
switch ke := ke.(type) { switch ke := ke.(type) {
case key.FocusEvent: case key.FocusEvent:
@@ -140,7 +149,9 @@ func (e *Editor) editorEvent(gtx *layout.Context) (EditorEvent, bool) {
} }
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) { if !ke.Modifiers.Contain(key.ModShift) {
return SubmitEvent{}, true e.events = append(e.events, SubmitEvent{
Text: e.Text(),
})
} }
} }
if e.command(ke) { if e.command(ke) {
@@ -153,10 +164,9 @@ func (e *Editor) editorEvent(gtx *layout.Context) (EditorEvent, bool) {
e.append(ke.Text) e.append(ke.Text)
} }
if e.rr.Changed() { if e.rr.Changed() {
return ChangeEvent{}, true e.events = append(e.events, ChangeEvent{})
} }
} }
return nil, false
} }
// Focus requests the input focus for the Editor. // Focus requests the input focus for the Editor.
@@ -164,18 +174,20 @@ func (e *Editor) Focus() {
e.requestFocus = true e.requestFocus = true
} }
// Layout flushes any remaining events and lays out the editor. // Layout lays out the editor.
func (e *Editor) Layout(gtx *layout.Context, sh *Shaper, font Font) { func (e *Editor) Layout(gtx *layout.Context, sh *Shaper, font Font) {
if e.font != font { if e.font != font {
e.invalidate() e.invalidate()
e.font = font e.font = font
} }
e.processEvents(gtx)
e.layout(gtx, sh) e.layout(gtx, sh)
if !e.clicker.Active() {
e.events = nil
}
} }
func (e *Editor) layout(gtx *layout.Context, sh *Shaper) { func (e *Editor) layout(gtx *layout.Context, sh *Shaper) {
for _, ok := e.Event(gtx); ok; _, ok = e.Event(gtx) {
}
// Crude configuration change detection. // Crude configuration change detection.
if scale := gtx.Px(unit.Sp(100)); scale != e.scale { if scale := gtx.Px(unit.Sp(100)); scale != e.scale {
e.invalidate() e.invalidate()
@@ -230,7 +242,7 @@ func (e *Editor) layout(gtx *layout.Context, sh *Shaper) {
e.shapes = append(e.shapes, line{off, path}) e.shapes = append(e.shapes, line{off, path})
} }
key.InputOp{Key: e, Focus: e.requestFocus}.Add(gtx.Ops) key.InputOp{Key: &e.eventKey, Focus: e.requestFocus}.Add(gtx.Ops)
e.requestFocus = false e.requestFocus = false
pointerPadding := gtx.Px(unit.Dp(4)) pointerPadding := gtx.Px(unit.Dp(4))
r := image.Rectangle{Max: e.viewSize} r := image.Rectangle{Max: e.viewSize}