Use text/v2 to draw text

This commit is contained in:
Trevor Slocum 2024-11-14 23:24:12 -08:00
parent eba7ce7e2f
commit 746cdb2994
17 changed files with 147 additions and 179 deletions

View file

@ -6,8 +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"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
// Button is a clickable button.
@ -15,7 +14,7 @@ type Button struct {
*Box
field *messeji.TextField
btnBackground color.RGBA
textFont *sfnt.Font
textFont *text.GoTextFaceSource
textSize int
textAutoSize int
borderSize int
@ -124,7 +123,7 @@ func (b *Button) SetText(text string) {
}
// SetFont sets the font and text size of button label. Scaling is not applied.
func (b *Button) SetFont(fnt *sfnt.Font, size int) {
func (b *Button) SetFont(fnt *text.GoTextFaceSource, size int) {
b.Lock()
defer b.Unlock()
@ -145,7 +144,7 @@ func (b *Button) resizeFont() {
}
var autoSize int
var ff font.Face
var ff *text.GoTextFace
for autoSize = b.textSize; autoSize > 0; autoSize-- {
ff = FontFace(b.textFont, autoSize)
bounds := BoundString(ff, b.field.Text())

View file

@ -3,13 +3,12 @@
package main
import (
"log"
"bytes"
"code.rocket9labs.com/tslocum/etk"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
"golang.org/x/image/font/opentype"
"golang.org/x/image/font/sfnt"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
type game struct {
@ -37,10 +36,10 @@ func (g *game) Draw(screen *ebiten.Image) {
}
}
func defaultFont() *sfnt.Font {
f, err := opentype.Parse(fonts.MPlus1pRegular_ttf)
func defaultFont() *text.GoTextFaceSource {
source, err := text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf))
if err != nil {
log.Fatal(err)
panic(err)
}
return f
return source
}

36
game.go
View file

@ -5,7 +5,6 @@ import (
"image"
"image/color"
"io"
"log"
"math"
"sync"
"time"
@ -13,9 +12,8 @@ import (
"code.rocket9labs.com/tslocum/etk/messeji"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/hajimehoshi/ebiten/v2/text/v2"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
"golang.org/x/image/font/sfnt"
"golang.org/x/image/math/fixed"
)
@ -117,29 +115,11 @@ var (
)
// FontFace returns a face for the provided font and size. Scaling is not applied.
func FontFace(fnt *sfnt.Font, size int) font.Face {
id := fmt.Sprintf("%p/%d", fnt, size)
fontCacheLock.Lock()
defer fontCacheLock.Unlock()
f := fontCache[id]
if f != nil {
return f
func FontFace(source *text.GoTextFaceSource, size int) *text.GoTextFace {
return &text.GoTextFace{
Source: source,
Size: float64(size),
}
const dpi = 72
f, err := opentype.NewFace(fnt, &opentype.FaceOptions{
Size: float64(size),
DPI: dpi,
Hinting: font.HintingFull,
})
if err != nil {
log.Fatal(err)
}
fontCache[id] = f
return f
}
// SetRoot sets the root widget. The root widget and all of its children will
@ -177,12 +157,12 @@ func int26ToRect(r fixed.Rectangle26_6) image.Rectangle {
}
// BoundString returns the bounds of the provided string.
func BoundString(f font.Face, s string) image.Rectangle {
func BoundString(f *text.GoTextFace, s string) image.Rectangle {
fontMutex.Lock()
defer fontMutex.Unlock()
bounds, _ := font.BoundString(f, s)
return int26ToRect(bounds)
w, h := text.Measure(s, f, 0)
return image.Rect(0, 0, int(w), int(h))
}
// SetDebug sets whether debug information is drawn on screen. When enabled,

11
go.mod
View file

@ -6,20 +6,21 @@ toolchain go1.23.0
require (
code.rocket9labs.com/tslocum/clipboard v0.0.0-20241012025701-2c0fb515daab
github.com/hajimehoshi/ebiten/v2 v2.8.3
github.com/hajimehoshi/ebiten/v2 v2.8.4
github.com/llgcode/draw2d v0.0.0-20240627062922-0ed1ff131195
golang.org/x/image v0.21.0
golang.org/x/image v0.22.0
)
require (
github.com/ebitengine/gomobile v0.0.0-20241016134836-cc2e38a7c0ee // indirect
github.com/ebitengine/hideconsole v1.0.0 // indirect
github.com/ebitengine/purego v0.8.1 // indirect
github.com/go-text/typesetting v0.2.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/jezek/xgb v1.1.1 // indirect
golang.org/x/exp/shiny v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/mobile v0.0.0-20241016134751-7ff83004ec2c // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
)

24
go.sum
View file

@ -6,12 +6,16 @@ github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj
github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A=
github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/go-text/typesetting v0.2.0 h1:fbzsgbmk04KiWtE+c3ZD4W2nmCRzBqrqQOvYlwAOdho=
github.com/go-text/typesetting v0.2.0/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I=
github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY=
github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/hajimehoshi/bitmapfont/v3 v3.2.0 h1:0DISQM/rseKIJhdF29AkhvdzIULqNIIlXAGWit4ez1Q=
github.com/hajimehoshi/bitmapfont/v3 v3.2.0/go.mod h1:8gLqGatKVu0pwcNCJguW3Igg9WQqVXF0zg/RvrGQWyg=
github.com/hajimehoshi/ebiten/v2 v2.8.3 h1:AKHqj3QbQMzNEhK33MMJeRwXm9UzftrUUo6AWwFV258=
github.com/hajimehoshi/ebiten/v2 v2.8.3/go.mod h1:SXx/whkvpfsavGo6lvZykprerakl+8Uo1X8d2U5aAnA=
github.com/hajimehoshi/ebiten/v2 v2.8.4 h1:BzXkcyYX046SRZFkzF2KaCaHiBjwCaufUPCAOK59JSw=
github.com/hajimehoshi/ebiten/v2 v2.8.4/go.mod h1:SXx/whkvpfsavGo6lvZykprerakl+8Uo1X8d2U5aAnA=
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
github.com/llgcode/draw2d v0.0.0-20240627062922-0ed1ff131195 h1:Vdz2cBh5Fw2MYHWi3ED2PraDQaWEUhNCr1XFHrP4N5A=
@ -22,13 +26,13 @@ github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
golang.org/x/exp/shiny v0.0.0-20241009180824-f66d83c29e7c h1:jTMrjjZRcSH/BDxWhXCP6OWsfVgmnwI7J+F4/nyVXaU=
golang.org/x/exp/shiny v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g=
golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4=
golang.org/x/mobile v0.0.0-20241016134751-7ff83004ec2c h1:zuNS/LWsEpPTLfrmBkis6Xofw3nieAqB4hYLn8+uswk=
golang.org/x/mobile v0.0.0-20241016134751-7ff83004ec2c/go.mod h1:snk1Mn2ZpdKCt90JPEsDh4sL3ReK520U2t0d7RHBnSU=
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.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=

View file

@ -6,7 +6,7 @@ import (
"code.rocket9labs.com/tslocum/etk/messeji"
"github.com/hajimehoshi/ebiten/v2"
"golang.org/x/image/font/sfnt"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
// Input is a text input widget. The Input widget is simply a Text widget that
@ -187,7 +187,7 @@ func (i *Input) SetAutoHideScrollBar(autoHide bool) {
}
// SetFont sets the font and text size of the field. Scaling is not applied.
func (t *Input) SetFont(fnt *sfnt.Font, size int) {
func (t *Input) SetFont(fnt *text.GoTextFaceSource, size int) {
t.Lock()
defer t.Unlock()

View file

@ -3,16 +3,15 @@
package game
import (
"bytes"
"fmt"
"log"
"code.rocket9labs.com/tslocum/etk/kibodo"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"golang.org/x/image/font/opentype"
"golang.org/x/image/font/sfnt"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
type game struct {
@ -121,10 +120,10 @@ func (g *game) Draw(screen *ebiten.Image) {
screen.DrawImage(g.buffer, g.op)
}
func defaultFont() *sfnt.Font {
f, err := opentype.Parse(fonts.MPlus1pRegular_ttf)
func defaultFont() *text.GoTextFaceSource {
source, err := text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf))
if err != nil {
log.Fatal(err)
panic(err)
}
return f
return source
}

View file

@ -3,15 +3,11 @@ package kibodo
import (
"image"
"image/color"
"log"
"time"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/hajimehoshi/ebiten/v2/text"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
"golang.org/x/image/font/sfnt"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
// Keyboard is an on-screen keyboard widget.
@ -50,7 +46,7 @@ type Keyboard struct {
hideShortcuts []ebiten.Key
labelFont font.Face
labelFont *text.GoTextFace
lineHeight int
lineOffset int
@ -62,12 +58,7 @@ type Keyboard struct {
}
// NewKeyboard returns a new Keyboard widget.
func NewKeyboard(f *sfnt.Font) *Keyboard {
ff, err := fontFace(f, 64)
if err != nil {
log.Fatal(err)
}
func NewKeyboard(f *text.GoTextFaceSource) *Keyboard {
k := &Keyboard{
alpha: 1.0,
op: &ebiten.DrawImageOptions{
@ -80,7 +71,7 @@ func NewKeyboard(f *sfnt.Font) *Keyboard {
backgroundColor: color.RGBA{0, 0, 0, 255},
holdTouchID: -1,
hideShortcuts: []ebiten.Key{ebiten.KeyEscape},
labelFont: ff,
labelFont: fontFace(f, 64),
backspaceDelay: 500 * time.Millisecond,
backspaceRepeat: 75 * time.Millisecond,
}
@ -88,13 +79,11 @@ func NewKeyboard(f *sfnt.Font) *Keyboard {
return k
}
func fontFace(f *sfnt.Font, size float64) (font.Face, error) {
const dpi = 72 // TODO
return opentype.NewFace(f, &opentype.FaceOptions{
Size: size,
DPI: dpi,
Hinting: font.HintingFull,
})
func fontFace(source *text.GoTextFaceSource, size float64) *text.GoTextFace {
return &text.GoTextFace{
Source: source,
Size: size,
}
}
// SetRect sets the position and size of the widget.
@ -156,7 +145,7 @@ func (k *Keyboard) SetShowExtended(show bool) {
}
// SetLabelFont sets the key label font.
func (k *Keyboard) SetLabelFont(face font.Face) {
func (k *Keyboard) SetLabelFont(face *text.GoTextFace) {
k.labelFont = face
k.fontUpdated()
@ -165,8 +154,11 @@ func (k *Keyboard) SetLabelFont(face font.Face) {
func (k *Keyboard) fontUpdated() {
m := k.labelFont.Metrics()
k.lineHeight = m.Height.Round()
k.lineOffset = m.Ascent.Round()
k.lineHeight = int(m.HAscent + m.HDescent)
k.lineOffset = int(m.CapHeight)
if k.lineOffset < 0 {
k.lineOffset *= -1
}
}
// SetHideShortcuts sets the key shortcuts which, when pressed, will hide the
@ -626,13 +618,17 @@ func (k *Keyboard) drawBackground() {
label = key.UpperLabel
}
bounds := text.BoundString(k.labelFont, label)
x := (key.w - bounds.Dx()) / 2
boundsW, boundsY := text.Measure(label, k.labelFont, float64(k.lineHeight))
x := key.w/2 - int(boundsW)/2
if x < 0 {
x = 0
}
y := halfLineHeight + (key.h-halfLineHeight)/2
text.Draw(keyImage, label, k.labelFont, key.x+x, key.y+y, color.White)
y := key.h/2 - int(boundsY)/2
_ = halfLineHeight
op := &text.DrawOptions{}
op.GeoM.Translate(float64(key.x+x), float64(key.y+y))
op.ColorScale.ScaleWithColor(color.White)
text.Draw(keyImage, label, k.labelFont, op)
// Draw border
keyImage.SubImage(image.Rect(key.x, key.y, key.x+key.w, key.y+1)).(*ebiten.Image).Fill(lightShade)

View file

@ -1,15 +1,14 @@
package kibodo
import (
"log"
"bytes"
"runtime"
"testing"
"time"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
"golang.org/x/image/font/opentype"
"golang.org/x/image/font/sfnt"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
// TODO test presses registered
@ -88,10 +87,10 @@ func NewDummyGame() *DummyGame {
return &DummyGame{}
}
func defaultFont() *sfnt.Font {
f, err := opentype.Parse(fonts.MPlus1pRegular_ttf)
func defaultFont() *text.GoTextFaceSource {
source, err := text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf))
if err != nil {
log.Fatal(err)
panic(err)
}
return f
return source
}

View file

@ -3,6 +3,7 @@
package game
import (
"bytes"
"fmt"
"image"
"log"
@ -13,8 +14,7 @@ import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
const initialText = `
@ -28,24 +28,19 @@ Below is an InputField, which accepts keyboard input.
`
var (
mplusNormalFont font.Face
mplusNormalFont *text.GoTextFace
fontMutex *sync.Mutex
)
func init() {
tt, err := opentype.Parse(fonts.MPlus1pRegular_ttf)
source, err := text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf))
if err != nil {
log.Fatal(err)
panic(err)
}
const dpi = 72
mplusNormalFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
Size: 28,
DPI: dpi,
Hinting: font.HintingFull,
})
if err != nil {
log.Fatal(err)
mplusNormalFont = &text.GoTextFace{
Source: source,
Size: 24,
}
}

View file

@ -5,7 +5,7 @@ import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"golang.org/x/image/font"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
// InputField is a text input field. Call Update and Draw when your Game's
@ -40,7 +40,7 @@ type InputField struct {
}
// NewInputField returns a new InputField. See type documentation for more info.
func NewInputField(face font.Face, faceMutex *sync.Mutex) *InputField {
func NewInputField(face *text.GoTextFace, faceMutex *sync.Mutex) *InputField {
f := &InputField{
TextField: NewTextField(face, faceMutex),
}

View file

@ -11,9 +11,7 @@ import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/hajimehoshi/ebiten/v2/text"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
// Alignment specifies how text is aligned within the field.
@ -97,7 +95,7 @@ type TextField struct {
vertical Alignment
// face is the font face of the text within the field.
face font.Face
face *text.GoTextFace
// faceMutex is the lock which is held whenever utilizing the font face.
faceMutex *sync.Mutex
@ -190,7 +188,7 @@ type TextField struct {
}
// NewTextField returns a new TextField. See type documentation for more info.
func NewTextField(face font.Face, faceMutex *sync.Mutex) *TextField {
func NewTextField(face *text.GoTextFace, faceMutex *sync.Mutex) *TextField {
if faceMutex == nil {
faceMutex = &sync.Mutex{}
}
@ -423,7 +421,7 @@ func (f *TextField) SetBackgroundColor(c color.RGBA) {
}
// SetFont sets the font face of the text within the field.
func (f *TextField) SetFont(face font.Face, mutex *sync.Mutex) {
func (f *TextField) SetFont(face *text.GoTextFace, mutex *sync.Mutex) {
if mutex == nil {
mutex = &sync.Mutex{}
}
@ -793,8 +791,8 @@ func (f *TextField) Draw(screen *ebiten.Image) {
func (f *TextField) fontUpdated() {
m := f.face.Metrics()
f.lineHeight = m.Height.Ceil()
f.lineOffset = m.CapHeight.Ceil()
f.lineHeight = int(m.HAscent + m.HDescent)
f.lineOffset = int(m.HLineGap)
if f.lineOffset < 0 {
f.lineOffset *= -1
}
@ -811,11 +809,11 @@ func (f *TextField) wrapContent(withScrollBar bool) {
if f.singleLine || len(f.buffer) == 0 {
buffer := f.prefix + string(bytes.Join(f.buffer, nil)) + f.suffix
bounds, _ := font.BoundString(f.face, buffer)
w, _ := text.Measure(buffer, f.face, float64(f.lineHeight))
f.bufferWrapped = []string{buffer}
f.wrapStart = 0
f.lineWidths = append(f.lineWidths[:0], (bounds.Max.X - bounds.Min.X).Floor())
f.lineWidths = append(f.lineWidths[:0], int(w))
f.needWrap = -1
return
@ -862,7 +860,6 @@ func (f *TextField) wrapContent(withScrollBar bool) {
// wrapping is enabled, break the line at the last whitespace character.
var start int
var lastSpace int
var bounds fixed.Rectangle26_6
var boundsWidth int
WRAPTEXT:
for start < l {
@ -871,8 +868,8 @@ func (f *TextField) wrapContent(withScrollBar bool) {
if unicode.IsSpace(r) {
lastSpace = start + e + 1
}
bounds, _ = font.BoundString(f.face, line[start:start+e]+string(r))
boundsWidth = (bounds.Max.X - bounds.Min.X).Floor()
w, _ := text.Measure(line[start:start+e]+string(r), f.face, float64(f.lineHeight))
boundsWidth = int(w)
if boundsWidth > availableWidth {
original := e
if e != l-start-1 && e > 0 {
@ -882,8 +879,8 @@ func (f *TextField) wrapContent(withScrollBar bool) {
e = lastSpace - start - 1
}
if e != original {
bounds, _ = font.BoundString(f.face, line[start:start+e+1])
boundsWidth = (bounds.Max.X - bounds.Min.X).Floor()
w, _ := text.Measure(line[start:start+e+1], f.face, float64(f.lineHeight))
boundsWidth = int(w)
}
if len(f.bufferWrapped) <= j {
@ -956,10 +953,16 @@ func (f *TextField) drawContent() (overflow bool) {
}
// Calculate buffer size (width for single-line fields or height for multi-line fields).
if f.singleLine {
bounds, _ := font.BoundString(f.face, f.bufferWrapped[firstVisible])
f.bufferSize = (bounds.Max.X - bounds.Min.X).Floor()
w, _ := text.Measure(f.bufferWrapped[firstVisible], f.face, float64(f.lineHeight))
f.bufferSize = int(w)
if f.bufferSize > fieldWidth-f.padding*2 {
overflow = true
}
} else {
f.bufferSize = (len(f.bufferWrapped)) * lineHeight
if f.bufferSize > fieldHeight-f.padding*2 {
overflow = true
}
}
for i := firstVisible; i <= lastVisible; i++ {
line := f.bufferWrapped[i]
@ -970,10 +973,10 @@ func (f *TextField) drawContent() (overflow bool) {
}
}
lineX := f.padding
lineY := 1 + f.padding + f.lineOffset + lineHeight*i
lineY := 1 + f.padding + -f.lineOffset + lineHeight*i
// Calculate whether the line overflows the visible area.
lineOverflows := lineY < 0 || lineY >= h-(f.padding*2)
lineOverflows := lineY < 0 || lineY >= h-f.padding
if lineOverflows {
overflow = true
}
@ -998,7 +1001,7 @@ func (f *TextField) drawContent() (overflow bool) {
}
// Align vertically.
totalHeight := f.lineOffset + lineHeight*(lines-1)
totalHeight := f.lineOffset + lineHeight*(lines)
if f.vertical == AlignCenter && totalHeight <= h {
lineY = fieldHeight/2 - totalHeight/2 + f.lineOffset + (lineHeight * (i))
} else if f.vertical == AlignEnd && totalHeight <= h {
@ -1006,7 +1009,10 @@ func (f *TextField) drawContent() (overflow bool) {
}
// Draw line.
text.Draw(f.img, line, f.face, lineX, lineY, f.textColor)
op := &text.DrawOptions{}
op.GeoM.Translate(float64(lineX), float64(lineY))
op.ColorScale.ScaleWithColor(f.textColor)
text.Draw(f.img, line, f.face, op)
}
return overflow

View file

@ -1,16 +1,15 @@
package messeji
import (
"bytes"
"embed"
"fmt"
"image"
"log"
"sync"
"testing"
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
//go:embed testdata
@ -18,7 +17,25 @@ var testDataFS embed.FS
var testTextField *TextField
func testFace() (*text.GoTextFace, error) {
source, err := text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf))
if err != nil {
return nil, err
}
face := &text.GoTextFace{
Source: source,
Size: 24,
}
return face, nil
}
func TestWrapContent(t *testing.T) {
face, err := testFace()
if err != nil {
t.Error(err)
}
testCases := []struct {
long bool // Test data type.
wordWrap bool // Enable wordwrap.
@ -30,22 +47,6 @@ func TestWrapContent(t *testing.T) {
{true, true},
}
tt, err := opentype.Parse(fonts.MPlus1pRegular_ttf)
if err != nil {
log.Fatal(err)
}
const size = 24
const dpi = 72
face, err := opentype.NewFace(tt, &opentype.FaceOptions{
Size: size,
DPI: dpi,
Hinting: font.HintingFull,
})
if err != nil {
log.Fatal(err)
}
testRect := image.Rect(0, 0, 200, 400)
for _, c := range testCases {
@ -79,6 +80,11 @@ func TestWrapContent(t *testing.T) {
}
func BenchmarkWrapContent(b *testing.B) {
face, err := testFace()
if err != nil {
b.Error(err)
}
testCases := []struct {
long bool // Test data type.
wordWrap bool // Enable wordwrap.
@ -90,22 +96,6 @@ func BenchmarkWrapContent(b *testing.B) {
{true, true},
}
tt, err := opentype.Parse(fonts.MPlus1pRegular_ttf)
if err != nil {
log.Fatal(err)
}
const size = 24
const dpi = 72
face, err := opentype.NewFace(tt, &opentype.FaceOptions{
Size: size,
DPI: dpi,
Hinting: font.HintingFull,
})
if err != nil {
log.Fatal(err)
}
testRect := image.Rect(0, 0, 200, 400)
for _, c := range testCases {

View file

@ -119,6 +119,7 @@ func (s *Select) AddOption(label string) {
t := NewText(label)
t.SetVertical(AlignCenter)
t.SetForeground(textColor)
t.SetAutoResize(true)
s.list.AddChildAt(t, 0, len(s.items)-1)
}

View file

@ -3,14 +3,14 @@ package etk
import (
"image/color"
"golang.org/x/image/font/sfnt"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
var transparent = color.RGBA{0, 0, 0, 0}
// Attributes represents a default attribute configuration. Integer values will be scaled.
type Attributes struct {
TextFont *sfnt.Font
TextFont *text.GoTextFaceSource
TextSize int
TextColorLight color.RGBA

View file

@ -6,15 +6,14 @@ import (
"code.rocket9labs.com/tslocum/etk/messeji"
"github.com/hajimehoshi/ebiten/v2"
"golang.org/x/image/font"
"golang.org/x/image/font/sfnt"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
// Text is a text display widget.
type Text struct {
*Box
field *messeji.TextField
textFont *sfnt.Font
textFont *text.GoTextFaceSource
textSize int
textResize bool
textAutoSize int
@ -193,7 +192,7 @@ func (t *Text) resizeFont() {
}
var autoSize int
var ff font.Face
var ff *text.GoTextFace
for autoSize = t.textSize; autoSize > 0; autoSize-- {
ff = FontFace(t.textFont, autoSize)
bounds := BoundString(ff, t.field.Text())
@ -243,7 +242,7 @@ func (t *Text) FontSize() int {
}
// SetFont sets the font and text size of the field. Scaling is not applied.
func (t *Text) SetFont(fnt *sfnt.Font, size int) {
func (t *Text) SetFont(fnt *text.GoTextFaceSource, size int) {
t.Lock()
defer t.Unlock()

View file

@ -5,14 +5,14 @@ import (
"image"
"github.com/hajimehoshi/ebiten/v2"
"golang.org/x/image/font/sfnt"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
// Window displays a single child widget at a time, and includes a list to
// view other child widgets. Window.Show must be called after adding a widget.
type Window struct {
*Box
font *sfnt.Font
font *text.GoTextFaceSource
fontSize int
frameSize int
list *List
@ -56,7 +56,7 @@ func (w *Window) SetRect(r image.Rectangle) {
}
// SetFont sets the font and text size of the window titles. Scaling is not applied.
func (w *Window) SetFont(fnt *sfnt.Font, size int) {
func (w *Window) SetFont(fnt *text.GoTextFaceSource, size int) {
w.Lock()
defer w.Unlock()