From 4c67a3c44828c31cded69e86b9a6fce0ec35e617 Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Wed, 21 Aug 2024 23:43:05 -0700 Subject: [PATCH] Resize font to fit button label, add Text.SetAutoResize to allow fitting text within field --- button.go | 35 +++++++++++++++++++++++- go.mod | 10 +++---- go.sum | 20 +++++++------- text.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 121 insertions(+), 23 deletions(-) diff --git a/button.go b/button.go index 8c3a358..503eeeb 100644 --- a/button.go +++ b/button.go @@ -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. diff --git a/go.mod b/go.mod index a8df43e..b507b41 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 5bf4ed7..6fa63b2 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/text.go b/text.go index 6910a2a..97f474f 100644 --- a/text.go +++ b/text.go @@ -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.