Initial commit
commit
b5d14e1830
@ -0,0 +1,2 @@
|
||||
.idea
|
||||
*.sh
|
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Trevor Slocum <trevor@rocketnine.space>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -0,0 +1,15 @@
|
||||
# kibodo
|
||||
[](https://docs.rocketnine.space/code.rocketnine.space/tslocum/kibodo)
|
||||
[](https://liberapay.com/rocketnine.space)
|
||||
|
||||
On-screen keyboard widget for [ebiten](https://github.com/hajimehoshi/ebiten)
|
||||
|
||||
**Currently in pre-alpha state. Here be dragons.**
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available via [godoc](https://docs.rocketnine.space/code.rocketnine.space/tslocum/kibodo).
|
||||
|
||||
## Support
|
||||
|
||||
Please share issues and suggestions [here](https://code.rocketnine.space/tslocum/kibodo/issues).
|
@ -0,0 +1,82 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
)
|
||||
import "code.rocketnine.space/tslocum/kibodo"
|
||||
|
||||
type game struct {
|
||||
w, h int
|
||||
|
||||
k *kibodo.Keyboard
|
||||
|
||||
userInput []byte
|
||||
|
||||
op *ebiten.DrawImageOptions
|
||||
}
|
||||
|
||||
// NewDemoGame returns a new kibodo demo game.
|
||||
func NewDemoGame() *game {
|
||||
k := kibodo.NewKeyboard()
|
||||
k.SetAllowUserHide(true)
|
||||
k.SetPassThroughPhysicalInput(true)
|
||||
k.SetKeys(kibodo.KeysQWERTY)
|
||||
|
||||
g := &game{
|
||||
k: k,
|
||||
op: &ebiten.DrawImageOptions{
|
||||
Filter: ebiten.FilterNearest,
|
||||
},
|
||||
}
|
||||
|
||||
ch := make(chan *kibodo.Input, 10)
|
||||
k.Show(ch)
|
||||
|
||||
go func() {
|
||||
for input := range ch {
|
||||
if input.Rune > 0 {
|
||||
g.userInput = append(g.userInput, []byte(string(input.Rune))...)
|
||||
continue
|
||||
}
|
||||
g.userInput = append(g.userInput, []byte("<"+input.Key.String()+">")...)
|
||||
}
|
||||
g.userInput = []byte("(Hidden, click to show)")
|
||||
}()
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||
s := ebiten.DeviceScaleFactor()
|
||||
outsideWidth, outsideHeight = int(float64(outsideWidth)*s), int(float64(outsideHeight)*s)
|
||||
if g.w == outsideWidth && g.h == outsideHeight {
|
||||
return outsideWidth, outsideHeight
|
||||
}
|
||||
|
||||
g.w, g.h = outsideWidth, outsideHeight
|
||||
|
||||
sizeH := 200
|
||||
g.k.SetRect(0, sizeH, g.w, g.h-sizeH)
|
||||
|
||||
return outsideWidth, outsideHeight
|
||||
}
|
||||
|
||||
func (g *game) Update() error {
|
||||
return g.k.Update()
|
||||
}
|
||||
|
||||
func (g *game) Draw(screen *ebiten.Image) {
|
||||
g.k.Draw(screen)
|
||||
|
||||
debugBox := image.NewRGBA(image.Rect(10, 20, 200, 200))
|
||||
debugImg := ebiten.NewImageFromImage(debugBox)
|
||||
ebitenutil.DebugPrint(debugImg, fmt.Sprintf("FPS %0.0f - TPS %0.0f\n\n%s", ebiten.CurrentFPS(), ebiten.CurrentTPS(), g.userInput))
|
||||
g.op.GeoM.Reset()
|
||||
g.op.GeoM.Translate(3, 0)
|
||||
g.op.GeoM.Scale(2, 2)
|
||||
screen.DrawImage(debugImg, g.op)
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package mobile
|
||||
|
||||
import (
|
||||
"code.rocketnine.space/tslocum/kibodo/demos/kibodo/game"
|
||||
"github.com/hajimehoshi/ebiten/v2/mobile"
|
||||
)
|
||||
|
||||
func init() {
|
||||
mobile.SetGame(game.NewDemoGame())
|
||||
}
|
||||
|
||||
// Dummy is a dummy exported function.
|
||||
//
|
||||
// gomobile will only compile packages that include at least one exported function.
|
||||
// Dummy forces gomobile to compile this package.
|
||||
func Dummy() {}
|
@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"code.rocketnine.space/tslocum/kibodo/demos/kibodo/game"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ebiten.SetWindowTitle("キーボード")
|
||||
ebiten.SetWindowResizable(true)
|
||||
ebiten.SetMaxTPS(60)
|
||||
|
||||
g := game.NewDemoGame()
|
||||
if err := ebiten.RunGame(g); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
/*
|
||||
Package kibodo provides an on-screen keyboard widget for ebiten
|
||||
*/
|
||||
package kibodo
|
@ -0,0 +1,14 @@
|
||||
module code.rocketnine.space/tslocum/kibodo
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12.0.20210828073710-0e5dca9453a5
|
||||
|
||||
require (
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be // indirect
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
|
||||
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 // indirect
|
||||
)
|
@ -0,0 +1,59 @@
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be h1:vEIVIuBApEBQTEJt19GfhoU+zFSV+sNTa9E9FdnRYfk=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/hajimehoshi/bitmapfont/v2 v2.1.3/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12.0.20210828073710-0e5dca9453a5 h1:fUSKz2wvklV02UTmBXXDlNKc6molRGUu5O8b80AvEa4=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.2.0-alpha.12.0.20210828073710-0e5dca9453a5/go.mod h1:B4Cje+Kb1ZjztrKFPaiMWOGXO3Vp8u+zIBdxkZqkyD4=
|
||||
github.com/hajimehoshi/file2byteslice v0.0.0-20200812174855-0e5e8a80490e/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
||||
github.com/hajimehoshi/oto/v2 v2.0.0-alpha.2/go.mod h1:rUKQmwMkqmRxe+IAof9+tuYA2ofm8cAWXFmSfzDN8vQ=
|
||||
github.com/jakecoffman/cp v1.1.0/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg=
|
||||
github.com/jfreymuth/oggvorbis v1.0.3/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII=
|
||||
github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 h1:3In5TnfvnuXTF/uflgpYxSCEGP2NdYT37KsPh3VjZYU=
|
||||
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 h1:rw6UNGRMfarCepjI8qOepea/SXwIBVfTKjztZ5gBbq4=
|
||||
golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
@ -0,0 +1,27 @@
|
||||
package kibodo
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
type Key struct {
|
||||
LowerLabel string
|
||||
UpperLabel string
|
||||
LowerInput *Input
|
||||
UpperInput *Input
|
||||
|
||||
x, y int
|
||||
w, h int
|
||||
}
|
||||
|
||||
type Input struct {
|
||||
Rune rune
|
||||
Key ebiten.Key
|
||||
}
|
||||
|
||||
func (i *Input) String() string {
|
||||
if i.Rune > 0 {
|
||||
return string(i.Rune)
|
||||
}
|
||||
return i.Key.String()
|
||||
}
|
@ -0,0 +1,216 @@
|
||||
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
|
||||
}
|
@ -0,0 +1,314 @@
|
||||
package kibodo
|
||||
|
||||
import "github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
var KeysQWERTY = [][]*Key{
|
||||
{
|
||||
{
|
||||
LowerLabel: "`",
|
||||
UpperLabel: "~",
|
||||
LowerInput: &Input{Rune: '`'},
|
||||
UpperInput: &Input{Rune: '~'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "1",
|
||||
UpperLabel: "!",
|
||||
LowerInput: &Input{Rune: '1'},
|
||||
UpperInput: &Input{Rune: '!'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "2",
|
||||
UpperLabel: "@",
|
||||
LowerInput: &Input{Rune: '2'},
|
||||
UpperInput: &Input{Rune: '@'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "3",
|
||||
UpperLabel: "#",
|
||||
LowerInput: &Input{Rune: '3'},
|
||||
UpperInput: &Input{Rune: '#'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "4",
|
||||
UpperLabel: "$",
|
||||
LowerInput: &Input{Rune: '4'},
|
||||
UpperInput: &Input{Rune: '$'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "5",
|
||||
UpperLabel: "%",
|
||||
LowerInput: &Input{Rune: '5'},
|
||||
UpperInput: &Input{Rune: '%'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "6",
|
||||
UpperLabel: "^",
|
||||
LowerInput: &Input{Rune: '6'},
|
||||
UpperInput: &Input{Rune: '^'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "7",
|
||||
UpperLabel: "&",
|
||||
LowerInput: &Input{Rune: '7'},
|
||||
UpperInput: &Input{Rune: '&'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "8",
|
||||
UpperLabel: "*",
|
||||
LowerInput: &Input{Rune: '8'},
|
||||
UpperInput: &Input{Rune: '*'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "9",
|
||||
UpperLabel: "(",
|
||||
LowerInput: &Input{Rune: '9'},
|
||||
UpperInput: &Input{Rune: '('},
|
||||
},
|
||||
{
|
||||
LowerLabel: "0",
|
||||
UpperLabel: ")",
|
||||
LowerInput: &Input{Rune: '0'},
|
||||
UpperInput: &Input{Rune: ')'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "-",
|
||||
UpperLabel: "_",
|
||||
LowerInput: &Input{Rune: '-'},
|
||||
UpperInput: &Input{Rune: '_'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "=",
|
||||
UpperLabel: "+",
|
||||
LowerInput: &Input{Rune: '='},
|
||||
UpperInput: &Input{Rune: '+'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "Backspace",
|
||||
UpperLabel: "Backspace",
|
||||
LowerInput: &Input{Key: ebiten.KeyBackspace},
|
||||
UpperInput: &Input{Key: ebiten.KeyBackspace},
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
LowerLabel: "q",
|
||||
UpperLabel: "Q",
|
||||
LowerInput: &Input{Rune: 'q'},
|
||||
UpperInput: &Input{Rune: 'Q'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "w",
|
||||
UpperLabel: "W",
|
||||
LowerInput: &Input{Rune: 'w'},
|
||||
UpperInput: &Input{Rune: 'W'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "e",
|
||||
UpperLabel: "E",
|
||||
LowerInput: &Input{Rune: 'e'},
|
||||
UpperInput: &Input{Rune: 'E'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "r",
|
||||
UpperLabel: "R",
|
||||
LowerInput: &Input{Rune: 'r'},
|
||||
UpperInput: &Input{Rune: 'R'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "t",
|
||||
UpperLabel: "T",
|
||||
LowerInput: &Input{Rune: 't'},
|
||||
UpperInput: &Input{Rune: 'T'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "y",
|
||||
UpperLabel: "Y",
|
||||
LowerInput: &Input{Rune: 'y'},
|
||||
UpperInput: &Input{Rune: 'Y'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "u",
|
||||
UpperLabel: "U",
|
||||
LowerInput: &Input{Rune: 'u'},
|
||||
UpperInput: &Input{Rune: 'U'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "i",
|
||||
UpperLabel: "I",
|
||||
LowerInput: &Input{Rune: 'i'},
|
||||
UpperInput: &Input{Rune: 'I'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "o",
|
||||
UpperLabel: "O",
|
||||
LowerInput: &Input{Rune: 'o'},
|
||||
UpperInput: &Input{Rune: 'O'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "p",
|
||||
UpperLabel: "P",
|
||||
LowerInput: &Input{Rune: 'p'},
|
||||
UpperInput: &Input{Rune: 'P'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "[",
|
||||
UpperLabel: "{",
|
||||
LowerInput: &Input{Rune: '['},
|
||||
UpperInput: &Input{Rune: '{'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "]",
|
||||
UpperLabel: "}",
|
||||
LowerInput: &Input{Rune: ']'},
|
||||
UpperInput: &Input{Rune: '}'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "\\",
|
||||
UpperLabel: "|",
|
||||
LowerInput: &Input{Rune: '\\'},
|
||||
UpperInput: &Input{Rune: '|'},
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
LowerLabel: "a",
|
||||
UpperLabel: "A",
|
||||
LowerInput: &Input{Rune: 'a'},
|
||||
UpperInput: &Input{Rune: 'A'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "s",
|
||||
UpperLabel: "S",
|
||||
LowerInput: &Input{Rune: 's'},
|
||||
UpperInput: &Input{Rune: 'S'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "d",
|
||||
UpperLabel: "D",
|
||||
LowerInput: &Input{Rune: 'd'},
|
||||
UpperInput: &Input{Rune: 'D'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "f",
|
||||
UpperLabel: "F",
|
||||
LowerInput: &Input{Rune: 'f'},
|
||||
UpperInput: &Input{Rune: 'F'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "g",
|
||||
UpperLabel: "G",
|
||||
LowerInput: &Input{Rune: 'g'},
|
||||
UpperInput: &Input{Rune: 'G'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "h",
|
||||
UpperLabel: "H",
|
||||
LowerInput: &Input{Rune: 'h'},
|
||||
UpperInput: &Input{Rune: 'H'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "j",
|
||||
UpperLabel: "J",
|
||||
LowerInput: &Input{Rune: 'j'},
|
||||
UpperInput: &Input{Rune: 'J'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "k",
|
||||
UpperLabel: "K",
|
||||
LowerInput: &Input{Rune: 'k'},
|
||||
UpperInput: &Input{Rune: 'K'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "l",
|
||||
UpperLabel: "L",
|
||||
LowerInput: &Input{Rune: 'l'},
|
||||
UpperInput: &Input{Rune: 'L'},
|
||||
},
|
||||
{
|
||||
LowerLabel: ";",
|
||||
UpperLabel: ":",
|
||||
LowerInput: &Input{Rune: ';'},
|
||||
UpperInput: &Input{Rune: ':'},
|
||||
},
|
||||
{
|
||||
LowerLabel: `'`,
|
||||
UpperLabel: `"`,
|
||||
LowerInput: &Input{Rune: '\''},
|
||||
UpperInput: &Input{Rune: '"'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "Enter",
|
||||
UpperLabel: "Enter",
|
||||
LowerInput: &Input{Key: ebiten.KeyEnter},
|
||||
UpperInput: &Input{Key: ebiten.KeyEnter},
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
LowerLabel: "Shift",
|
||||
UpperLabel: "SHIFT",
|
||||
LowerInput: &Input{Key: ebiten.KeyShift},
|
||||
UpperInput: &Input{Key: ebiten.KeyShift},
|
||||
},
|
||||
{
|
||||
LowerLabel: "z",
|
||||
UpperLabel: "Z",
|
||||
LowerInput: &Input{Rune: 'z'},
|
||||
UpperInput: &Input{Rune: 'Z'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "x",
|
||||
UpperLabel: "X",
|
||||
LowerInput: &Input{Rune: 'x'},
|
||||
UpperInput: &Input{Rune: 'X'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "c",
|
||||
UpperLabel: "C",
|
||||
LowerInput: &Input{Rune: 'c'},
|
||||
UpperInput: &Input{Rune: 'C'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "v",
|
||||
UpperLabel: "V",
|
||||
LowerInput: &Input{Rune: 'v'},
|
||||
UpperInput: &Input{Rune: 'V'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "b",
|
||||
UpperLabel: "B",
|
||||
LowerInput: &Input{Rune: 'b'},
|
||||
UpperInput: &Input{Rune: 'B'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "n",
|
||||
UpperLabel: "N",
|
||||
LowerInput: &Input{Rune: 'n'},
|
||||
UpperInput: &Input{Rune: 'N'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "m",
|
||||
UpperLabel: "M",
|
||||
LowerInput: &Input{Rune: 'm'},
|
||||
UpperInput: &Input{Rune: 'M'},
|
||||
},
|
||||
{
|
||||
LowerLabel: ",",
|
||||
UpperLabel: "<",
|
||||
LowerInput: &Input{Rune: ','},
|
||||
UpperInput: &Input{Rune: '<'},
|
||||
},
|
||||
{
|
||||
LowerLabel: ".",
|
||||
UpperLabel: ">",
|
||||
LowerInput: &Input{Rune: '.'},
|
||||
UpperInput: &Input{Rune: '>'},
|
||||
},
|
||||
{
|
||||
LowerLabel: "/",
|
||||
UpperLabel: "?",
|
||||
LowerInput: &Input{Rune: '/'},
|
||||
UpperInput: &Input{Rune: '?'},
|
||||
},
|
||||
},
|
||||
}
|
Loading…
Reference in New Issue