diff --git a/widgets/textinput/editor.go b/widgets/textinput/editor.go index 2fb50b8..63df1d1 100644 --- a/widgets/textinput/editor.go +++ b/widgets/textinput/editor.go @@ -280,8 +280,8 @@ type fieldEditor struct { } // newFieldEditor returns a new fieldEditor instance. -func newFieldEditor() *fieldEditor { - return &fieldEditor{} +func newFieldEditor(onChange ChangeFn) *fieldEditor { + return &fieldEditor{onChange: onChange} } // minFieldWidth is the minimum supported width of the text input field. @@ -329,12 +329,7 @@ func (fe *fieldEditor) content() string { // reset resets the content back to zero. func (fe *fieldEditor) reset() { - newValue := newFieldEditor() - if fe.onChange != nil { - fe.onChange("") - newValue.onChange = fe.onChange - } - *fe = *newValue + *fe = *newFieldEditor(fe.onChange) } // insert inserts the rune at the current position of the cursor. diff --git a/widgets/textinput/editor_test.go b/widgets/textinput/editor_test.go index bb76ef6..89e6937 100644 --- a/widgets/textinput/editor_test.go +++ b/widgets/textinput/editor_test.go @@ -309,7 +309,7 @@ func TestCurCell(t *testing.T) { for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { - fe := newFieldEditor() + fe := newFieldEditor(nil) fe.data = tc.data fe.firstRune = tc.firstRune fe.curDataPos = tc.curDataPos @@ -323,14 +323,14 @@ func TestCurCell(t *testing.T) { func TestFieldEditor(t *testing.T) { tests := []struct { - desc string - width int - ops func(*fieldEditor) error - wantView string - wantContent string - wantCurIdx int - wantErr bool - mutations int + desc string + width int + ops func(*fieldEditor) error + wantView string + wantContent string + wantCurIdx int + wantErr bool + wantOnChangeCalls int }{ { desc: "fails for width too small", @@ -353,10 +353,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('c') return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 3, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 3, + wantOnChangeCalls: 3, }, { desc: "longer data than the width, cursor at the end", @@ -368,10 +368,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('d') return nil }, - wantView: "⇦cd", - wantContent: "abcd", - wantCurIdx: 3, - mutations: 4, + wantView: "⇦cd", + wantContent: "abcd", + wantCurIdx: 3, + wantOnChangeCalls: 4, }, { desc: "longer data than the width, cursor at the end, has full-width runes", @@ -383,10 +383,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('世') return nil }, - wantView: "⇦世", - wantContent: "abc世", - wantCurIdx: 3, - mutations: 4, + wantView: "⇦世", + wantContent: "abc世", + wantCurIdx: 3, + wantOnChangeCalls: 4, }, { desc: "width decreased, adjusts cursor and shifts data", @@ -401,10 +401,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('d') return nil }, - wantView: "⇦cd", - wantContent: "abcd", - wantCurIdx: 3, - mutations: 4, + wantView: "⇦cd", + wantContent: "abcd", + wantCurIdx: 3, + wantOnChangeCalls: 4, }, { desc: "cursor won't go right beyond the end of the data", @@ -419,10 +419,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦cd", - wantContent: "abcd", - wantCurIdx: 3, - mutations: 4, + wantView: "⇦cd", + wantContent: "abcd", + wantCurIdx: 3, + wantOnChangeCalls: 4, }, { desc: "moves cursor to the left", @@ -438,10 +438,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦cd", - wantContent: "abcd", - wantCurIdx: 2, - mutations: 4, + wantView: "⇦cd", + wantContent: "abcd", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "scrolls content to the left, start becomes visible", @@ -459,10 +459,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "abc⇨", - wantContent: "abcd", - wantCurIdx: 1, - mutations: 4, + wantView: "abc⇨", + wantContent: "abcd", + wantCurIdx: 1, + wantOnChangeCalls: 4, }, { desc: "scrolls content to the left, both ends invisible", @@ -481,10 +481,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦cd⇨", - wantContent: "abcde", - wantCurIdx: 1, - mutations: 5, + wantView: "⇦cd⇨", + wantContent: "abcde", + wantCurIdx: 1, + wantOnChangeCalls: 5, }, { desc: "scrolls left, then back right to make end visible again", @@ -509,10 +509,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦de", - wantContent: "abcde", - wantCurIdx: 3, - mutations: 5, + wantView: "⇦de", + wantContent: "abcde", + wantCurIdx: 3, + wantOnChangeCalls: 5, }, { desc: "scrolls left, won't go beyond the start of data", @@ -534,10 +534,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "abc⇨", - wantContent: "abcde", - wantCurIdx: 0, - mutations: 5, + wantView: "abc⇨", + wantContent: "abcde", + wantCurIdx: 0, + wantOnChangeCalls: 5, }, { desc: "scrolls left, then back right won't go beyond the end of data", @@ -563,10 +563,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦de", - wantContent: "abcde", - wantCurIdx: 3, - mutations: 5, + wantView: "⇦de", + wantContent: "abcde", + wantCurIdx: 3, + wantOnChangeCalls: 5, }, { desc: "have less data than width, all fits", @@ -580,10 +580,10 @@ func TestFieldEditor(t *testing.T) { } return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 3, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 3, + wantOnChangeCalls: 3, }, { desc: "moves cursor to the start", @@ -600,10 +600,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorStart() return nil }, - wantView: "abc⇨", - wantContent: "abcde", - wantCurIdx: 0, - mutations: 5, + wantView: "abc⇨", + wantContent: "abcde", + wantCurIdx: 0, + wantOnChangeCalls: 5, }, { desc: "moves cursor to the end", @@ -624,10 +624,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorEnd() return nil }, - wantView: "⇦de", - wantContent: "abcde", - wantCurIdx: 3, - mutations: 5, + wantView: "⇦de", + wantContent: "abcde", + wantCurIdx: 3, + wantOnChangeCalls: 5, }, { desc: "deletesBefore when cursor after the data", @@ -644,10 +644,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "⇦cd", - wantContent: "abcd", - wantCurIdx: 3, - mutations: 6, + wantView: "⇦cd", + wantContent: "abcd", + wantCurIdx: 3, + wantOnChangeCalls: 6, }, { desc: "deletesBefore when cursor after the data, text has full-width rune", @@ -664,10 +664,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "⇦世", - wantContent: "abc世", - wantCurIdx: 3, - mutations: 6, + wantView: "⇦世", + wantContent: "abc世", + wantCurIdx: 3, + wantOnChangeCalls: 6, }, { desc: "deletesBefore when cursor in the middle", @@ -690,10 +690,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "acd⇨", - wantContent: "acde", - wantCurIdx: 1, - mutations: 6, + wantView: "acd⇨", + wantContent: "acde", + wantCurIdx: 1, + wantOnChangeCalls: 6, }, { desc: "deletesBefore when cursor in the middle, full-width runes", @@ -716,10 +716,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "世c⇨", - wantContent: "世cde", - wantCurIdx: 2, - mutations: 6, + wantView: "世c⇨", + wantContent: "世cde", + wantCurIdx: 2, + wantOnChangeCalls: 6, }, { desc: "deletesBefore does nothing when cursor at the start", @@ -740,10 +740,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "abc⇨", - wantContent: "abcde", - wantCurIdx: 0, - mutations: 5, + wantView: "abc⇨", + wantContent: "abcde", + wantCurIdx: 0, + wantOnChangeCalls: 5, }, { desc: "delete does nothing when cursor at the end", @@ -760,10 +760,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "⇦de", - wantContent: "abcde", - wantCurIdx: 3, - mutations: 5, + wantView: "⇦de", + wantContent: "abcde", + wantCurIdx: 3, + wantOnChangeCalls: 5, }, { desc: "delete in the middle, last rune remains hidden", @@ -785,10 +785,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "acd⇨", - wantContent: "acde", - wantCurIdx: 1, - mutations: 6, + wantView: "acd⇨", + wantContent: "acde", + wantCurIdx: 1, + wantOnChangeCalls: 6, }, { desc: "delete in the middle, last rune becomes visible", @@ -811,10 +811,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "ade", - wantContent: "ade", - wantCurIdx: 1, - mutations: 7, + wantView: "ade", + wantContent: "ade", + wantCurIdx: 1, + wantOnChangeCalls: 7, }, { desc: "delete in the middle, last full-width rune would be invisible, shifts to keep cursor in window", @@ -838,10 +838,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "⇦世", - wantContent: "ab世", - wantCurIdx: 1, - mutations: 7, + wantView: "⇦世", + wantContent: "ab世", + wantCurIdx: 1, + wantOnChangeCalls: 7, }, { desc: "delete in the middle, last rune was and is visible", @@ -861,10 +861,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "ac", - wantContent: "ac", - wantCurIdx: 1, - mutations: 4, + wantView: "ac", + wantContent: "ac", + wantCurIdx: 1, + wantOnChangeCalls: 4, }, { desc: "delete in the middle, last full-width rune was and is visible", @@ -884,10 +884,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "a世", - wantContent: "a世", - wantCurIdx: 1, - mutations: 4, + wantView: "a世", + wantContent: "a世", + wantCurIdx: 1, + wantOnChangeCalls: 4, }, { desc: "delete last rune, contains full-width runes", @@ -908,10 +908,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "", - wantContent: "", - wantCurIdx: 0, - mutations: 6, + wantView: "", + wantContent: "", + wantCurIdx: 0, + wantOnChangeCalls: 6, }, { desc: "half-width runes only, exact fit", @@ -925,10 +925,10 @@ func TestFieldEditor(t *testing.T) { } return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 3, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 3, + wantOnChangeCalls: 3, }, { desc: "full-width runes only, exact fit", @@ -942,10 +942,10 @@ func TestFieldEditor(t *testing.T) { } return nil }, - wantView: "你好世", - wantContent: "你好世", - wantCurIdx: 6, - mutations: 3, + wantView: "你好世", + wantContent: "你好世", + wantCurIdx: 6, + wantOnChangeCalls: 3, }, { desc: "half-width runes only, both ends hidden", @@ -964,10 +964,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦cd⇨", - wantContent: "abcde", - wantCurIdx: 1, - mutations: 5, + wantView: "⇦cd⇨", + wantContent: "abcde", + wantCurIdx: 1, + wantOnChangeCalls: 5, }, { desc: "half-width runes only, both ends invisible, scrolls to make start visible", @@ -987,10 +987,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "abc⇨", - wantContent: "abcde", - wantCurIdx: 1, - mutations: 5, + wantView: "abc⇨", + wantContent: "abcde", + wantCurIdx: 1, + wantOnChangeCalls: 5, }, { desc: "half-width runes only, both ends invisible, deletes to make start visible", @@ -1010,10 +1010,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "acd⇨", - wantContent: "acde", - wantCurIdx: 1, - mutations: 6, + wantView: "acd⇨", + wantContent: "acde", + wantCurIdx: 1, + wantOnChangeCalls: 6, }, { desc: "half-width runes only, deletion on second page refills the field", @@ -1033,10 +1033,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "⇦df", - wantContent: "abcdf", - wantCurIdx: 2, - mutations: 7, + wantView: "⇦df", + wantContent: "abcdf", + wantCurIdx: 2, + wantOnChangeCalls: 7, }, { desc: "half-width runes only, both ends invisible, scrolls to make end visible", @@ -1060,10 +1060,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦de", - wantContent: "abcde", - wantCurIdx: 2, - mutations: 5, + wantView: "⇦de", + wantContent: "abcde", + wantCurIdx: 2, + wantOnChangeCalls: 5, }, { desc: "half-width runes only, both ends invisible, deletes to make end visible", @@ -1086,10 +1086,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "⇦de", - wantContent: "abde", - wantCurIdx: 1, - mutations: 6, + wantView: "⇦de", + wantContent: "abde", + wantCurIdx: 1, + wantOnChangeCalls: 6, }, { desc: "full-width runes only, both ends invisible", @@ -1106,10 +1106,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦⇦世⇨", - wantContent: "你好世界", - wantCurIdx: 2, - mutations: 4, + wantView: "⇦⇦世⇨", + wantContent: "你好世界", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "full-width runes only, both ends invisible, scrolls to make start visible", @@ -1130,10 +1130,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "你好⇨", - wantContent: "你好世界", - wantCurIdx: 2, - mutations: 4, + wantView: "你好⇨", + wantContent: "你好世界", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "full-width runes only, both ends invisible, deletes to make start visible", @@ -1154,10 +1154,10 @@ func TestFieldEditor(t *testing.T) { fe.deleteBefore() return nil }, - wantView: "你世⇨", - wantContent: "你世界", - wantCurIdx: 2, - mutations: 5, + wantView: "你世⇨", + wantContent: "你世界", + wantCurIdx: 2, + wantOnChangeCalls: 5, }, { desc: "full-width runes only, both ends invisible, scrolls to make end visible", @@ -1178,10 +1178,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦⇦界", - wantContent: "你好世界", - wantCurIdx: 2, - mutations: 4, + wantView: "⇦⇦界", + wantContent: "你好世界", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "full-width runes only, both ends invisible, deletes to make end visible", @@ -1202,10 +1202,10 @@ func TestFieldEditor(t *testing.T) { fe.delete() return nil }, - wantView: "⇦⇦界", - wantContent: "你好界", - wantCurIdx: 2, - mutations: 5, + wantView: "⇦⇦界", + wantContent: "你好界", + wantCurIdx: 2, + wantOnChangeCalls: 5, }, { desc: "scrolls to make full-width rune appear at the beginning", @@ -1223,10 +1223,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "你b⇨", - wantContent: "你bcd", - wantCurIdx: 2, - mutations: 4, + wantView: "你b⇨", + wantContent: "你bcd", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "scrolls to make full-width rune appear at the end", @@ -1245,10 +1245,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦你", - wantContent: "abc你", - wantCurIdx: 1, - mutations: 4, + wantView: "⇦你", + wantContent: "abc你", + wantCurIdx: 1, + wantOnChangeCalls: 4, }, { desc: "inserts after last full width rune, first is half-width", @@ -1264,10 +1264,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('e') return nil }, - wantView: "⇦c你e", - wantContent: "abc你e", - wantCurIdx: 5, - mutations: 5, + wantView: "⇦c你e", + wantContent: "abc你e", + wantCurIdx: 5, + wantOnChangeCalls: 5, }, { desc: "inserts after last full width rune, first is half-width", @@ -1282,10 +1282,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('d') return nil }, - wantView: "⇦你d", - wantContent: "世b你d", - wantCurIdx: 4, - mutations: 4, + wantView: "⇦你d", + wantContent: "世b你d", + wantCurIdx: 4, + wantOnChangeCalls: 4, }, { desc: "inserts after last full width rune, hidden rune is full-width", @@ -1300,10 +1300,10 @@ func TestFieldEditor(t *testing.T) { fe.insert('d') return nil }, - wantView: "⇦⇦cd", - wantContent: "世你cd", - wantCurIdx: 4, - mutations: 4, + wantView: "⇦⇦cd", + wantContent: "世你cd", + wantCurIdx: 4, + wantOnChangeCalls: 4, }, { desc: "scrolls right, first is full-width, last are half-width", @@ -1327,10 +1327,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦⇦def⇨", - wantContent: "a你世defgh", - wantCurIdx: 3, - mutations: 8, + wantView: "⇦⇦def⇨", + wantContent: "a你世defgh", + wantCurIdx: 3, + wantOnChangeCalls: 8, }, { desc: "scrolls right, first is half-width, last is full-width", @@ -1354,10 +1354,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦你世⇨", - wantContent: "abc你世fgh", - wantCurIdx: 3, - mutations: 8, + wantView: "⇦你世⇨", + wantContent: "abc你世fgh", + wantCurIdx: 3, + wantOnChangeCalls: 8, }, { desc: "scrolls right, first and last are full-width", @@ -1375,10 +1375,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦⇦世⇨", - wantContent: "你好世界", - wantCurIdx: 2, - mutations: 4, + wantView: "⇦⇦世⇨", + wantContent: "你好世界", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "scrolls right, first and last are half-width", @@ -1402,10 +1402,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRight() return nil }, - wantView: "⇦cdef⇨", - wantContent: "abcdefg", - wantCurIdx: 4, - mutations: 7, + wantView: "⇦cdef⇨", + wantContent: "abcdefg", + wantCurIdx: 4, + wantOnChangeCalls: 7, }, { desc: "scrolls left, first is full-width, last are half-width", @@ -1429,10 +1429,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦⇦def⇨", - wantContent: "a你世defgh", - wantCurIdx: 2, - mutations: 8, + wantView: "⇦⇦def⇨", + wantContent: "a你世defgh", + wantCurIdx: 2, + wantOnChangeCalls: 8, }, { desc: "scrolls left, first is half-width, last is full-width", @@ -1456,10 +1456,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦你世⇨", - wantContent: "abc你世fgh", - wantCurIdx: 1, - mutations: 8, + wantView: "⇦你世⇨", + wantContent: "abc你世fgh", + wantCurIdx: 1, + wantOnChangeCalls: 8, }, { desc: "scrolls left, first and last are full-width", @@ -1476,10 +1476,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦⇦世⇨", - wantContent: "你好世界", - wantCurIdx: 2, - mutations: 4, + wantView: "⇦⇦世⇨", + wantContent: "你好世界", + wantCurIdx: 2, + wantOnChangeCalls: 4, }, { desc: "scrolls left, first and last are half-width", @@ -1502,10 +1502,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorLeft() return nil }, - wantView: "⇦cdef⇨", - wantContent: "abcdefg", - wantCurIdx: 1, - mutations: 7, + wantView: "⇦cdef⇨", + wantContent: "abcdefg", + wantCurIdx: 1, + wantOnChangeCalls: 7, }, { desc: "resets the field editor", @@ -1520,10 +1520,10 @@ func TestFieldEditor(t *testing.T) { fe.reset() return nil }, - wantView: "", - wantContent: "", - wantCurIdx: 0, - mutations: 3, + wantView: "", + wantContent: "", + wantCurIdx: 0, + wantOnChangeCalls: 4, }, { desc: "doesn't insert runes with rune width of zero", @@ -1537,10 +1537,10 @@ func TestFieldEditor(t *testing.T) { } return nil }, - wantView: "ac", - wantContent: "ac", - wantCurIdx: 2, - mutations: 2, + wantView: "ac", + wantContent: "ac", + wantCurIdx: 2, + wantOnChangeCalls: 2, }, { desc: "all text visible, moves cursor to position zero", @@ -1555,10 +1555,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(0) return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 0, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 0, + wantOnChangeCalls: 3, }, { desc: "all text visible, moves cursor to position in the middle", @@ -1573,10 +1573,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(1) return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 1, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 1, + wantOnChangeCalls: 3, }, { desc: "all text visible, moves cursor back to the last character", @@ -1592,10 +1592,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(2) return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 2, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 2, + wantOnChangeCalls: 3, }, { desc: "all text visible, moves cursor to the appending space", @@ -1611,10 +1611,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(3) return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 3, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 3, + wantOnChangeCalls: 3, }, { desc: "all text visible, moves cursor before the beginning of data", @@ -1630,10 +1630,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(-1) return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 0, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 0, + wantOnChangeCalls: 3, }, { desc: "all text visible, moves cursor after the appending space", @@ -1649,10 +1649,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(10) return nil }, - wantView: "abc", - wantContent: "abc", - wantCurIdx: 3, - mutations: 3, + wantView: "abc", + wantContent: "abc", + wantCurIdx: 3, + wantOnChangeCalls: 3, }, { desc: "moves cursor when there is no text", @@ -1687,10 +1687,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(0) return nil }, - wantView: "⇦cd⇨", - wantContent: "abcde", - wantCurIdx: 1, - mutations: 5, + wantView: "⇦cd⇨", + wantContent: "abcde", + wantCurIdx: 1, + wantOnChangeCalls: 5, }, { desc: "both ends hidden, moves cursor onto the first character", @@ -1714,10 +1714,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(1) return nil }, - wantView: "⇦cd⇨", - wantContent: "abcde", - wantCurIdx: 1, - mutations: 5, + wantView: "⇦cd⇨", + wantContent: "abcde", + wantCurIdx: 1, + wantOnChangeCalls: 5, }, { desc: "both ends hidden, moves cursor onto the right arrow", @@ -1740,10 +1740,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(3) return nil }, - wantView: "⇦cd⇨", - wantContent: "abcde", - wantCurIdx: 2, - mutations: 5, + wantView: "⇦cd⇨", + wantContent: "abcde", + wantCurIdx: 2, + wantOnChangeCalls: 5, }, { desc: "both ends hidden, moves cursor onto the last character", @@ -1766,10 +1766,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(2) return nil }, - wantView: "⇦cd⇨", - wantContent: "abcde", - wantCurIdx: 2, - mutations: 5, + wantView: "⇦cd⇨", + wantContent: "abcde", + wantCurIdx: 2, + wantOnChangeCalls: 5, }, { desc: "moves cursor onto the first cell containing a full-width rune", @@ -1792,10 +1792,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(4) return nil }, - wantView: "⇦⇦世界⇨", - wantContent: "你好世界你", - wantCurIdx: 4, - mutations: 5, + wantView: "⇦⇦世界⇨", + wantContent: "你好世界你", + wantCurIdx: 4, + wantOnChangeCalls: 5, }, { desc: "moves cursor onto the second cell containing a full-width rune", @@ -1818,10 +1818,10 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(5) return nil }, - wantView: "⇦⇦世界⇨", - wantContent: "你好世界你", - wantCurIdx: 4, - mutations: 5, + wantView: "⇦⇦世界⇨", + wantContent: "你好世界你", + wantCurIdx: 4, + wantOnChangeCalls: 5, }, { desc: "moves cursor onto the second right arrow", @@ -1844,20 +1844,19 @@ func TestFieldEditor(t *testing.T) { fe.cursorRelCell(1) return nil }, - wantView: "⇦⇦世界⇨", - wantContent: "你好世界你", - wantCurIdx: 2, - mutations: 5, + wantView: "⇦⇦世界⇨", + wantContent: "你好世界你", + wantCurIdx: 2, + wantOnChangeCalls: 5, }, } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { - fe := newFieldEditor() var changeCount int - fe.onChange = func(data string) { + fe := newFieldEditor(func(data string) { changeCount++ - } + }) if tc.ops != nil { if err := tc.ops(fe); err != nil { @@ -1882,8 +1881,8 @@ func TestFieldEditor(t *testing.T) { t.Errorf("content -> %q, want %q", gotContent, tc.wantContent) } - if tc.mutations != changeCount { - t.Errorf("mutation count -> %d, want %d", changeCount, tc.mutations) + if tc.wantOnChangeCalls != changeCount { + t.Errorf("unexpected number of onChange calls -> %d, want %d", changeCount, tc.wantOnChangeCalls) } }) } diff --git a/widgets/textinput/options.go b/widgets/textinput/options.go index 572365e..2ff1103 100644 --- a/widgets/textinput/options.go +++ b/widgets/textinput/options.go @@ -270,13 +270,14 @@ func OnSubmit(fn SubmitFn) Option { }) } -// ChangeFn if provided is called when the content of the text input field changes, -// the argument data contains all the text in the field. +// The argument to ChangeFn contains all the text in the field after the change. // // The callback function must be thread-safe as the keyboard event that // triggers the submission comes from a separate goroutine. type ChangeFn func(data string) +// OnChange sets a function that will be called when the content of the text input +// field changes. func OnChange(fn ChangeFn) Option { return option(func(opts *options) { opts.onChange = fn diff --git a/widgets/textinput/textinput.go b/widgets/textinput/textinput.go index 7efbe80..ee4c0d3 100644 --- a/widgets/textinput/textinput.go +++ b/widgets/textinput/textinput.go @@ -70,10 +70,9 @@ func New(opts ...Option) (*TextInput, error) { return nil, err } ti := &TextInput{ - editor: newFieldEditor(), + editor: newFieldEditor(opt.onChange), opts: opt, } - ti.editor.onChange = opt.onChange for _, r := range ti.opts.defaultText { ti.editor.insert(r) }