diff --git a/button.go b/button.go index 164b11a..4067681 100644 --- a/button.go +++ b/button.go @@ -16,7 +16,6 @@ type Button struct { btnBackground color.RGBA textFont *text.GoTextFaceSource textSize int - textAutoSize int borderSize int borderTop color.RGBA borderRight color.RGBA @@ -37,7 +36,7 @@ func NewButton(label string, onSelected func() error) *Button { f.SetForegroundColor(textColor) f.SetHorizontal(messeji.AlignCenter) f.SetVertical(messeji.AlignCenter) - f.SetScrollBarVisible(false) + f.SetAutoResize(true) b := &Button{ Box: NewBox(), @@ -53,7 +52,6 @@ func NewButton(label string, onSelected func() error) *Button { borderLeft: Style.ButtonBorderLeft, } b.SetBackground(Style.ButtonBgColor) - b.resizeFont() return b } @@ -62,7 +60,6 @@ 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) @@ -119,7 +116,6 @@ 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. @@ -128,36 +124,7 @@ func (b *Button) SetFont(fnt *text.GoTextFaceSource, size int) { defer b.Unlock() b.textFont, b.textSize = fnt, size - b.resizeFont() -} - -func (b *Button) resizeFont() { - w, h := b.rect.Dx()-b.field.Padding()*2, b.rect.Dy()-b.field.Padding()*2 - if w == 0 || h == 0 { - if b.textAutoSize == b.textSize { - return - } - b.textAutoSize = b.textSize - ff := FontFace(b.textFont, b.textSize) - b.field.SetFont(ff, fontMutex) - return - } - - var autoSize int - var ff *text.GoTextFace - for autoSize = b.textSize; autoSize > 0; autoSize-- { - ff = FontFace(b.textFont, autoSize) - bounds := BoundString(ff, b.field.Text()) - if bounds.Dx() <= w && bounds.Dy() <= h { - break - } - } - if b.textAutoSize == autoSize { - return - } - - b.field.SetFont(ff, fontMutex) - b.textAutoSize = autoSize + b.field.SetFont(b.textFont, b.textSize, fontMutex) } // SetHorizontal sets the horizontal alignment of the button label. diff --git a/game.go b/game.go index d2884da..b1d14a8 100644 --- a/game.go +++ b/game.go @@ -463,7 +463,7 @@ func draw(w Widget, screen *ebiten.Image) error { } func newText() *messeji.TextField { - f := messeji.NewTextField(FontFace(Style.TextFont, Scale(Style.TextSize)), fontMutex) + f := messeji.NewTextField(Style.TextFont, Scale(Style.TextSize), fontMutex) f.SetForegroundColor(Style.TextColorLight) f.SetBackgroundColor(transparent) f.SetScrollBarColors(Style.ScrollAreaColor, Style.ScrollHandleColor) diff --git a/go.mod b/go.mod index a92c2e5..3c99daf 100644 --- a/go.mod +++ b/go.mod @@ -18,8 +18,8 @@ require ( 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/exp/shiny v0.0.0-20241108190413-2d47ceb2692f // indirect + golang.org/x/mobile v0.0.0-20241108191957-fa514ef75a0f // 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 e33d134..a2139e9 100644 --- a/go.sum +++ b/go.sum @@ -24,12 +24,12 @@ github.com/llgcode/ps v0.0.0-20210114104736-f4b0c5d1e02e h1:ZAvbj5hI/G/EbAYAcj4y github.com/llgcode/ps v0.0.0-20210114104736-f4b0c5d1e02e/go.mod h1:1l8ky+Ew27CMX29uG+a2hNOKpeNYEQjjtiALiBlFQbY= 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/exp/shiny v0.0.0-20241108190413-2d47ceb2692f h1:vic/+xALPgqKNmH4tJ2tsJh9M51ULiNg5hoQd84DVM4= +golang.org/x/exp/shiny v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o= 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/mobile v0.0.0-20241108191957-fa514ef75a0f h1:23H/YlmTHfmmvpZ+ajKZL0qLz0+IwFOIqQA0mQbmLeM= +golang.org/x/mobile v0.0.0-20241108191957-fa514ef75a0f/go.mod h1:UbSUP4uu/C9hw9R2CkojhXlAxvayHjBdU9aRvE+c1To= 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= diff --git a/input.go b/input.go index b83e521..0ee88ce 100644 --- a/input.go +++ b/input.go @@ -23,7 +23,7 @@ type Input struct { // NewInput returns a new Input widget. func NewInput(text string, onSelected func(text string) (handled bool)) *Input { - f := messeji.NewInputField(FontFace(Style.TextFont, Scale(Style.TextSize)), fontMutex) + f := messeji.NewInputField(Style.TextFont, Scale(Style.TextSize), fontMutex) f.SetForegroundColor(Style.TextColorLight) f.SetBackgroundColor(transparent) f.SetScrollBarColors(Style.ScrollAreaColor, Style.ScrollHandleColor) @@ -191,7 +191,7 @@ func (t *Input) SetFont(fnt *text.GoTextFaceSource, size int) { t.Lock() defer t.Unlock() - t.field.SetFont(FontFace(fnt, size), fontMutex) + t.field.SetFont(fnt, size, fontMutex) } // Padding returns the amount of padding around the text within the field. diff --git a/messeji/examples/messeji/game/game.go b/messeji/examples/messeji/game/game.go index f7bec03..f0cd89f 100644 --- a/messeji/examples/messeji/game/game.go +++ b/messeji/examples/messeji/game/game.go @@ -28,20 +28,17 @@ Below is an InputField, which accepts keyboard input. ` var ( - mplusNormalFont *text.GoTextFace - fontMutex *sync.Mutex + fontSource *text.GoTextFaceSource + fontSize = 24 + fontMutex *sync.Mutex ) func init() { - source, err := text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf)) + var err error + fontSource, err = text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf)) if err != nil { panic(err) } - - mplusNormalFont = &text.GoTextFace{ - Source: source, - Size: 24, - } } type game struct { @@ -63,8 +60,8 @@ type game struct { // NewDemoGame returns a new messeji demo game. func NewDemoGame() *game { g := &game{ - buffer: messeji.NewTextField(mplusNormalFont, fontMutex), - input: messeji.NewInputField(mplusNormalFont, fontMutex), + buffer: messeji.NewTextField(fontSource, fontSize, fontMutex), + input: messeji.NewInputField(fontSource, fontSize, fontMutex), op: &ebiten.DrawImageOptions{ Filter: ebiten.FilterNearest, }, diff --git a/messeji/inputfield.go b/messeji/inputfield.go index 315729a..d7f5ccc 100644 --- a/messeji/inputfield.go +++ b/messeji/inputfield.go @@ -40,9 +40,9 @@ type InputField struct { } // NewInputField returns a new InputField. See type documentation for more info. -func NewInputField(face *text.GoTextFace, faceMutex *sync.Mutex) *InputField { +func NewInputField(fontSource *text.GoTextFaceSource, fontSize int, fontMutex *sync.Mutex) *InputField { f := &InputField{ - TextField: NewTextField(face, faceMutex), + TextField: NewTextField(fontSource, fontSize, fontMutex), } f.TextField.suffix = "_" return f diff --git a/messeji/textfield.go b/messeji/textfield.go index 0dbdaa5..e5e1af3 100644 --- a/messeji/textfield.go +++ b/messeji/textfield.go @@ -94,11 +94,22 @@ type TextField struct { // vertical is the vertical alignment of the text within field. vertical Alignment - // face is the font face of the text within the field. - face *text.GoTextFace + autoResize bool - // faceMutex is the lock which is held whenever utilizing the font face. - faceMutex *sync.Mutex + // fontSource is the font face source of the text within the field. + fontSource *text.GoTextFaceSource + + // fontFace is the font face of the text within the field. + fontFace *text.GoTextFace + + // fontSize is the maximum font size of the text within the field. + fontSize int + + // overrideFontSize is the actual font size of the text within the field. + overrideFontSize int + + // fontMutex is the lock which is held whenever utilizing the font. + fontMutex *sync.Mutex // lineHeight is the height of a single line of text. lineHeight int @@ -188,14 +199,15 @@ type TextField struct { } // NewTextField returns a new TextField. See type documentation for more info. -func NewTextField(face *text.GoTextFace, faceMutex *sync.Mutex) *TextField { - if faceMutex == nil { - faceMutex = &sync.Mutex{} +func NewTextField(fontSource *text.GoTextFaceSource, fontSize int, fontMutex *sync.Mutex) *TextField { + if fontMutex == nil { + fontMutex = &sync.Mutex{} } f := &TextField{ - face: face, - faceMutex: faceMutex, + fontSource: fontSource, + fontSize: fontSize, + fontMutex: fontMutex, textColor: initialForeground, backgroundColor: initialBackground, padding: initialPadding, @@ -210,10 +222,10 @@ func NewTextField(face *text.GoTextFace, faceMutex *sync.Mutex) *TextField { redraw: true, } - f.faceMutex.Lock() - defer f.faceMutex.Unlock() + f.fontMutex.Lock() + defer f.fontMutex.Unlock() - f.fontUpdated() + f.resizeFont() return f } @@ -243,6 +255,12 @@ func (f *TextField) SetRect(r image.Rectangle) { } f.r = r + f.resizeFont() +} + +func (f *TextField) text() string { + f.processIncoming() + return string(bytes.Join(f.buffer, []byte("\n"))) } // Text returns the text in the field. @@ -250,9 +268,7 @@ func (f *TextField) Text() string { f.Lock() defer f.Unlock() - f.processIncoming() - - return string(bytes.Join(f.buffer, []byte("\n"))) + return f.text() } // SetText sets the text in the field. @@ -268,6 +284,7 @@ func (f *TextField) SetText(text string) { f.incoming = append(f.incoming[:0], []byte(text)...) f.modified = true f.redraw = true + f.resizeFont() } // SetLast sets the text of the last line of the field. Newline characters are @@ -292,6 +309,7 @@ func (f *TextField) SetLast(text string) { f.modified = true f.redraw = true + f.resizeFont() } // SetPrefix sets the text shown before the content of the field. @@ -303,6 +321,7 @@ func (f *TextField) SetPrefix(text string) { f.needWrap = 0 f.wrapStart = 0 f.modified = true + f.resizeFont() } // SetSuffix sets the text shown after the content of the field. @@ -314,6 +333,7 @@ func (f *TextField) SetSuffix(text string) { f.needWrap = 0 f.wrapStart = 0 f.modified = true + f.resizeFont() } // SetFollow sets whether the field should automatically scroll to the end when @@ -339,6 +359,7 @@ func (f *TextField) SetSingleLine(single bool) { f.needWrap = 0 f.wrapStart = 0 f.modified = true + f.resizeFont() } // SetHorizontal sets the horizontal alignment of the text within the field. @@ -392,6 +413,7 @@ func (f *TextField) SetLineHeight(height int) { f.needWrap = 0 f.wrapStart = 0 f.modified = true + f.resizeFont() } // ForegroundColor returns the color of the text within the field. @@ -421,7 +443,7 @@ func (f *TextField) SetBackgroundColor(c color.RGBA) { } // SetFont sets the font face of the text within the field. -func (f *TextField) SetFont(face *text.GoTextFace, mutex *sync.Mutex) { +func (f *TextField) SetFont(faceSource *text.GoTextFaceSource, size int, mutex *sync.Mutex) { if mutex == nil { mutex = &sync.Mutex{} } @@ -432,13 +454,25 @@ func (f *TextField) SetFont(face *text.GoTextFace, mutex *sync.Mutex) { mutex.Lock() defer mutex.Unlock() - f.face = face - f.faceMutex = mutex - f.fontUpdated() + f.fontSource = faceSource + f.fontSize = size + f.fontMutex = mutex + f.overrideFontSize = 0 f.needWrap = 0 f.wrapStart = 0 f.modified = true + f.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 (f *TextField) SetAutoResize(resize bool) { + f.Lock() + defer f.Unlock() + + f.autoResize = resize + f.resizeFont() } // Padding returns the amount of padding around the text within the field. @@ -458,6 +492,7 @@ func (f *TextField) SetPadding(padding int) { f.needWrap = 0 f.wrapStart = 0 f.modified = true + f.resizeFont() } // Visible returns whether the field is currently visible on the screen. @@ -493,6 +528,7 @@ func (f *TextField) SetScrollBarWidth(width int) { f.needWrap = 0 f.wrapStart = 0 f.modified = true + f.resizeFont() } // SetScrollBarColors sets the color of the scroll bar area and handle. @@ -580,6 +616,53 @@ func (f *TextField) SetWordWrap(wrap bool) { f.modified = true } +func (f *TextField) resizeFont() { + if !f.autoResize { + if f.overrideFontSize == f.fontSize { + return + } + f.overrideFontSize = f.fontSize + f.fontFace = fontFace(f.fontSource, f.overrideFontSize) + f.fontUpdated() + f.bufferModified() + return + } + + w, h := f.r.Dx()-f.padding*2, f.r.Dy()-f.padding*2 + if w <= 0 || h <= 0 { + if f.overrideFontSize == f.fontSize { + return + } + f.overrideFontSize = f.fontSize + f.fontFace = fontFace(f.fontSource, f.overrideFontSize) + f.fontUpdated() + f.bufferModified() + return + } + + buffer := f.text() + + var size int + var ff *text.GoTextFace + var m text.Metrics + for size = f.fontSize; size > 1; size-- { + ff = fontFace(f.fontSource, size) + m = ff.Metrics() + bw, bh := text.Measure(buffer, ff, m.HAscent+m.HDescent) + if int(bw) <= w && int(bh) <= h { + break + } + } + if f.overrideFontSize == size { + return + } + + f.overrideFontSize = size + f.fontFace = ff + f.fontUpdated() + f.bufferModified() +} + // SetHandleKeyboard sets a flag controlling whether keyboard input should be handled // by the field. This can be used to facilitate focus changes between multiple inputs. func (f *TextField) SetHandleKeyboard(handle bool) { @@ -600,6 +683,7 @@ func (f *TextField) SetMask(r rune) { f.maskRune = r f.modified = true + f.resizeFont() } // Write writes to the field's buffer. @@ -763,12 +847,12 @@ func (f *TextField) Draw(screen *ebiten.Image) { defer f.Unlock() if f.modified { - f.faceMutex.Lock() + f.fontMutex.Lock() f.bufferModified() f.modified = false - f.faceMutex.Unlock() + f.fontMutex.Unlock() } if !f.visible || rectIsZero(f.r) { @@ -776,12 +860,12 @@ func (f *TextField) Draw(screen *ebiten.Image) { } if f.redraw { - f.faceMutex.Lock() + f.fontMutex.Lock() f.drawImage() f.redraw = false - f.faceMutex.Unlock() + f.fontMutex.Unlock() } op := &ebiten.DrawImageOptions{} @@ -790,7 +874,7 @@ func (f *TextField) Draw(screen *ebiten.Image) { } func (f *TextField) fontUpdated() { - m := f.face.Metrics() + m := f.fontFace.Metrics() f.lineHeight = int(m.HAscent + m.HDescent) f.lineOffset = int(m.HLineGap) if f.lineOffset < 0 { @@ -807,9 +891,9 @@ func (f *TextField) wrapContent(withScrollBar bool) { } f.wrapScrollBar = withScrollBar - if f.singleLine || len(f.buffer) == 0 { + if len(f.buffer) == 0 || (f.singleLine && !f.autoResize) { buffer := f.prefix + string(bytes.Join(f.buffer, nil)) + f.suffix - w, _ := text.Measure(buffer, f.face, float64(f.lineHeight)) + w, _ := text.Measure(buffer, f.fontFace, float64(f.lineHeight)) f.bufferWrapped = []string{buffer} f.wrapStart = 0 @@ -836,7 +920,7 @@ func (f *TextField) wrapContent(withScrollBar bool) { line += f.suffix } l := len(line) - availableWidth := w - (f.padding * 2) - 5 + availableWidth := w - (f.padding * 2) - 15 f.wrapStart = j @@ -864,29 +948,31 @@ func (f *TextField) wrapContent(withScrollBar bool) { WRAPTEXT: for start < l { lastSpace = -1 - for e, r := range line[start:] { - if unicode.IsSpace(r) { - lastSpace = start + e + 1 + var e int + for _, r := range line[start:] { + if e > l-start { + e = l - start } - w, _ := text.Measure(line[start:start+e]+string(r), f.face, float64(f.lineHeight)) + runeLength := len(string(r)) + if unicode.IsSpace(r) { + lastSpace = start + e + } + w, _ := text.Measure(line[start:start+e], f.fontFace, float64(f.lineHeight)) boundsWidth = int(w) if boundsWidth > availableWidth { - original := e - if e != l-start-1 && e > 0 { - e-- + if e > 0 { + e -= runeLength } - if f.wordWrap && lastSpace != -1 && start+e+1 < l { - e = lastSpace - start - 1 - } - if e != original { - w, _ := text.Measure(line[start:start+e+1], f.face, float64(f.lineHeight)) - boundsWidth = int(w) + if f.wordWrap && lastSpace != -1 && start+e+runeLength < l { + e = lastSpace - start } + w, _ := text.Measure(line[start:start+e], f.fontFace, float64(f.lineHeight)) + boundsWidth = int(w) if len(f.bufferWrapped) <= j { - f.bufferWrapped = append(f.bufferWrapped, line[start:start+e+1]) + f.bufferWrapped = append(f.bufferWrapped, line[start:start+e]) } else { - f.bufferWrapped[j] = line[start : start+e+1] + f.bufferWrapped[j] = line[start : start+e] } if len(f.lineWidths) <= j { f.lineWidths = append(f.lineWidths, boundsWidth) @@ -895,11 +981,14 @@ func (f *TextField) wrapContent(withScrollBar bool) { } j++ - start = start + e + 1 + start += e + runeLength continue WRAPTEXT } + e += runeLength } + w, _ := text.Measure(line[start:], f.fontFace, float64(f.lineHeight)) + boundsWidth = int(w) if len(f.bufferWrapped) <= j { f.bufferWrapped = append(f.bufferWrapped, line[start:]) } else { @@ -954,7 +1043,7 @@ func (f *TextField) drawContent() (overflow bool) { numVisible := lastVisible - firstVisible // Calculate buffer size (width for single-line fields or height for multi-line fields). if f.singleLine { - w, _ := text.Measure(f.bufferWrapped[firstVisible], f.face, float64(f.lineHeight)) + w, _ := text.Measure(f.bufferWrapped[firstVisible], f.fontFace, float64(f.lineHeight)) f.bufferSize = int(w) if f.bufferSize > fieldWidth-f.padding*2 { overflow = true @@ -1003,9 +1092,9 @@ func (f *TextField) drawContent() (overflow bool) { // Align vertically. totalHeight := f.lineOffset + lineHeight*(lines) - if f.vertical == AlignCenter && totalHeight <= h { + if f.vertical == AlignCenter && (f.autoResize || totalHeight <= h) { lineY = fieldHeight/2 - totalHeight/2 + f.lineOffset + (lineHeight * (i)) - 2 - } else if f.vertical == AlignEnd && totalHeight <= h { + } else if f.vertical == AlignEnd && (f.autoResize || totalHeight <= h) { lineY = fieldHeight - lineHeight*(numVisible+1-i) - f.padding } @@ -1013,7 +1102,7 @@ func (f *TextField) drawContent() (overflow bool) { op := &text.DrawOptions{} op.GeoM.Translate(float64(lineX), float64(lineY)) op.ColorScale.ScaleWithColor(f.textColor) - text.Draw(f.img, line, f.face, op) + text.Draw(f.img, line, f.fontFace, op) } return overflow @@ -1040,10 +1129,24 @@ func (f *TextField) clampOffset() { } func (f *TextField) showScrollBar() bool { - return !f.singleLine && f.scrollVisible && (f.overflow || !f.scrollAutoHide) + return !f.autoResize && !f.singleLine && f.scrollVisible && (f.overflow || !f.scrollAutoHide) } func (f *TextField) wrap() { + w, h := f.r.Dx(), f.r.Dy() + + var newImage bool + if f.img == nil { + newImage = true + } else { + imgRect := f.img.Bounds() + imgW, imgH := imgRect.Dx(), imgRect.Dy() + newImage = imgW != w || imgH != h + } + if newImage { + f.img = ebiten.NewImage(w, h) + } + showScrollBar := f.showScrollBar() f.wrapContent(showScrollBar) f.overflow = f.drawContent() @@ -1060,24 +1163,12 @@ func (f *TextField) drawImage() { return } - w, h := f.r.Dx(), f.r.Dy() - - var newImage bool - if f.img == nil { - newImage = true - } else { - imgRect := f.img.Bounds() - imgW, imgH := imgRect.Dx(), imgRect.Dy() - newImage = imgW != w || imgH != h - } - if newImage { - f.img = ebiten.NewImage(w, h) - } - f.wrap() // Draw scrollbar. if f.showScrollBar() { + w, h := f.r.Dx(), f.r.Dy() + scrollAreaX, scrollAreaY := w-f.scrollWidth, 0 f.scrollRect = image.Rect(scrollAreaX, scrollAreaY, scrollAreaX+f.scrollWidth, h) @@ -1152,3 +1243,10 @@ func (f *TextField) bufferModified() { func rectIsZero(r image.Rectangle) bool { return r.Dx() == 0 || r.Dy() == 0 } + +func fontFace(source *text.GoTextFaceSource, size int) *text.GoTextFace { + return &text.GoTextFace{ + Source: source, + Size: float64(size), + } +} diff --git a/messeji/textfield_test.go b/messeji/textfield_test.go index 6f6baeb..ec267f8 100644 --- a/messeji/textfield_test.go +++ b/messeji/textfield_test.go @@ -31,10 +31,8 @@ func testFace() (*text.GoTextFace, error) { } func TestWrapContent(t *testing.T) { - face, err := testFace() - if err != nil { - t.Error(err) - } + const fontSize = 24 + fontSource := defaultFont() testCases := []struct { long bool // Test data type. @@ -69,7 +67,7 @@ func TestWrapContent(t *testing.T) { } t.Run(name, func(t *testing.T) { - textField := NewTextField(face, &sync.Mutex{}) + textField := NewTextField(fontSource, fontSize, &sync.Mutex{}) testTextField = textField textField.SetRect(testRect) textField.SetWordWrap(c.wordWrap) @@ -80,10 +78,8 @@ func TestWrapContent(t *testing.T) { } func BenchmarkWrapContent(b *testing.B) { - face, err := testFace() - if err != nil { - b.Error(err) - } + const fontSize = 24 + fontSource := defaultFont() testCases := []struct { long bool // Test data type. @@ -117,7 +113,7 @@ func BenchmarkWrapContent(b *testing.B) { name += "/wrapWord" } - textField := NewTextField(face, &sync.Mutex{}) + textField := NewTextField(fontSource, fontSize, &sync.Mutex{}) testTextField = textField textField.SetRect(testRect) textField.SetWordWrap(c.wordWrap) @@ -131,3 +127,11 @@ func BenchmarkWrapContent(b *testing.B) { }) } } + +func defaultFont() *text.GoTextFaceSource { + source, err := text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf)) + if err != nil { + panic(err) + } + return source +} diff --git a/text.go b/text.go index 25d8002..4d581ba 100644 --- a/text.go +++ b/text.go @@ -15,8 +15,6 @@ type Text struct { field *messeji.TextField textFont *text.GoTextFaceSource textSize int - textResize bool - textAutoSize int scrollVisible bool children []Widget } @@ -35,7 +33,6 @@ func NewText(text string) *Text { textSize: Scale(Style.TextSize), scrollVisible: true, } - t.resizeFont() return t } @@ -46,7 +43,6 @@ 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. @@ -139,7 +135,6 @@ func (t *Text) Write(p []byte) (n int, err error) { if err != nil { return n, err } - t.resizeFont() return n, err } @@ -157,7 +152,6 @@ func (t *Text) SetText(text string) { defer t.Unlock() t.field.SetText(text) - t.resizeFont() } // SetLast sets the text of the last line of the field. @@ -166,52 +160,18 @@ func (t *Text) SetLast(text string) { defer t.Unlock() t.field.SetLast(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 - } +// 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() - w, h := t.rect.Dx()-t.field.Padding()*2, t.rect.Dy()-t.field.Padding()*2 - if w == 0 || h == 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 *text.GoTextFace - for autoSize = t.textSize; autoSize > 0; autoSize-- { - ff = FontFace(t.textFont, autoSize) - bounds := BoundString(ff, t.field.Text()) - if bounds.Dx() <= w && bounds.Dy() <= h { - break - } - } - if t.textAutoSize == autoSize { - return - } - - t.field.SetFont(ff, fontMutex) - t.textAutoSize = autoSize + t.field.SetAutoResize(resize) } func (t *Text) scrollBarVisible() bool { - if t.textResize { - return false - } return t.scrollVisible } @@ -238,7 +198,7 @@ func (t *Text) FontSize() int { t.Lock() defer t.Unlock() - return t.textAutoSize + return t.textSize } // SetFont sets the font and text size of the field. Scaling is not applied. @@ -247,18 +207,7 @@ func (t *Text) SetFont(fnt *text.GoTextFaceSource, size int) { defer t.Unlock() 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() - t.field.SetScrollBarVisible(t.scrollBarVisible()) + t.field.SetFont(t.textFont, t.textSize, fontMutex) } // Padding returns the amount of padding around the text within the field.