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.
217 lines
4.7 KiB
217 lines
4.7 KiB
package kibodo
|
|
|
|
import (
|
|
"image"
|
|
"image/color"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
|
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
|
)
|
|
|
|
// Keyboard is an on-screen keyboard widget.
|
|
type Keyboard struct {
|
|
x, y int
|
|
w, h int
|
|
|
|
visible bool
|
|
alpha float64
|
|
passPhysical bool
|
|
allowUserHide bool
|
|
|
|
internalBuffer []rune
|
|
inputBuffer chan *Input
|
|
|
|
keys [][]*Key
|
|
|
|
background *ebiten.Image
|
|
|
|
op *ebiten.DrawImageOptions
|
|
}
|
|
|
|
// NewKeyboard returns a new Keyboard.
|
|
func NewKeyboard() *Keyboard {
|
|
k := &Keyboard{
|
|
alpha: 1.0,
|
|
op: &ebiten.DrawImageOptions{
|
|
Filter: ebiten.FilterNearest,
|
|
},
|
|
background: ebiten.NewImage(1, 1),
|
|
}
|
|
return k
|
|
}
|
|
|
|
// SetRect sets the position and size of the widget.
|
|
func (k *Keyboard) SetRect(x, y, w, h int) {
|
|
if k.x == x && k.y == y && k.w == w && k.h == h {
|
|
return
|
|
}
|
|
k.x, k.y, k.w, k.h = x, y, w, h
|
|
|
|
k.updateKeyRects()
|
|
k.drawBackground()
|
|
}
|
|
|
|
func (k *Keyboard) GetKeys() [][]*Key {
|
|
return k.keys
|
|
}
|
|
|
|
func (k *Keyboard) SetKeys(keys [][]*Key) {
|
|
k.keys = keys
|
|
|
|
k.updateKeyRects()
|
|
k.drawBackground()
|
|
}
|
|
|
|
func (k *Keyboard) updateKeyRects() {
|
|
for row, rowKeys := range k.keys {
|
|
for i, key := range rowKeys {
|
|
key.x = 50 * i
|
|
key.y = 50 * row
|
|
key.w = 40
|
|
key.h = 40
|
|
}
|
|
}
|
|
}
|
|
|
|
func (k *Keyboard) at(x, y int) *Key {
|
|
if !k.visible {
|
|
return nil
|
|
}
|
|
if x >= k.x && x <= k.x+k.w && y >= k.y && y <= k.y+k.h {
|
|
x, y = x-k.x, y-k.y // Offset
|
|
for _, rowKeys := range k.keys {
|
|
for _, key := range rowKeys {
|
|
if x >= key.x && x <= key.x+key.w && y >= key.y && y <= key.y+key.h {
|
|
return key
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (k *Keyboard) Hit(key *Key) {
|
|
// TODO lower or upper
|
|
k.inputBuffer <- key.LowerInput
|
|
}
|
|
|
|
// Update handles user input.
|
|
func (k *Keyboard) Update() error {
|
|
if !k.visible {
|
|
return nil
|
|
}
|
|
|
|
// Pass through physical keyboard input
|
|
if k.passPhysical {
|
|
// Read input characters
|
|
k.internalBuffer = ebiten.AppendInputChars(k.internalBuffer[:0])
|
|
if len(k.internalBuffer) > 0 {
|
|
for _, r := range k.internalBuffer {
|
|
k.inputBuffer <- &Input{Rune: r} // Pass through
|
|
}
|
|
}
|
|
// Hide widget
|
|
if k.allowUserHide && inpututil.IsKeyJustPressed(ebiten.KeyEscape) {
|
|
// TODO allow customizing the close key
|
|
k.Hide()
|
|
}
|
|
// TODO handle other keys
|
|
}
|
|
// Handle mouse input
|
|
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
|
|
x, y := ebiten.CursorPosition()
|
|
key := k.at(x, y)
|
|
if key != nil {
|
|
k.Hit(key)
|
|
}
|
|
}
|
|
// TODO handle touch input
|
|
return nil
|
|
}
|
|
|
|
func (k *Keyboard) offset(x, y int) (int, int) {
|
|
return x + k.x, y + k.y
|
|
}
|
|
|
|
func (k *Keyboard) drawBackground() {
|
|
if k.w == 0 || k.h == 0 {
|
|
return
|
|
}
|
|
|
|
debugBox := image.NewRGBA(image.Rect(0, 0, k.w, k.h))
|
|
buffer := ebiten.NewImageFromImage(debugBox)
|
|
buffer.Fill(color.White)
|
|
|
|
for _, rowKeys := range k.keys {
|
|
for _, key := range rowKeys {
|
|
labelImg := ebiten.NewImageFromImage(image.NewRGBA(image.Rect(0, 0, key.w, key.h)))
|
|
ebitenutil.DebugPrint(labelImg, key.LowerLabel)
|
|
img := ebiten.NewImageFromImage(image.NewRGBA(image.Rect(0, 0, key.w, key.h)))
|
|
img.Fill(color.RGBA{150, 150, 150, 255})
|
|
k.op.GeoM.Reset()
|
|
k.op.GeoM.Scale(2, 2)
|
|
if len(key.LowerLabel) <= 1 {
|
|
k.op.GeoM.Translate(float64(12), 0)
|
|
}
|
|
img.DrawImage(labelImg, k.op)
|
|
|
|
k.op.GeoM.Reset()
|
|
k.op.GeoM.Translate(float64(key.x), float64(key.y))
|
|
buffer.DrawImage(img, k.op)
|
|
}
|
|
}
|
|
|
|
k.background = buffer
|
|
}
|
|
|
|
// Draw draws the widget on the provided image.
|
|
func (k *Keyboard) Draw(target *ebiten.Image) {
|
|
if !k.visible {
|
|
return
|
|
}
|
|
|
|
k.op.GeoM.Reset()
|
|
k.op.GeoM.Translate(float64(k.x), float64(k.y))
|
|
k.op.ColorM.Scale(1, 1, 1, k.alpha)
|
|
target.DrawImage(k.background, k.op)
|
|
k.op.ColorM.Reset()
|
|
}
|
|
|
|
// SetAllowUserHide sets a flag that controls whether the widget may be hidden
|
|
// by the user. The input buffer channel is closed when the widget is hidden.
|
|
func (k *Keyboard) SetAllowUserHide(allow bool) {
|
|
k.allowUserHide = allow
|
|
}
|
|
|
|
// SetPassThroughPhysicalInput sets a flag that controls whether physical
|
|
// keyboard input is passed through to the widget's input buffer. This is not
|
|
// enabled by default.
|
|
func (k *Keyboard) SetPassThroughPhysicalInput(pass bool) {
|
|
k.passPhysical = pass
|
|
}
|
|
|
|
// SetAlpha sets the transparency level of the widget on a scale of 0 to 1.0.
|
|
func (k *Keyboard) SetAlpha(alpha float64) {
|
|
k.alpha = alpha
|
|
}
|
|
|
|
// Show shows the widget and begins sending input to the provided channel. The
|
|
// channel is closed if the widget is hidden.
|
|
func (k *Keyboard) Show(inputBuffer chan *Input) {
|
|
if k.inputBuffer != nil {
|
|
close(k.inputBuffer)
|
|
}
|
|
k.inputBuffer = inputBuffer
|
|
k.visible = true
|
|
}
|
|
|
|
// Hide hides the widget and closes the input buffer channel.
|
|
func (k *Keyboard) Hide() {
|
|
if k.inputBuffer != nil {
|
|
close(k.inputBuffer)
|
|
k.inputBuffer = nil
|
|
}
|
|
k.visible = false
|
|
}
|