diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c52aac0ae..480b014801 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ More detailed release notes can be found on the [releases page](https://github.c ### Fixed * Regression: Preferences are not parsed at program start (#3125) +* Wrappable RichText in a Split container causes crash (#3003, #2961) * meta.Version is always 1.0.0 on android & ios (#3109) * Iphone / IOS incorrect screen size / incorrect click coordinates (#3122) diff --git a/widget/entry_test.go b/widget/entry_test.go index bbe4cf4ecd..0b1c986677 100644 --- a/widget/entry_test.go +++ b/widget/entry_test.go @@ -533,7 +533,6 @@ func TestEntry_OnKeyDown_Backspace(t *testing.T) { assert.Equal(t, 1, entry.CursorColumn) entry = widget.NewMultiLineEntry() - entry.Wrapping = fyne.TextWrapWord entry.SetText("Line\n2b\n") down := &fyne.KeyEvent{Name: fyne.KeyDown} entry.TypedKey(down) @@ -545,6 +544,21 @@ func TestEntry_OnKeyDown_Backspace(t *testing.T) { assert.Equal(t, "Line\nb\n", entry.Text) assert.Equal(t, 1, entry.CursorRow) assert.Equal(t, 0, entry.CursorColumn) + + entry.CursorRow = 0 + entry.CursorColumn = 0 + entry.Wrapping = fyne.TextWrapWord + entry.SetText("Line 2b") + entry.Resize(fyne.NewSize(50, 50)) + entry.TypedKey(down) + entry.TypedKey(right) + assert.Equal(t, 1, entry.CursorRow) + assert.Equal(t, 1, entry.CursorColumn) + + entry.TypedKey(backspace) + assert.Equal(t, "Line b", entry.Text) + assert.Equal(t, 1, entry.CursorRow) + assert.Equal(t, 0, entry.CursorColumn) } func TestEntry_OnKeyDown_BackspaceBeyondText(t *testing.T) { diff --git a/widget/richtext.go b/widget/richtext.go index 1ff9e1261d..47ea3cb9bb 100644 --- a/widget/richtext.go +++ b/widget/richtext.go @@ -756,7 +756,7 @@ func findSpaceIndex(text []rune, fallback int) int { // lineBounds returns a slice containing the boundary metadata of each line with the given wrapping applied. func lineBounds(seg *TextSegment, wrap fyne.TextWrap, firstWidth, maxWidth float32, measurer func([]rune) float32) []rowBoundary { lines := splitLines(seg) - if maxWidth <= 0 || wrap == fyne.TextWrapOff { + if wrap == fyne.TextWrapOff { return lines } @@ -791,13 +791,21 @@ func lineBounds(seg *TextSegment, wrap fyne.TextWrap, firstWidth, maxWidth float high = l.end measureWidth = maxWidth } else { - high = binarySearch(checker, low, high) + newHigh := binarySearch(checker, low, high) + if newHigh <= low { + bounds = append(bounds, rowBoundary{[]RichTextSegment{seg}, reuse, low, low + 1}) + reuse++ + low++ + } else { + high = newHigh + } } } case fyne.TextWrapWord: for low < high { sub := text[low:high] - if measurer(sub) <= measureWidth { + subWidth := measurer(sub) + if subWidth <= measureWidth { bounds = append(bounds, rowBoundary{[]RichTextSegment{seg}, reuse, low, high}) reuse++ low = high @@ -810,8 +818,20 @@ func lineBounds(seg *TextSegment, wrap fyne.TextWrap, firstWidth, maxWidth float oldHigh := high last := low + len(sub) - 1 fallback := binarySearch(checker, low, last) - low - high = low + findSpaceIndex(sub, fallback) - if high == fallback && measurer(sub) <= maxWidth { // add a newline as there is more space on next + + if fallback < 1 { // even a character won't fit + bounds = append(bounds, rowBoundary{[]RichTextSegment{seg}, reuse, low, low + 1}) + low++ + high = low + 1 + reuse++ + + if high > l.end { + return bounds + } + } else { + high = low + findSpaceIndex(sub, fallback) + } + if high == fallback && subWidth <= maxWidth { // add a newline as there is more space on next bounds = append(bounds, rowBoundary{[]RichTextSegment{seg}, reuse, low, low}) reuse++ high = oldHigh