Resize font to fit button label, add Text.SetAutoResize to allow fitting text within field

This commit is contained in:
Trevor Slocum 2024-08-21 23:43:05 -07:00
parent c1f1fe1adb
commit 4c67a3c448
4 changed files with 121 additions and 23 deletions

View file

@ -6,6 +6,7 @@ import (
"code.rocket9labs.com/tslocum/etk/messeji"
"github.com/hajimehoshi/ebiten/v2"
"golang.org/x/image/font"
"golang.org/x/image/font/sfnt"
)
@ -13,6 +14,9 @@ import (
type Button struct {
*Box
field *messeji.TextField
textFont *sfnt.Font
textSize int
textAutoSize int
borderSize int
borderTop color.RGBA
borderRight color.RGBA
@ -38,6 +42,8 @@ func NewButton(label string, onSelected func() error) *Button {
b := &Button{
Box: NewBox(),
field: f,
textFont: Style.TextFont,
textSize: Scale(Style.TextSize),
onSelected: onSelected,
borderSize: Scale(Style.BorderSize),
borderTop: Style.BorderColorTop,
@ -46,6 +52,7 @@ func NewButton(label string, onSelected func() error) *Button {
borderLeft: Style.BorderColorLeft,
}
b.SetBackground(Style.ButtonBgColor)
b.resizeFont()
return b
}
@ -54,6 +61,7 @@ func (b *Button) SetRect(r image.Rectangle) {
b.Box.rect = r
b.field.SetRect(r)
b.resizeFont()
for _, w := range b.children {
w.SetRect(r)
@ -93,6 +101,7 @@ func (b *Button) SetText(text string) {
defer b.Unlock()
b.field.SetText(text)
b.resizeFont()
}
// SetFont sets the font and text size of button label. Scaling is not applied.
@ -100,7 +109,31 @@ func (b *Button) SetFont(fnt *sfnt.Font, size int) {
b.Lock()
defer b.Unlock()
b.field.SetFont(FontFace(fnt, size), fontMutex)
b.textFont, b.textSize = fnt, size
b.resizeFont()
}
func (b *Button) resizeFont() {
w := b.rect.Dx() - b.field.Padding()*2
if w == 0 {
b.textAutoSize = b.textSize
return
}
var autoSize int
var ff font.Face
for autoSize = b.textSize; autoSize > 0; autoSize-- {
ff = FontFace(b.textFont, autoSize)
if BoundString(ff, b.field.Text()).Dx() <= w {
break
}
}
if b.textAutoSize == autoSize {
return
}
b.field.SetFont(ff, fontMutex)
b.textAutoSize = autoSize
}
// SetHorizontal sets the horizontal alignment of the button label.

10
go.mod
View file

@ -5,16 +5,16 @@ go 1.18
require (
github.com/hajimehoshi/ebiten/v2 v2.7.8
github.com/llgcode/draw2d v0.0.0-20240627062922-0ed1ff131195
golang.org/x/image v0.18.0
golang.org/x/image v0.19.0
)
require (
github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 // indirect
github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc // indirect
github.com/ebitengine/hideconsole v1.0.0 // indirect
github.com/ebitengine/purego v0.7.1 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/jezek/xgb v1.1.1 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect
)

20
go.sum
View file

@ -1,5 +1,5 @@
github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 h1:48bCqKTuD7Z0UovDfvpCn7wZ0GUZ+yosIteNDthn3FU=
github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895/go.mod h1:XZdLv05c5hOZm3fM2NlJ92FyEZjnslcMcNRrhxs8+8M=
github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc h1:76TYsaP1F48tiQRlrr71NsbfxBcFM9/8bEHS9/JbsQg=
github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc/go.mod h1:RM/c3pvru6dRqgGEW7RCTb6czFXYAa3MxbXu3u8/dcI=
github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE=
github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A=
github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA=
@ -14,11 +14,11 @@ github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFl
github.com/llgcode/draw2d v0.0.0-20240627062922-0ed1ff131195 h1:Vdz2cBh5Fw2MYHWi3ED2PraDQaWEUhNCr1XFHrP4N5A=
github.com/llgcode/draw2d v0.0.0-20240627062922-0ed1ff131195/go.mod h1:1Vk0LDW6jG5cGc2D9RQUxHaE0vYhTvIwSo9mOL6K4/U=
github.com/llgcode/ps v0.0.0-20210114104736-f4b0c5d1e02e h1:ZAvbj5hI/G/EbAYAcj4yCXUNiFKefEhH0qfImDDD0/8=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ=
golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=

79
text.go
View file

@ -6,14 +6,19 @@ import (
"code.rocket9labs.com/tslocum/etk/messeji"
"github.com/hajimehoshi/ebiten/v2"
"golang.org/x/image/font"
"golang.org/x/image/font/sfnt"
)
// Text is a text display widget.
type Text struct {
*Box
field *messeji.TextField
children []Widget
field *messeji.TextField
textFont *sfnt.Font
textSize int
textResize bool
textAutoSize int
children []Widget
}
// NewText returns a new Text widget.
@ -23,10 +28,14 @@ func NewText(text string) *Text {
f.SetForegroundColor(Style.TextColorLight)
f.SetHandleKeyboard(true)
return &Text{
Box: NewBox(),
field: f,
t := &Text{
Box: NewBox(),
field: f,
textFont: Style.TextFont,
textSize: Scale(Style.TextSize),
}
t.resizeFont()
return t
}
// SetRect sets the position and size of the widget.
@ -36,6 +45,7 @@ func (t *Text) SetRect(r image.Rectangle) {
t.rect = r
t.field.SetRect(r)
t.resizeFont()
}
// Foreground return the color of the text within the field.
@ -118,7 +128,12 @@ func (t *Text) Write(p []byte) (n int, err error) {
t.Lock()
defer t.Unlock()
return t.field.Write(p)
n, err = t.field.Write(p)
if err != nil {
return n, err
}
t.resizeFont()
return n, err
}
// Text returns the content of the text buffer.
@ -135,6 +150,45 @@ func (t *Text) SetText(text string) {
defer t.Unlock()
t.field.SetText(text)
t.resizeFont()
}
func (t *Text) resizeFont() {
if !t.textResize {
if t.textAutoSize == t.textSize {
return
}
t.textAutoSize = t.textSize
ff := FontFace(t.textFont, t.textSize)
t.field.SetFont(ff, fontMutex)
return
}
w := t.rect.Dx() - t.field.Padding()*2
if w == 0 {
if t.textAutoSize == t.textSize {
return
}
t.textAutoSize = t.textSize
ff := FontFace(t.textFont, t.textSize)
t.field.SetFont(ff, fontMutex)
return
}
var autoSize int
var ff font.Face
for autoSize = t.textSize; autoSize > 0; autoSize-- {
ff = FontFace(t.textFont, autoSize)
if BoundString(ff, t.field.Text()).Dx() <= w {
break
}
}
if t.textAutoSize == autoSize {
return
}
t.field.SetFont(ff, fontMutex)
t.textAutoSize = autoSize
}
// SetScrollBarVisible sets whether the scroll bar is visible on the screen.
@ -159,7 +213,18 @@ func (t *Text) SetFont(fnt *sfnt.Font, size int) {
t.Lock()
defer t.Unlock()
t.field.SetFont(FontFace(fnt, size), fontMutex)
t.textFont, t.textSize = fnt, size
t.resizeFont()
}
// SetAutoResize sets whether the font is automatically scaled down when it is
// too large to fit the entire text buffer on one line.
func (t *Text) SetAutoResize(resize bool) {
t.Lock()
defer t.Unlock()
t.textResize = resize
t.resizeFont()
}
// Padding returns the amount of padding around the text within the field.