Skip to content

Commit

Permalink
Merge branch 'feature/multiline' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
andydotxyz committed Sep 26, 2018
2 parents f8512dd + 1ec27f9 commit 34d72c5
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 31 deletions.
2 changes: 1 addition & 1 deletion examples/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func welcome(app fyne.App) {
W.NewGroup("Demos", []fyne.CanvasObject{
appButton(app, "Canvas", canvasApp),
appButton(app, "Layout", layoutApp),
&W.Entry{},
&W.Entry{Text: "Entry"},
&W.Check{Text: "Check", OnChanged: func(on bool) { fmt.Println("checked", on) }},
}...),

Expand Down
28 changes: 17 additions & 11 deletions widget/entry.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
package widget

import "fmt"
import "log"

import "github.com/fyne-io/fyne"
import "github.com/fyne-io/fyne/canvas"
import "github.com/fyne-io/fyne/theme"

type entryRenderer struct {
label *canvas.Text
label *Label
bg, box *canvas.Rectangle

objects []fyne.CanvasObject
entry *Entry
}

func emptyTextMinSize(style fyne.TextStyle) fyne.Size {
return fyne.GetDriver().RenderedTextSize("M", theme.TextSize(), style)
}

// MinSize calculates the minimum size of an entry widget.
// This is based on the contained text with a standard amount of padding added.
func (e *entryRenderer) MinSize() fyne.Size {
var textSize fyne.Size
if e.label.Text == "" {
textSize = fyne.GetDriver().RenderedTextSize("M", e.label.TextSize, e.label.TextStyle)
textSize = emptyTextMinSize(e.label.TextStyle)
} else {
textSize = e.label.MinSize()
}
Expand All @@ -40,13 +45,13 @@ func (e *entryRenderer) Layout(size fyne.Size) {

// ApplyTheme is called when the Entry may need to update it's look.
func (e *entryRenderer) ApplyTheme() {
e.label.Color = theme.TextColor()
e.label.ApplyTheme()
e.box.FillColor = theme.BackgroundColor()
e.Refresh()
}

func (e *entryRenderer) Refresh() {
e.label.Text = e.entry.Text
e.label.SetText(e.entry.Text)

if e.entry.focused {
e.bg.FillColor = theme.FocusColor()
Expand All @@ -61,7 +66,7 @@ func (e *entryRenderer) Objects() []fyne.CanvasObject {
return e.objects
}

// Entry widget allows simple text to be input when focussed.
// Entry widget allows simple text to be input when focused.
type Entry struct {
baseWidget

Expand Down Expand Up @@ -101,7 +106,7 @@ func (e *Entry) Focused() bool {
return e.focused
}

// OnKeyDown receives key input events when the Entry widget is focussed.
// OnKeyDown receives key input events when the Entry widget is focused.
func (e *Entry) OnKeyDown(key *fyne.KeyEvent) {
if key.Name == "BackSpace" {
if len(e.Text) == 0 {
Expand All @@ -112,16 +117,17 @@ func (e *Entry) OnKeyDown(key *fyne.KeyEvent) {
substr := string(runes[0 : len(runes)-1])

e.SetText(substr)
return
}

if key.String != "" {
} else if key.Name == "Return" {
e.SetText(fmt.Sprintf("%s\n", e.Text))
} else if key.String != "" {
e.SetText(fmt.Sprintf("%s%s", e.Text, key.String))
} else {
log.Println("Unhandled key press", key.String)
}
}

func (e *Entry) createRenderer() fyne.WidgetRenderer {
text := canvas.NewText(e.Text, theme.TextColor())
text := NewLabel(e.Text)
bg := canvas.NewRectangle(theme.ButtonColor())
box := canvas.NewRectangle(theme.BackgroundColor())

Expand Down
113 changes: 96 additions & 17 deletions widget/label.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package widget

import "bufio"
import "strings"

import "github.com/fyne-io/fyne"
import "github.com/fyne-io/fyne/canvas"
import "github.com/fyne-io/fyne/theme"
Expand All @@ -8,19 +11,88 @@ type labelRenderer struct {
objects []fyne.CanvasObject

background *canvas.Rectangle
text *canvas.Text
texts []*canvas.Text

label *Label
lines int
}

func (l *labelRenderer) parseText(text string) []string {
if !strings.Contains(text, "\n") {
return []string{text}
}

var texts []string
s := bufio.NewScanner(strings.NewReader(text))
for s.Scan() {
texts = append(texts, s.Text())
}
// this checks if Scan() ended on a blank line
if string(text[len(text)-1]) == "\n" {
texts = append(texts, "")
}

return texts
}

func (l *labelRenderer) updateTexts(strings []string) {
l.lines = len(strings)
count := len(l.texts)
refresh := false

for i, str := range strings {
if i >= count {
text := canvas.NewText("", theme.TextColor())
l.texts = append(l.texts, text)
l.objects = append(l.objects, text)

refresh = true
}
l.texts[i].Text = str
}

for i := l.lines; i < len(l.texts); i++ {
l.texts[i].Text = ""
refresh = true
}

if refresh {
// TODO invalidate container size (to shrink)
l.Refresh()
}
}

// MinSize calculates the minimum size of a label.
// This is based on the contained text with a standard amount of padding added.
func (l *labelRenderer) MinSize() fyne.Size {
return l.text.MinSize().Add(fyne.NewSize(theme.Padding()*2, theme.Padding()*2))
height := 0
width := 0
for i := 0; i < l.lines; i++ {
min := l.texts[i].MinSize()
if l.texts[i].Text == "" {
min = emptyTextMinSize(l.label.TextStyle)
}
height += min.Height
width = fyne.Max(width, min.Width)
}

return fyne.NewSize(width, height).Add(fyne.NewSize(theme.Padding()*2, theme.Padding()*2))
}

func (l *labelRenderer) Layout(size fyne.Size) {
l.text.Resize(size)
yPos := theme.Padding()
lineHeight := size.Height - theme.Padding()*2
if len(l.texts) > 1 {
lineHeight = lineHeight / l.lines
}
lineSize := fyne.NewSize(size.Width, lineHeight)
for i := 0; i < l.lines; i++ {
text := l.texts[i]
text.Resize(lineSize)
text.Move(fyne.NewPos(theme.Padding(), yPos))
yPos += lineHeight
}

l.background.Resize(size)
}

Expand All @@ -32,13 +104,16 @@ func (l *labelRenderer) Objects() []fyne.CanvasObject {
func (l *labelRenderer) ApplyTheme() {
l.background.FillColor = theme.BackgroundColor()

l.text.Color = theme.TextColor()
for _, text := range l.texts {
text.Color = theme.TextColor()
}
}

func (l *labelRenderer) Refresh() {
l.text.Alignment = l.label.Alignment
l.text.TextStyle = l.label.TextStyle
l.text.Text = l.label.Text
for _, text := range l.texts {
text.Alignment = l.label.Alignment
text.TextStyle = l.label.TextStyle
}

fyne.RefreshObject(l.label)
}
Expand All @@ -56,21 +131,25 @@ type Label struct {
func (l *Label) SetText(text string) {
l.Text = text

render := l.Renderer().(*labelRenderer)
for _, obj := range render.texts {
obj.Text = ""
}

render.updateTexts(render.parseText(l.Text))

l.Renderer().Refresh()
}

func (l *Label) createRenderer() fyne.WidgetRenderer {
obj := canvas.NewText(l.Text, theme.TextColor())
obj.Alignment = l.Alignment
obj.TextStyle = l.TextStyle
bg := canvas.NewRectangle(theme.ButtonColor())

objects := []fyne.CanvasObject{
bg,
obj,
}
render := &labelRenderer{label: l}

render.background = canvas.NewRectangle(theme.ButtonColor())
render.texts = []*canvas.Text{}
render.objects = []fyne.CanvasObject{render.background}
render.updateTexts(render.parseText(l.Text))

return &labelRenderer{objects, bg, obj, l}
return render
}

// Renderer is a private method to Fyne which links this widget to it's renderer
Expand Down
21 changes: 19 additions & 2 deletions widget/label_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import "github.com/fyne-io/fyne"
import _ "github.com/fyne-io/fyne/test"
import "github.com/fyne-io/fyne/theme"

func TestLabelSize(t *testing.T) {
func TestLabel_MinSize(t *testing.T) {
label := NewLabel("Test")
min := label.MinSize()

Expand All @@ -21,5 +21,22 @@ func TestLabelSize(t *testing.T) {
func TestLabel_Alignment(t *testing.T) {
label := &Label{Text: "Test", Alignment: fyne.TextAlignTrailing}

assert.Equal(t, fyne.TextAlignTrailing, label.Renderer().(*labelRenderer).text.Alignment)
assert.Equal(t, fyne.TextAlignTrailing, label.Renderer().(*labelRenderer).texts[0].Alignment)
}

func TestText_MinSize_Multiline(t *testing.T) {
text := NewLabel("Break")
min := text.MinSize()

text = NewLabel("Bre\nak")
min2 := text.MinSize()
assert.True(t, min2.Width < min.Width)
assert.True(t, min2.Height > min.Height)

yPos := -1
for _, text := range text.Renderer().(*labelRenderer).texts {
assert.True(t, text.CurrentSize().Height < min2.Height)
assert.True(t, text.CurrentPosition().Y > yPos)
yPos = text.CurrentPosition().Y
}
}

0 comments on commit 34d72c5

Please sign in to comment.