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

Start fixing scrolling containing entries #1994

Merged
merged 2 commits into from Feb 26, 2021
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
92 changes: 76 additions & 16 deletions widget/entry.go
Expand Up @@ -163,8 +163,15 @@ func (e *Entry) CreateRenderer() fyne.WidgetRenderer {
line := canvas.NewRectangle(theme.ShadowColor())

e.content = &entryContent{entry: e}
e.scroll = widget.NewScroll(e.content)
objects := []fyne.CanvasObject{box, line, e.scroll}
e.scroll = &widget.Scroll{}
objects := []fyne.CanvasObject{box, line}
if e.Wrapping != fyne.TextWrapOff {
e.scroll.Content = e.content
objects = append(objects, e.scroll)
} else {
e.scroll.Hide()
objects = append(objects, e.content)
}
e.content.scroll = e.scroll

if e.Password && e.ActionItem == nil {
Expand Down Expand Up @@ -1083,17 +1090,33 @@ type entryRenderer struct {
func (r *entryRenderer) Destroy() {
}

func (r *entryRenderer) trailingInset() float32 {
xInset := float32(0)

if r.entry.ActionItem != nil {
xInset = theme.IconInlineSize() + 2*theme.Padding()
}

if r.entry.Validator != nil {
if r.entry.ActionItem == nil {
xInset = theme.IconInlineSize() + 2*theme.Padding()
} else {
xInset += theme.IconInlineSize() + theme.Padding()
}
}

return xInset
}

func (r *entryRenderer) Layout(size fyne.Size) {
r.line.Resize(fyne.NewSize(size.Width, theme.InputBorderSize()))
r.line.Move(fyne.NewPos(0, size.Height-theme.InputBorderSize()))
r.box.Resize(size.Subtract(fyne.NewSize(0, theme.InputBorderSize()*2)))
r.box.Move(fyne.NewPos(0, theme.InputBorderSize()))

actionIconSize := fyne.NewSize(0, 0)
xInset := float32(0)
if r.entry.ActionItem != nil {
actionIconSize = fyne.NewSize(theme.IconInlineSize(), theme.IconInlineSize())
xInset = theme.IconInlineSize() + 2*theme.Padding()

r.entry.ActionItem.Resize(actionIconSize)
r.entry.ActionItem.Move(fyne.NewPos(size.Width-actionIconSize.Width-2*theme.Padding(), theme.Padding()*2))
Expand All @@ -1108,25 +1131,28 @@ func (r *entryRenderer) Layout(size fyne.Size) {

if r.entry.ActionItem == nil {
r.entry.validationStatus.Move(fyne.NewPos(size.Width-validatorIconSize.Width-2*theme.Padding(), theme.Padding()*2))
xInset = theme.IconInlineSize() + 2*theme.Padding()
} else {
r.entry.validationStatus.Move(fyne.NewPos(size.Width-validatorIconSize.Width-actionIconSize.Width-3*theme.Padding(), theme.Padding()*2))
xInset += theme.IconInlineSize() + theme.Padding()
}
}

entrySize := size.Subtract(fyne.NewSize(xInset, theme.InputBorderSize()*2))
entrySize := size.Subtract(fyne.NewSize(r.trailingInset(), theme.InputBorderSize()*2))
entryPos := fyne.NewPos(0, theme.InputBorderSize())
r.scroll.Resize(entrySize)
r.scroll.Move(entryPos)
if r.entry.Wrapping == fyne.TextWrapOff {
r.entry.content.Resize(entrySize)
r.entry.content.Move(entryPos)
} else {
r.scroll.Resize(entrySize)
r.scroll.Move(entryPos)
}
}

// MinSize calculates the minimum size of an entry widget.
// This is based on the contained text with a standard amount of padding added.
// If MultiLine is true then we will reserve space for at leasts 3 lines
func (r *entryRenderer) MinSize() fyne.Size {
if r.scroll.Direction == widget.ScrollNone {
return r.scroll.MinSize().Add(fyne.NewSize(0, theme.InputBorderSize()*2))
return r.entry.content.MinSize().Add(fyne.NewSize(0, theme.InputBorderSize()*2))
}

minSize := r.entry.placeholderProvider().charMinSize().Add(fyne.NewSize(theme.Padding()*2, theme.Padding()*2))
Expand All @@ -1150,12 +1176,44 @@ func (r *entryRenderer) Objects() []fyne.CanvasObject {
func (r *entryRenderer) Refresh() {
r.entry.propertyLock.RLock()
provider := r.entry.textProvider()
content := r.entry.Text
text := r.entry.Text
content := r.entry.content
focused := r.entry.focused
size := r.entry.size
wrapping := r.entry.Wrapping
r.entry.propertyLock.RUnlock()

if content != string(provider.buffer) {
r.entry.SetText(content)
// correct our scroll wrappers if the wrap mode changed
entrySize := size.Subtract(fyne.NewSize(r.trailingInset(), theme.InputBorderSize()*2))
if wrapping == fyne.TextWrapOff && r.scroll.Content != nil {
r.scroll.Hide()
r.scroll.Content = nil
content.Move(fyne.NewPos(0, theme.InputBorderSize()))
content.Resize(entrySize)

for i, o := range r.objects {
if o == r.scroll {
r.objects[i] = content
break
}
}
} else if wrapping != fyne.TextWrapOff && r.scroll.Content == nil {
r.scroll.Content = content
content.Move(fyne.NewPos(0, 0))
r.scroll.Move(fyne.NewPos(0, theme.InputBorderSize()))
r.scroll.Resize(entrySize)
r.scroll.Show()

for i, o := range r.objects {
if o == content {
r.objects[i] = r.scroll
break
}
}
}

if text != string(provider.buffer) {
r.entry.SetText(text)
return
}

Expand Down Expand Up @@ -1193,7 +1251,7 @@ func (r *entryRenderer) Refresh() {
r.entry.validationStatus.Hide()
}

cache.Renderer(r.scroll.Content.(*entryContent)).Refresh()
cache.Renderer(r.entry.content).Refresh()
canvas.Refresh(r.entry.super())
}

Expand Down Expand Up @@ -1453,8 +1511,10 @@ func (r *entryContentRenderer) ensureCursorVisible() {
} else if cy2 >= offset.X+size.Height {
move.DY += cy2 - (offset.Y + size.Height)
}
r.content.scroll.Offset = r.content.scroll.Offset.Add(move)
r.content.scroll.Refresh()
if r.content.scroll.Content != nil {
r.content.scroll.Offset = r.content.scroll.Offset.Add(move)
r.content.scroll.Refresh()
}
}

func (r *entryContentRenderer) moveCursor() {
Expand Down
42 changes: 32 additions & 10 deletions widget/entry_test.go
Expand Up @@ -1392,20 +1392,19 @@ func TestEntry_TextWrap(t *testing.T) {
"single line WrapOff": {
want: "entry/wrap_single_line_off.xml",
},
// Disallowed - fallback to TextWrapOff
"single line Truncate": {
wrap: fyne.TextTruncate,
want: "entry/wrap_single_line_off.xml",
want: "entry/wrap_single_line_truncate.xml",
},
// Disallowed - fallback to TextWrapOff
// Disallowed - fallback to TextWrapTruncate (horizontal)
"single line WrapBreak": {
wrap: fyne.TextWrapBreak,
want: "entry/wrap_single_line_off.xml",
want: "entry/wrap_single_line_truncate.xml",
},
// Disallowed - fallback to TextWrapOff
// Disallowed - fallback to TextWrapTruncate (horizontal)
"single line WrapWord": {
wrap: fyne.TextWrapWord,
want: "entry/wrap_single_line_off.xml",
want: "entry/wrap_single_line_truncate.xml",
},
"multi line WrapOff": {
multiLine: true,
Expand All @@ -1415,7 +1414,7 @@ func TestEntry_TextWrap(t *testing.T) {
"multi line Truncate": {
multiLine: true,
wrap: fyne.TextTruncate,
want: "entry/wrap_multi_line_off.xml",
want: "entry/wrap_multi_line_truncate.xml",
},
"multi line WrapBreak": {
multiLine: true,
Expand Down Expand Up @@ -1445,6 +1444,25 @@ func TestEntry_TextWrap(t *testing.T) {
}
}

func TestEntry_TextWrap_Changed(t *testing.T) {
e, window := setupImageTest(t, false)
defer teardownImageTest(window)
c := window.Canvas()

c.Focus(e)
e.Wrapping = fyne.TextWrapOff
e.SetText("Testing Wrapping")
test.AssertRendersToMarkup(t, "entry/wrap_single_line_off.xml", c)

e.Wrapping = fyne.TextTruncate
e.Refresh()
test.AssertRendersToMarkup(t, "entry/wrap_single_line_truncate.xml", c)

e.Wrapping = fyne.TextWrapOff
e.Refresh()
test.AssertRendersToMarkup(t, "entry/wrap_single_line_off.xml", c)
}

func TestMultiLineEntry_MinSize(t *testing.T) {
entry := widget.NewEntry()
singleMin := entry.MinSize()
Expand Down Expand Up @@ -1567,8 +1585,7 @@ func TestPasswordEntry_Reveal(t *testing.T) {
// the Password field is set to true.
// In this case the action item will be set when the renderer is created.
t.Run("Entry with Password field", func(t *testing.T) {
entry := &widget.Entry{}
entry.Password = true
entry := &widget.Entry{Password: true, Wrapping: fyne.TextWrapWord}
entry.Refresh()
window := test.NewWindow(entry)
defer window.Close()
Expand Down Expand Up @@ -1657,7 +1674,12 @@ func checkNewlineIgnored(t *testing.T, entry *widget.Entry) {
func setupImageTest(t *testing.T, multiLine bool) (*widget.Entry, fyne.Window) {
test.NewApp()

entry := &widget.Entry{MultiLine: multiLine}
var entry *widget.Entry
if multiLine {
entry = &widget.Entry{MultiLine: true, Wrapping: fyne.TextWrapWord}
} else {
entry = &widget.Entry{Wrapping: fyne.TextWrapOff}
}
w := test.NewWindow(entry)
w.Resize(fyne.NewSize(150, 200))

Expand Down
1 change: 1 addition & 0 deletions widget/select_entry.go
Expand Up @@ -17,6 +17,7 @@ type SelectEntry struct {
func NewSelectEntry(options []string) *SelectEntry {
e := &SelectEntry{}
e.ExtendBaseWidget(e)
e.Wrapping = fyne.TextTruncate
e.options = options
return e
}
Expand Down
8 changes: 3 additions & 5 deletions widget/testdata/entry/disableable_disabled_custom_value.xml
Expand Up @@ -3,11 +3,9 @@
<widget pos="10,10" size="120x37" type="*widget.Entry">
<rectangle fillColor="rgba(102,102,102,255)" pos="0,2" size="120x33"/>
<rectangle fillColor="disabled" pos="0,35" size="120x2"/>
<widget pos="0,2" size="120x33" type="*widget.Scroll">
<widget size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text color="disabled" pos="8,6" size="112x21">Hello</text>
</widget>
<widget pos="0,2" size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text color="disabled" pos="8,6" size="112x21">Hello</text>
</widget>
</widget>
</widget>
Expand Down
14 changes: 6 additions & 8 deletions widget/testdata/entry/disableable_disabled_empty.xml
Expand Up @@ -3,14 +3,12 @@
<widget pos="10,10" size="120x37" type="*widget.Entry">
<rectangle fillColor="rgba(102,102,102,255)" pos="0,2" size="120x33"/>
<rectangle fillColor="disabled" pos="0,35" size="120x2"/>
<widget pos="0,2" size="120x33" type="*widget.Scroll">
<widget size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text color="disabled" pos="8,6" size="112x21"></text>
</widget>
<widget size="120x33" type="*widget.textProvider">
<text color="disabled" pos="8,6" size="112x21"></text>
</widget>
<widget pos="0,2" size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text color="disabled" pos="8,6" size="112x21"></text>
</widget>
<widget size="120x33" type="*widget.textProvider">
<text color="disabled" pos="8,6" size="112x21"></text>
</widget>
</widget>
</widget>
Expand Down
14 changes: 6 additions & 8 deletions widget/testdata/entry/disableable_disabled_placeholder.xml
Expand Up @@ -3,14 +3,12 @@
<widget pos="10,10" size="120x37" type="*widget.Entry">
<rectangle fillColor="rgba(102,102,102,255)" pos="0,2" size="120x33"/>
<rectangle fillColor="disabled" pos="0,35" size="120x2"/>
<widget pos="0,2" size="120x33" type="*widget.Scroll">
<widget size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text color="disabled" pos="8,6" size="112x21">Type!</text>
</widget>
<widget size="120x33" type="*widget.textProvider">
<text color="disabled" pos="8,6" size="112x21"></text>
</widget>
<widget pos="0,2" size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text color="disabled" pos="8,6" size="112x21">Type!</text>
</widget>
<widget size="120x33" type="*widget.textProvider">
<text color="disabled" pos="8,6" size="112x21"></text>
</widget>
</widget>
</widget>
Expand Down
8 changes: 3 additions & 5 deletions widget/testdata/entry/disableable_enabled_custom_value.xml
Expand Up @@ -3,11 +3,9 @@
<widget pos="10,10" size="120x37" type="*widget.Entry">
<rectangle fillColor="rgba(102,102,102,255)" pos="0,2" size="120x33"/>
<rectangle fillColor="shadow" pos="0,35" size="120x2"/>
<widget pos="0,2" size="120x33" type="*widget.Scroll">
<widget size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text pos="8,6" size="112x21">Hello</text>
</widget>
<widget pos="0,2" size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text pos="8,6" size="112x21">Hello</text>
</widget>
</widget>
</widget>
Expand Down
14 changes: 6 additions & 8 deletions widget/testdata/entry/disableable_enabled_empty.xml
Expand Up @@ -3,14 +3,12 @@
<widget pos="10,10" size="120x37" type="*widget.Entry">
<rectangle fillColor="rgba(102,102,102,255)" pos="0,2" size="120x33"/>
<rectangle fillColor="shadow" pos="0,35" size="120x2"/>
<widget pos="0,2" size="120x33" type="*widget.Scroll">
<widget size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text color="placeholder" pos="8,6" size="112x21"></text>
</widget>
<widget size="120x33" type="*widget.textProvider">
<text pos="8,6" size="112x21"></text>
</widget>
<widget pos="0,2" size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text color="placeholder" pos="8,6" size="112x21"></text>
</widget>
<widget size="120x33" type="*widget.textProvider">
<text pos="8,6" size="112x21"></text>
</widget>
</widget>
</widget>
Expand Down
14 changes: 6 additions & 8 deletions widget/testdata/entry/disableable_enabled_placeholder.xml
Expand Up @@ -3,14 +3,12 @@
<widget pos="10,10" size="120x37" type="*widget.Entry">
<rectangle fillColor="rgba(102,102,102,255)" pos="0,2" size="120x33"/>
<rectangle fillColor="shadow" pos="0,35" size="120x2"/>
<widget pos="0,2" size="120x33" type="*widget.Scroll">
<widget size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text color="placeholder" pos="8,6" size="112x21">Type!</text>
</widget>
<widget size="120x33" type="*widget.textProvider">
<text pos="8,6" size="112x21"></text>
</widget>
<widget pos="0,2" size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text color="placeholder" pos="8,6" size="112x21">Type!</text>
</widget>
<widget size="120x33" type="*widget.textProvider">
<text pos="8,6" size="112x21"></text>
</widget>
</widget>
</widget>
Expand Down
16 changes: 7 additions & 9 deletions widget/testdata/entry/focus_gained.xml
Expand Up @@ -3,16 +3,14 @@
<widget pos="10,10" size="120x37" type="*widget.Entry">
<rectangle fillColor="rgba(102,102,102,255)" pos="0,2" size="120x33"/>
<rectangle fillColor="primary" pos="0,35" size="120x2"/>
<widget pos="0,2" size="120x33" type="*widget.Scroll">
<widget size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text color="placeholder" pos="8,6" size="112x21"></text>
</widget>
<widget size="120x33" type="*widget.textProvider">
<text pos="8,6" size="112x21"></text>
</widget>
<rectangle fillColor="primary" pos="7,6" size="2x21"/>
<widget pos="0,2" size="120x33" type="*widget.entryContent">
<widget size="120x33" type="*widget.textProvider">
<text color="placeholder" pos="8,6" size="112x21"></text>
</widget>
<widget size="120x33" type="*widget.textProvider">
<text pos="8,6" size="112x21"></text>
</widget>
<rectangle fillColor="primary" pos="7,6" size="2x21"/>
</widget>
</widget>
</content>
Expand Down