mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-07 18:35:34 +00:00
widget: add Editor.MaxLen for limiting the content length og Editor
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+22
-5
@@ -47,6 +47,8 @@ type Editor struct {
|
|||||||
Mask rune
|
Mask rune
|
||||||
// InputHint specifies the type of on-screen keyboard to be displayed.
|
// InputHint specifies the type of on-screen keyboard to be displayed.
|
||||||
InputHint key.InputHint
|
InputHint key.InputHint
|
||||||
|
// MaxLen limits the editor content to a maximum length. Zero means no limit.
|
||||||
|
MaxLen int
|
||||||
|
|
||||||
eventKey int
|
eventKey int
|
||||||
font text.Font
|
font text.Font
|
||||||
@@ -334,6 +336,8 @@ func (e *Editor) processKey(gtx layout.Context) {
|
|||||||
if e.rr.Changed() {
|
if e.rr.Changed() {
|
||||||
e.events = append(e.events, ChangeEvent{})
|
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) {
|
for _, ke := range gtx.Events(&e.eventKey) {
|
||||||
e.blinkStart = gtx.Now
|
e.blinkStart = gtx.Now
|
||||||
switch ke := ke.(type) {
|
switch ke := ke.(type) {
|
||||||
@@ -361,7 +365,8 @@ func (e *Editor) processKey(gtx layout.Context) {
|
|||||||
case key.EditEvent:
|
case key.EditEvent:
|
||||||
e.caret.scroll = true
|
e.caret.scroll = true
|
||||||
e.scroller.Stop()
|
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
|
e.caret.xoff = 0
|
||||||
// Complete a paste event, initiated by Shortcut-V in Editor.command().
|
// Complete a paste event, initiated by Shortcut-V in Editor.command().
|
||||||
case clipboard.Event:
|
case clipboard.Event:
|
||||||
@@ -371,6 +376,9 @@ func (e *Editor) processKey(gtx layout.Context) {
|
|||||||
case key.SelectionEvent:
|
case key.SelectionEvent:
|
||||||
e.caret.scroll = true
|
e.caret.scroll = true
|
||||||
e.scroller.Stop()
|
e.scroller.Stop()
|
||||||
|
ke.Start -= adjust
|
||||||
|
ke.End -= adjust
|
||||||
|
adjust = 0
|
||||||
e.caret.start = e.closestPosition(combinedPos{runes: ke.Start}).runes
|
e.caret.start = e.closestPosition(combinedPos{runes: ke.Start}).runes
|
||||||
e.caret.end = e.closestPosition(combinedPos{runes: ke.End}).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.
|
// there is a selection, append overwrites it.
|
||||||
// xxx|yyy + append zzz => xxxzzz|yyy
|
// xxx|yyy + append zzz => xxxzzz|yyy
|
||||||
func (e *Editor) append(s string) {
|
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
|
e.caret.xoff = 0
|
||||||
start := e.caret.start
|
start := e.caret.start
|
||||||
if end := e.caret.end; end < start {
|
if end := e.caret.end; end < start {
|
||||||
start = end
|
start = end
|
||||||
}
|
}
|
||||||
e.caret.start = start + utf8.RuneCountInString(s)
|
e.caret.start = start + moves
|
||||||
e.caret.end = e.caret.start
|
e.caret.end = e.caret.start
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace the text between start and end with s. Indices are in runes.
|
// 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 {
|
if e.SingleLine {
|
||||||
s = strings.ReplaceAll(s, "\n", " ")
|
s = strings.ReplaceAll(s, "\n", " ")
|
||||||
}
|
}
|
||||||
@@ -1169,8 +1178,15 @@ func (e *Editor) replace(start, end int, s string) {
|
|||||||
endPos := e.closestPosition(combinedPos{runes: end})
|
endPos := e.closestPosition(combinedPos{runes: end})
|
||||||
startOff := e.runeOffset(startPos.runes)
|
startOff := e.runeOffset(startPos.runes)
|
||||||
e.rr.deleteRunes(startOff, endPos.runes-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)
|
e.rr.prepend(startOff, s)
|
||||||
newEnd := startPos.runes + utf8.RuneCountInString(s)
|
newEnd := startPos.runes + sc
|
||||||
adjust := func(pos int) int {
|
adjust := func(pos int) int {
|
||||||
switch {
|
switch {
|
||||||
case newEnd < pos && pos <= endPos.runes:
|
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.start = adjust(e.ime.start)
|
||||||
e.ime.end = adjust(e.ime.end)
|
e.ime.end = adjust(e.ime.end)
|
||||||
e.invalidate()
|
e.invalidate()
|
||||||
|
return sc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Editor) movePages(pages int, selAct selectionAction) {
|
func (e *Editor) movePages(pages int, selAct selectionAction) {
|
||||||
|
|||||||
@@ -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 {
|
func textWidth(e *Editor, lineNum, colStart, colEnd int) float32 {
|
||||||
var w fixed.Int26_6
|
var w fixed.Int26_6
|
||||||
glyphs := e.lines[lineNum].Layout.Glyphs
|
glyphs := e.lines[lineNum].Layout.Glyphs
|
||||||
|
|||||||
Reference in New Issue
Block a user