From 746cdb2994e902bbe132a0702ccf0659d0c82816 Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Thu, 14 Nov 2024 23:24:12 -0800 Subject: [PATCH] Use text/v2 to draw text --- button.go | 9 ++-- examples/showcase/game.go | 13 +++--- game.go | 36 ++++------------ go.mod | 11 ++--- go.sum | 24 ++++++----- input.go | 4 +- kibodo/examples/kibodo/game/game.go | 13 +++--- kibodo/keyboard.go | 50 ++++++++++------------ kibodo/keyboard_test.go | 13 +++--- messeji/examples/messeji/game/game.go | 21 ++++------ messeji/inputfield.go | 4 +- messeji/textfield.go | 48 +++++++++++---------- messeji/textfield_test.go | 60 +++++++++++---------------- select.go | 1 + style.go | 4 +- text.go | 9 ++-- window.go | 6 +-- 17 files changed, 147 insertions(+), 179 deletions(-) diff --git a/button.go b/button.go index 94ca264..164b11a 100644 --- a/button.go +++ b/button.go @@ -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()) diff --git a/examples/showcase/game.go b/examples/showcase/game.go index 297f669..1e81405 100644 --- a/examples/showcase/game.go +++ b/examples/showcase/game.go @@ -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 } diff --git a/game.go b/game.go index 6587c91..d2884da 100644 --- a/game.go +++ b/game.go @@ -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, diff --git a/go.mod b/go.mod index 171f4ed..a92c2e5 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 5d9ac22..e33d134 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/input.go b/input.go index 5d4da65..b83e521 100644 --- a/input.go +++ b/input.go @@ -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() diff --git a/kibodo/examples/kibodo/game/game.go b/kibodo/examples/kibodo/game/game.go index 2183bd3..6c975c0 100644 --- a/kibodo/examples/kibodo/game/game.go +++ b/kibodo/examples/kibodo/game/game.go @@ -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 } diff --git a/kibodo/keyboard.go b/kibodo/keyboard.go index 617d885..4a0c917 100644 --- a/kibodo/keyboard.go +++ b/kibodo/keyboard.go @@ -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) diff --git a/kibodo/keyboard_test.go b/kibodo/keyboard_test.go index 01020f8..a40cd5e 100644 --- a/kibodo/keyboard_test.go +++ b/kibodo/keyboard_test.go @@ -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 } diff --git a/messeji/examples/messeji/game/game.go b/messeji/examples/messeji/game/game.go index 797c3e3..f7bec03 100644 --- a/messeji/examples/messeji/game/game.go +++ b/messeji/examples/messeji/game/game.go @@ -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, } } diff --git a/messeji/inputfield.go b/messeji/inputfield.go index 1f76f13..315729a 100644 --- a/messeji/inputfield.go +++ b/messeji/inputfield.go @@ -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), } diff --git a/messeji/textfield.go b/messeji/textfield.go index 080f803..4dc20f4 100644 --- a/messeji/textfield.go +++ b/messeji/textfield.go @@ -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 diff --git a/messeji/textfield_test.go b/messeji/textfield_test.go index b12171e..6f6baeb 100644 --- a/messeji/textfield_test.go +++ b/messeji/textfield_test.go @@ -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 { diff --git a/select.go b/select.go index dcecd8b..aebb316 100644 --- a/select.go +++ b/select.go @@ -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) } diff --git a/style.go b/style.go index b79eea3..197d4bf 100644 --- a/style.go +++ b/style.go @@ -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 diff --git a/text.go b/text.go index fe91597..25d8002 100644 --- a/text.go +++ b/text.go @@ -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() diff --git a/window.go b/window.go index d263707..899402f 100644 --- a/window.go +++ b/window.go @@ -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()