Text input and display widgets for Ebitengine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
3.1 KiB

//go:build example
// +build example
package game
import (
"fmt"
"image"
"log"
"strings"
"code.rocketnine.space/tslocum/messeji"
"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"
)
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.
`
var mplusNormalFont font.Face
func init() {
tt, err := opentype.Parse(fonts.MPlus1pRegular_ttf)
if err != nil {
log.Fatal(err)
}
const dpi = 72
mplusNormalFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
Size: 32,
DPI: dpi,
Hinting: font.HintingFull,
})
if err != nil {
log.Fatal(err)
}
}
type game struct {
w, h int
buffer *messeji.TextField
input *messeji.InputField
singleLine bool
op *ebiten.DrawImageOptions
spinnerIndex int
}
// NewDemoGame returns a new messeji demo game.
func NewDemoGame() *game {
g := &game{
buffer: messeji.NewTextField(mplusNormalFont),
input: messeji.NewInputField(mplusNormalFont),
op: &ebiten.DrawImageOptions{
Filter: ebiten.FilterNearest,
},
}
g.buffer.SetText(strings.TrimSpace(initialText) + "\n")
g.input.SetHandleKeyboard(true)
g.input.SetSelectedFunc(func() (skipNewline bool) {
log.Printf("Input: %s", g.input.Text())
g.buffer.Write([]byte(fmt.Sprintf("Input: %s\n", g.input.Text())))
// Clear the input field.
g.input.SetText("")
return true
})
return g
}
func (g *game) Layout(outsideWidth, outsideHeight int) (int, int) {
if outsideWidth == g.w && outsideHeight == g.h {
return outsideWidth, outsideHeight
}
padding := 10
w, h := outsideWidth-padding*2, g.input.LineHeight()*3+g.input.Padding()*2
if h > outsideHeight-padding {
h = outsideHeight - padding
}
x, y := outsideWidth/2-w/2, outsideHeight-h-padding
g.buffer.SetRect(image.Rect(padding, padding, outsideWidth-padding, y-padding))
g.input.SetRect(image.Rect(x, y, x+w, y+h))
g.w, g.h = outsideWidth, outsideHeight
return outsideWidth, outsideHeight
}
func (g *game) Update() error {
if inpututil.IsKeyJustPressed(ebiten.KeyEnter) && !g.input.Visible() {
g.input.SetVisible(true)
return nil
}
if inpututil.IsKeyJustPressed(ebiten.KeyTab) {
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)
return nil
}
err := g.buffer.Update()
if err != nil {
return fmt.Errorf("failed to update buffer: %s", err)
}
err = g.input.Update()
if err != nil {
return fmt.Errorf("failed to update input field: %s", err)
}
return nil
}
func (g *game) Draw(screen *ebiten.Image) {
// Draw display field.
g.buffer.Draw(screen)
// Draw input field.
g.input.Draw(screen)
}