Add SetHorizontal and SetVertical

These methods control how text is aligned within the field.
This commit is contained in:
Trevor Slocum 2022-06-09 13:53:16 -07:00
parent ed816e4129
commit b37820227a
5 changed files with 115 additions and 23 deletions

View File

@ -1,8 +1,9 @@
# messeji
[![GoDoc](https://code.rocketnine.space/tslocum/godoc-static/raw/branch/master/badge.svg)](https://docs.rocketnine.space/code.rocketnine.space/tslocum/messeji)
[![Donate](https://img.shields.io/liberapay/receives/rocketnine.space.svg?logo=liberapay)](https://liberapay.com/rocketnine.space)
[![Donate via LiberaPay](https://img.shields.io/liberapay/receives/rocketnine.space.svg?logo=liberapay)](https://liberapay.com/rocketnine.space)
[![Donate via Patreon](https://img.shields.io/badge/dynamic/json?color=%23e85b46&label=Patreon&query=data.attributes.patron_count&suffix=%20patrons&url=https%3A%2F%2Fwww.patreon.com%2Fapi%2Fcampaigns%2F5252223)](https://www.patreon.com/rocketnine)
Text widgets for [Ebiten](https://github.com/hajimehoshi/ebiten)
Text widgets for [Ebitengine](https://github.com/hajimehoshi/ebiten)
**Note:** [IME](https://en.wikipedia.org/wiki/Input_method) is not yet supported.

2
doc.go
View File

@ -1,4 +1,4 @@
/*
Package messeji provides text widgets for Ebiten.
Package messeji provides text widgets for Ebitengine.
*/
package messeji

View File

@ -21,9 +21,10 @@ const initialText = `
Welcome to the メッセージ (messeji) text widgets demo.
This is a TextField, which can be used to display text.
Below is an InputField, which accepts keyboard input.
Press <Tab> to toggle word wrap.
Press <Enter> to append input text.
Press <Ctrl+M> to toggle multi-line.
<Tab> to cycle horizontal alignment.
<Enter> to append input text to buffer.
<Ctrl+Tab> to toggle word wrap.
<Ctrl+Enter> to toggle multi-line input.
`
var mplusNormalFont font.Face
@ -36,7 +37,7 @@ func init() {
const dpi = 72
mplusNormalFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
Size: 32,
Size: 28,
DPI: dpi,
Hinting: font.HintingFull,
})
@ -57,6 +58,8 @@ type game struct {
op *ebiten.DrawImageOptions
spinnerIndex int
horizontal messeji.Alignment
}
// NewDemoGame returns a new messeji demo game.
@ -109,21 +112,26 @@ func (g *game) Layout(outsideWidth, outsideHeight int) (int, int) {
}
func (g *game) Update() error {
if inpututil.IsKeyJustPressed(ebiten.KeyEnter) && !g.input.Visible() {
if inpututil.IsKeyJustPressed(ebiten.KeyEnter) && ebiten.IsKeyPressed(ebiten.KeyControl) {
g.singleLine = !g.singleLine
g.input.SetSingleLine(g.singleLine)
return nil
} else if inpututil.IsKeyJustPressed(ebiten.KeyEnter) && !g.input.Visible() {
g.input.SetVisible(true)
return nil
}
if inpututil.IsKeyJustPressed(ebiten.KeyTab) {
if inpututil.IsKeyJustPressed(ebiten.KeyTab) && ebiten.IsKeyPressed(ebiten.KeyControl) {
wrap := g.buffer.WordWrap()
g.buffer.SetWordWrap(!wrap)
g.input.SetWordWrap(!wrap)
return nil
}
if inpututil.IsKeyJustPressed(ebiten.KeyM) && ebiten.IsKeyPressed(ebiten.KeyControl) {
g.singleLine = !g.singleLine
g.input.SetSingleLine(g.singleLine)
} else if inpututil.IsKeyJustPressed(ebiten.KeyTab) {
g.horizontal++
if g.horizontal > messeji.AlignEnd {
g.horizontal = messeji.AlignStart
}
g.buffer.SetHorizontal(g.horizontal)
return nil
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

@ -13,8 +13,22 @@ import (
"golang.org/x/image/font"
)
// Alignment specifies how text is aligned within the field.
type Alignment int
const (
initialPadding = 2
// AlignStart aligns text at the start of the field.
AlignStart Alignment = 0
// AlignCenter aligns text at the center of the field.
AlignCenter Alignment = 1
// AlignEnd aligns text at the end of the field.
AlignEnd Alignment = 2
)
const (
initialPadding = 5
initialScrollWidth = 32
)
@ -51,9 +65,18 @@ type TextField struct {
// line mode is enabled,
bufferSize int
// lineWidths is the size (in pixels) of each line as it appears on the screen.
lineWidths []int
// singleLine is whether the field displays all text on a single line.
singleLine bool
// horizontal is the horizontal alignment of the text within field.
horizontal Alignment
// vertical is the vertical alignment of the text within field.
vertical Alignment
// face is the font face of the text within the field.
face font.Face
@ -207,6 +230,32 @@ func (f *TextField) SetSingleLine(single bool) {
f.bufferModified()
}
// SetHorizontal sets the horizontal alignment of the text within the field.
func (f *TextField) SetHorizontal(h Alignment) {
f.Lock()
defer f.Unlock()
if f.horizontal == h {
return
}
f.horizontal = h
f.bufferModified()
}
// SetVertical sets the veritcal alignment of the text within the field.
func (f *TextField) SetVertical(v Alignment) {
f.Lock()
defer f.Unlock()
if f.vertical == v {
return
}
f.vertical = v
f.bufferModified()
}
// LineHeight returns the line height for the field.
func (f *TextField) LineHeight() int {
f.Lock()
@ -467,10 +516,15 @@ func (f *TextField) setDefaultLineHeight() {
}
func (f *TextField) wrapContent(withScrollBar bool) {
f.lineWidths = f.lineWidths[:0]
buffer := f.prefix + f.buffer + f.suffix
if f.singleLine {
f.bufferWrapped = []string{strings.ReplaceAll(buffer, "\n", "")}
buffer = strings.ReplaceAll(buffer, "\n", "")
bounds := text.BoundString(f.face, buffer)
f.bufferWrapped = []string{buffer}
f.lineWidths = append(f.lineWidths, bounds.Dx())
return
}
@ -483,8 +537,11 @@ func (f *TextField) wrapContent(withScrollBar bool) {
l := len(line)
var start int
var end int
var initialEnd int
for start < l {
for end = l; end > start; end-- {
initialEnd = end
bounds := text.BoundString(f.face, line[start:end])
if bounds.Dx() < w-(f.padding*2)-2 {
if f.wordWrap {
@ -492,12 +549,21 @@ func (f *TextField) wrapContent(withScrollBar bool) {
for endOffset := 0; endOffset < end-start; endOffset++ {
if unicode.IsSpace(rune(line[end-endOffset])) {
end = end - endOffset
if end < l-1 {
end++
}
break
}
}
}
}
if end != initialEnd && f.horizontal != AlignStart {
bounds = text.BoundString(f.face, line[start:end])
}
f.bufferWrapped = append(f.bufferWrapped, line[start:end])
f.lineWidths = append(f.lineWidths, bounds.Dx())
break
}
}
@ -510,12 +576,19 @@ func (f *TextField) wrapContent(withScrollBar bool) {
func (f *TextField) drawContent() (overflow bool) {
f.img.Fill(f.backgroundColor)
fieldWidth := f.r.Dx()
fieldHeight := f.r.Dy()
if f.showScrollBar() {
fieldWidth -= f.scrollWidth
}
lines := len(f.bufferWrapped)
h := f.r.Dy()
lineHeight := f.overrideLineHeight
if lineHeight == 0 {
lineHeight = f.lineHeight
}
lineOffset := 7
lineOffset := lineHeight / 3
f.bufferSize = 0
for i, line := range f.bufferWrapped {
lineX := f.padding
@ -545,6 +618,18 @@ func (f *TextField) drawContent() (overflow bool) {
lineY -= f.offset
}
if f.horizontal == AlignCenter {
lineX = (fieldWidth - f.lineWidths[i]) / 2
} else if f.horizontal == AlignEnd {
lineX = (fieldWidth - f.lineWidths[i]) - f.padding*2
}
if f.vertical == AlignCenter && lineHeight*lines <= h {
lineY = (fieldHeight-(lineHeight*lines))/2 + lineHeight*(i+1) - lineOffset
} else if f.vertical == AlignEnd && lineHeight*lines <= h {
lineY = (fieldHeight - lineHeight*i) - f.padding*2
}
text.Draw(f.img, line, f.face, lineX, lineY, f.textColor)
}
@ -596,22 +681,20 @@ func (f *TextField) drawImage() {
f.drawContent()
}
scrollBarW := 32
// Draw scrollbar.
if f.showScrollBar() {
scrollAreaX, scrollAreaY := w-scrollBarW, 0
f.scrollRect = image.Rect(scrollAreaX, scrollAreaY, scrollAreaX+scrollBarW, h)
scrollAreaX, scrollAreaY := w-f.scrollWidth, 0
f.scrollRect = image.Rect(scrollAreaX, scrollAreaY, scrollAreaX+f.scrollWidth, h)
scrollBarH := f.scrollWidth / 2
if scrollBarH < 4 {
scrollBarH = 4
}
scrollX, scrollY := w-scrollBarW, 0
scrollX, scrollY := w-f.scrollWidth, 0
pct := float64(f.offset) / float64(f.bufferSize-h)
scrollY += int(float64(h-scrollBarH) * pct)
scrollBarRect := image.Rect(scrollX, scrollY, scrollX+scrollBarW, scrollY+scrollBarH)
scrollBarRect := image.Rect(scrollX, scrollY, scrollX+f.scrollWidth, scrollY+scrollBarH)
f.img.SubImage(f.scrollRect).(*ebiten.Image).Fill(color.RGBA{200, 200, 200, 255})
f.img.SubImage(scrollBarRect).(*ebiten.Image).Fill(color.RGBA{108, 108, 108, 255})