~eliasnaur/gio

1d9ab6531396466de17614cfbb21b04ec6423b4a — Elias Naur 10 months ago 48e9cda
widget: add Editor.MaxLen for limiting the content length og Editor

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2 files changed, 53 insertions(+), 5 deletions(-)

M widget/editor.go
M widget/editor_test.go
M widget/editor.go => widget/editor.go +22 -5
@@ 47,6 47,8 @@ type Editor struct {
	Mask rune
	// InputHint specifies the type of on-screen keyboard to be displayed.
	InputHint key.InputHint
	// MaxLen limits the editor content to a maximum length. Zero means no limit.
	MaxLen int

	eventKey     int
	font         text.Font


@@ 334,6 336,8 @@ func (e *Editor) processKey(gtx layout.Context) {
	if e.rr.Changed() {
		e.events = append(e.events, ChangeEvent{})
	}
	// adjust keeps track of runes dropped because of MaxLen.
	var adjust int
	for _, ke := range gtx.Events(&e.eventKey) {
		e.blinkStart = gtx.Now
		switch ke := ke.(type) {


@@ 361,7 365,8 @@ func (e *Editor) processKey(gtx layout.Context) {
		case key.EditEvent:
			e.caret.scroll = true
			e.scroller.Stop()
			e.replace(ke.Range.Start, ke.Range.End, ke.Text)
			moves := e.replace(ke.Range.Start, ke.Range.End, ke.Text)
			adjust += utf8.RuneCountInString(ke.Text) - moves
			e.caret.xoff = 0
		// Complete a paste event, initiated by Shortcut-V in Editor.command().
		case clipboard.Event:


@@ 371,6 376,9 @@ func (e *Editor) processKey(gtx layout.Context) {
		case key.SelectionEvent:
			e.caret.scroll = true
			e.scroller.Stop()
			ke.Start -= adjust
			ke.End -= adjust
			adjust = 0
			e.caret.start = e.closestPosition(combinedPos{runes: ke.Start}).runes
			e.caret.end = e.closestPosition(combinedPos{runes: ke.End}).runes
		}


@@ 1147,18 1155,19 @@ func (e *Editor) Insert(s string) {
// there is a selection, append overwrites it.
// xxx|yyy + append zzz => xxxzzz|yyy
func (e *Editor) append(s string) {
	e.replace(e.caret.start, e.caret.end, s)
	moves := e.replace(e.caret.start, e.caret.end, s)
	e.caret.xoff = 0
	start := e.caret.start
	if end := e.caret.end; end < start {
		start = end
	}
	e.caret.start = start + utf8.RuneCountInString(s)
	e.caret.start = start + moves
	e.caret.end = e.caret.start
}

// replace the text between start and end with s. Indices are in runes.
func (e *Editor) replace(start, end int, s string) {
// It returns the number of runes inserted.
func (e *Editor) replace(start, end int, s string) int {
	if e.SingleLine {
		s = strings.ReplaceAll(s, "\n", " ")
	}


@@ 1169,8 1178,15 @@ func (e *Editor) replace(start, end int, s string) {
	endPos := e.closestPosition(combinedPos{runes: end})
	startOff := e.runeOffset(startPos.runes)
	e.rr.deleteRunes(startOff, endPos.runes-startPos.runes)
	sc := utf8.RuneCountInString(s)
	el := e.Len()
	for e.MaxLen > 0 && el+sc > e.MaxLen {
		_, n := utf8.DecodeLastRuneInString(s)
		s = s[:len(s)-n]
		sc--
	}
	e.rr.prepend(startOff, s)
	newEnd := startPos.runes + utf8.RuneCountInString(s)
	newEnd := startPos.runes + sc
	adjust := func(pos int) int {
		switch {
		case newEnd < pos && pos <= endPos.runes:


@@ 1186,6 1202,7 @@ func (e *Editor) replace(start, end int, s string) {
	e.ime.start = adjust(e.ime.start)
	e.ime.end = adjust(e.ime.end)
	e.invalidate()
	return sc
}

func (e *Editor) movePages(pages int, selAct selectionAction) {

M widget/editor_test.go => widget/editor_test.go +31 -0
@@ 947,6 947,37 @@ func TestEditor_WriteTo(t *testing.T) {
	}
}

func TestEditor_MaxLen(t *testing.T) {
	e := new(Editor)

	e.MaxLen = 8
	e.SetText("123456789")
	if got, want := e.Text(), "12345678"; got != want {
		t.Errorf("editor failed to cap SetText")
	}

	e.SetText("2345678")
	gtx := layout.Context{
		Ops:         new(op.Ops),
		Constraints: layout.Exact(image.Pt(100, 100)),
		Queue: newQueue(
			key.EditEvent{Range: key.Range{Start: 0, End: 0}, Text: "1234"},
			key.SelectionEvent{Start: 4, End: 4},
		),
	}
	cache := text.NewCache(gofont.Collection())
	fontSize := unit.Sp(10)
	font := text.Font{}
	e.Layout(gtx, cache, font, fontSize, nil)

	if got, want := e.Text(), "12345678"; got != want {
		t.Errorf("editor failed to cap EditEvent")
	}
	if start, end := e.Selection(); start != 1 || end != 1 {
		t.Errorf("editor failed to adjust SelectionEvent")
	}
}

func textWidth(e *Editor, lineNum, colStart, colEnd int) float32 {
	var w fixed.Int26_6
	glyphs := e.lines[lineNum].Layout.Glyphs