Skip to content

Commit

Permalink
Add the capability to scroll in RichText, leaving space for great opt…
Browse files Browse the repository at this point in the history
…imisation

In the future this will simplify Entry and others
  • Loading branch information
andydotxyz committed Sep 12, 2021
1 parent 1669fce commit 3469491
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 6 deletions.
6 changes: 3 additions & 3 deletions cmd/fyne_demo/tutorials/widget.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ Normal **Bold** *Italic* [Link](https://fyne.io/) and some ` + "`Code`" + `.
This styled row should also wrap as expected, but only *when required*.
> An interesting quote here, most likely sharing some very interesting wisdom.`)
rich.Scroll = container.ScrollBoth

radioAlign := widget.NewRadioGroup([]string{"Text Alignment Leading", "Text Alignment Center", "Text Alignment Trailing"}, func(s string) {
var align fyne.TextAlign
Expand Down Expand Up @@ -234,12 +235,11 @@ This styled row should also wrap as expected, but only *when required*.
),
label,
hyperlink,
rich,
)

grid := makeTextGrid()
return fyne.NewContainerWithLayout(layout.NewBorderLayout(fixed, grid, nil, nil),
fixed, entryLoremIpsum, grid)
return container.NewBorder(fixed, grid, nil, nil,
container.NewGridWithRows(2, rich, entryLoremIpsum))
}

func makeInputTab(_ fyne.Window) fyne.CanvasObject {
Expand Down
58 changes: 55 additions & 3 deletions widget/richtext.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/internal/widget"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
)

Expand All @@ -24,9 +25,12 @@ type RichText struct {
BaseWidget
Segments []RichTextSegment
Wrapping fyne.TextWrap
Scroll widget.ScrollDirection

inset fyne.Size // this varies due to how the widget works (entry with scroller vs others with padding)
rowBounds []rowBoundary // cache for boundaries
scr *widget.Scroll
prop *canvas.Rectangle // used to apply text minsize to the scroller `scr`, if present - TODO improve #2464

visualCache map[RichTextSegment][]fyne.CanvasObject
cacheLock sync.Mutex
Expand All @@ -38,6 +42,7 @@ type RichText struct {
// Since: 2.1
func NewRichText(segments ...RichTextSegment) *RichText {
t := &RichText{Segments: segments}
t.Scroll = widget.ScrollNone
t.updateRowBounds()
return t
}
Expand All @@ -55,6 +60,12 @@ func NewRichTextWithText(text string) *RichText {

// CreateRenderer is a private method to Fyne which links this widget to its renderer
func (t *RichText) CreateRenderer() fyne.WidgetRenderer {
if t.scr == nil && t.Scroll != widget.ScrollNone {
t.prop = canvas.NewRectangle(color.Transparent)
t.scr = widget.NewScroll(&fyne.Container{Layout: layout.NewMaxLayout(), Objects: []fyne.CanvasObject{
t.prop, &fyne.Container{}}})
}

t.ExtendBaseWidget(t)
r := &textRenderer{obj: t}

Expand Down Expand Up @@ -374,7 +385,16 @@ func (t *RichText) updateRowBounds() {
if len(last.segments) == 1 {
begin = last.begin
}
text := string([]rune(textSeg.Text)[begin:last.end])
runes := []rune(textSeg.Text)
// check ranges - as we resize it can be wrong?
if begin > len(runes) {
begin = len(runes)
}
end := last.end
if end > len(runes) {
end = len(runes)
}
text := string(runes[begin:end])
lastWidth := fyne.MeasureText(text, textSeg.size(), textSeg.Style.TextStyle).Width
if len(retBounds) == 1 {
wrapWidth -= lastWidth
Expand Down Expand Up @@ -413,6 +433,10 @@ func (r *textRenderer) Layout(size fyne.Size) {
r.obj.propertyLock.RLock()
bounds := r.obj.rowBounds
objs := r.Objects()
if r.obj.scr != nil {
r.obj.scr.Resize(size)
objs = r.obj.scr.Content.(*fyne.Container).Objects[1].(*fyne.Container).Objects
}
r.obj.propertyLock.RUnlock()

left := theme.Padding()*2 - r.obj.inset.Width
Expand Down Expand Up @@ -473,7 +497,11 @@ func (r *textRenderer) MinSize() fyne.Size {
r.obj.propertyLock.RLock()
bounds := r.obj.rowBounds
wrap := r.obj.Wrapping
scroll := r.obj.Scroll
objs := r.Objects()
if r.obj.scr != nil {
objs = r.obj.scr.Content.(*fyne.Container).Objects[1].(*fyne.Container).Objects
}
r.obj.propertyLock.RUnlock()

height := float32(0)
Expand Down Expand Up @@ -512,13 +540,29 @@ func (r *textRenderer) MinSize() fyne.Size {
charMinSize := r.obj.charMinSize(false, fyne.TextStyle{})
height = charMinSize.Height
}
return fyne.NewSize(width, height).
min := fyne.NewSize(width, height).
Add(fyne.NewSize(theme.Padding()*4, theme.Padding()*4).Subtract(r.obj.inset).Subtract(r.obj.inset))

if r.obj.scr != nil {
r.obj.prop.SetMinSize(min)
}

switch scroll {
case widget.ScrollBoth:
return fyne.NewSize(32, 32)
case widget.ScrollHorizontalOnly:
return fyne.NewSize(32, min.Height)
case widget.ScrollVerticalOnly:
return fyne.NewSize(min.Width, 32)
default:
return min
}
}

func (r *textRenderer) Refresh() {
r.obj.propertyLock.RLock()
bounds := r.obj.rowBounds
scroll := r.obj.Scroll
r.obj.propertyLock.RUnlock()

var objs []fyne.CanvasObject
Expand Down Expand Up @@ -555,7 +599,15 @@ func (r *textRenderer) Refresh() {
}

r.obj.propertyLock.Lock()
r.SetObjects(objs)
if r.obj.scr != nil {
r.obj.scr.Content = &fyne.Container{Layout: layout.NewMaxLayout(), Objects: []fyne.CanvasObject{
r.obj.prop, &fyne.Container{Objects: objs}}}
r.obj.scr.Direction = scroll
r.SetObjects([]fyne.CanvasObject{r.obj.scr})
r.obj.scr.Refresh()
} else {
r.SetObjects(objs)
}
r.obj.propertyLock.Unlock()

r.Layout(r.obj.Size())
Expand Down
15 changes: 15 additions & 0 deletions widget/richtext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/internal/cache"
"fyne.io/fyne/v2/internal/widget"
"fyne.io/fyne/v2/test"
"fyne.io/fyne/v2/theme"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -97,6 +98,20 @@ func TestText_RowLength(t *testing.T) {
assert.Equal(t, 5, rl)
}

func TestText_Scroll(t *testing.T) {
text1 := NewRichTextWithText("test1\ntest2")
text2 := NewRichTextWithText("test1\ntest2")
text2.Scroll = widget.ScrollBoth

assert.Less(t, text2.MinSize().Width, text1.MinSize().Width)
assert.Less(t, text2.MinSize().Height, text1.MinSize().Height)

text3 := NewRichTextWithText("test1\ntest2")
text3.Scroll = widget.ScrollVerticalOnly
assert.Equal(t, text3.MinSize().Width, text1.MinSize().Width)
assert.Less(t, text3.MinSize().Height, text1.MinSize().Height)
}

func TestText_InsertAt(t *testing.T) {
type fields struct {
buffer string
Expand Down

0 comments on commit 3469491

Please sign in to comment.