Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix crash with richtext that is too narrow #3111

Merged
merged 6 commits into from Jul 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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)

Expand Down
16 changes: 15 additions & 1 deletion widget/entry_test.go
Expand Up @@ -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)
Expand All @@ -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) {
Expand Down
30 changes: 25 additions & 5 deletions widget/richtext.go
Expand Up @@ -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
}

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down