|
|
|
@ -29,7 +29,8 @@ type Keyboard struct {
@@ -29,7 +29,8 @@ type Keyboard struct {
|
|
|
|
|
|
|
|
|
|
keys [][]*Key |
|
|
|
|
|
|
|
|
|
background *ebiten.Image |
|
|
|
|
backgroundLower *ebiten.Image |
|
|
|
|
backgroundUpper *ebiten.Image |
|
|
|
|
backgroundDirty bool |
|
|
|
|
|
|
|
|
|
op *ebiten.DrawImageOptions |
|
|
|
@ -46,7 +47,7 @@ type Keyboard struct {
@@ -46,7 +47,7 @@ type Keyboard struct {
|
|
|
|
|
labelFont font.Face |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// NewKeyboard returns a new Keyboard.
|
|
|
|
|
// NewKeyboard returns a new Keyboard widget.
|
|
|
|
|
func NewKeyboard() *Keyboard { |
|
|
|
|
fontFace, err := defaultFontFace(64) |
|
|
|
|
if err != nil { |
|
|
|
@ -58,7 +59,9 @@ func NewKeyboard() *Keyboard {
@@ -58,7 +59,9 @@ func NewKeyboard() *Keyboard {
|
|
|
|
|
op: &ebiten.DrawImageOptions{ |
|
|
|
|
Filter: ebiten.FilterNearest, |
|
|
|
|
}, |
|
|
|
|
background: ebiten.NewImage(1, 1), |
|
|
|
|
keys: KeysQWERTY, |
|
|
|
|
backgroundLower: ebiten.NewImage(1, 1), |
|
|
|
|
backgroundUpper: ebiten.NewImage(1, 1), |
|
|
|
|
backgroundColor: color.Black, |
|
|
|
|
labelFont: fontFace, |
|
|
|
|
} |
|
|
|
@ -93,10 +96,12 @@ func (k *Keyboard) SetRect(x, y, w, h int) {
@@ -93,10 +96,12 @@ func (k *Keyboard) SetRect(x, y, w, h int) {
|
|
|
|
|
k.backgroundDirty = true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// GetKeys returns the keys of the keyboard.
|
|
|
|
|
func (k *Keyboard) GetKeys() [][]*Key { |
|
|
|
|
return k.keys |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// SetKeys sets the keys of the keyboard.
|
|
|
|
|
func (k *Keyboard) SetKeys(keys [][]*Key) { |
|
|
|
|
k.keys = keys |
|
|
|
|
|
|
|
|
@ -104,12 +109,15 @@ func (k *Keyboard) SetKeys(keys [][]*Key) {
@@ -104,12 +109,15 @@ func (k *Keyboard) SetKeys(keys [][]*Key) {
|
|
|
|
|
k.backgroundDirty = true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// SetLabelFont sets the key label font.
|
|
|
|
|
func (k *Keyboard) SetLabelFont(face font.Face) { |
|
|
|
|
k.labelFont = face |
|
|
|
|
|
|
|
|
|
k.backgroundDirty = true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// SetHideShortcuts sets the key shortcuts which, when pressed, will hide the
|
|
|
|
|
// keyboard.
|
|
|
|
|
func (k *Keyboard) SetHideShortcuts(shortcuts []ebiten.Key) { |
|
|
|
|
k.hideShortcuts = shortcuts |
|
|
|
|
} |
|
|
|
@ -126,8 +134,9 @@ func (k *Keyboard) updateKeyRects() {
@@ -126,8 +134,9 @@ func (k *Keyboard) updateKeyRects() {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
cellPaddingW := 7 |
|
|
|
|
cellPaddingH := 7 |
|
|
|
|
// TODO user configurable
|
|
|
|
|
cellPaddingW := 2 |
|
|
|
|
cellPaddingH := 2 |
|
|
|
|
|
|
|
|
|
cellH := (k.h - (cellPaddingH * (len(k.keys) - 1))) / len(k.keys) |
|
|
|
|
|
|
|
|
@ -186,6 +195,7 @@ func (k *Keyboard) handleHideKey(inputKey ebiten.Key) bool {
@@ -186,6 +195,7 @@ func (k *Keyboard) handleHideKey(inputKey ebiten.Key) bool {
|
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Hit handles a key press.
|
|
|
|
|
func (k *Keyboard) Hit(key *Key) { |
|
|
|
|
input := key.LowerInput |
|
|
|
|
if k.shift { |
|
|
|
@ -194,7 +204,7 @@ func (k *Keyboard) Hit(key *Key) {
@@ -194,7 +204,7 @@ func (k *Keyboard) Hit(key *Key) {
|
|
|
|
|
|
|
|
|
|
if input.Key == ebiten.KeyShift { |
|
|
|
|
k.shift = !k.shift |
|
|
|
|
k.backgroundDirty = true |
|
|
|
|
ebiten.ScheduleFrame() |
|
|
|
|
return |
|
|
|
|
} else if k.handleHideKey(input.Key) { |
|
|
|
|
// Hidden
|
|
|
|
@ -204,7 +214,7 @@ func (k *Keyboard) Hit(key *Key) {
@@ -204,7 +214,7 @@ func (k *Keyboard) Hit(key *Key) {
|
|
|
|
|
k.inputBuffer <- input |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Update handles user input.
|
|
|
|
|
// Update handles user input. This function is called by Ebiten.
|
|
|
|
|
func (k *Keyboard) Update() error { |
|
|
|
|
if !k.visible { |
|
|
|
|
return nil |
|
|
|
@ -232,6 +242,7 @@ func (k *Keyboard) Update() error {
@@ -232,6 +242,7 @@ func (k *Keyboard) Update() error {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Handle mouse input
|
|
|
|
|
pressDuration := 50 * time.Millisecond |
|
|
|
|
if inpututil.IsMouseButtonJustReleased(ebiten.MouseButtonLeft) { |
|
|
|
|
x, y := ebiten.CursorPosition() |
|
|
|
|
|
|
|
|
@ -248,7 +259,7 @@ func (k *Keyboard) Update() error {
@@ -248,7 +259,7 @@ func (k *Keyboard) Update() error {
|
|
|
|
|
|
|
|
|
|
// TODO replace with pressUntil
|
|
|
|
|
go func() { |
|
|
|
|
time.Sleep(50 * time.Millisecond) |
|
|
|
|
time.Sleep(pressDuration) |
|
|
|
|
|
|
|
|
|
key.pressed = false |
|
|
|
|
ebiten.ScheduleFrame() |
|
|
|
@ -260,6 +271,15 @@ func (k *Keyboard) Update() error {
@@ -260,6 +271,15 @@ func (k *Keyboard) Update() error {
|
|
|
|
|
key := k.at(x, y) |
|
|
|
|
if key != nil { |
|
|
|
|
key.pressed = true |
|
|
|
|
|
|
|
|
|
for _, rowKeys := range k.keys { |
|
|
|
|
for _, rowKey := range rowKeys { |
|
|
|
|
if rowKey == key || !rowKey.pressed { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
rowKey.pressed = false |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Handle touch input
|
|
|
|
@ -268,17 +288,47 @@ func (k *Keyboard) Update() error {
@@ -268,17 +288,47 @@ func (k *Keyboard) Update() error {
|
|
|
|
|
x, y := ebiten.TouchPosition(id) |
|
|
|
|
|
|
|
|
|
key := k.at(x, y) |
|
|
|
|
if key != nil { |
|
|
|
|
if key != nil && !key.pressed { |
|
|
|
|
key.pressed = true |
|
|
|
|
key.pressedTouchID = id |
|
|
|
|
|
|
|
|
|
for _, rowKeys := range k.keys { |
|
|
|
|
for _, rowKey := range rowKeys { |
|
|
|
|
if rowKey != key && rowKey.pressed { |
|
|
|
|
rowKey.pressed = false |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
k.Hit(key) |
|
|
|
|
|
|
|
|
|
// TODO replace with pressUntil
|
|
|
|
|
go func() { |
|
|
|
|
time.Sleep(50 * time.Millisecond) |
|
|
|
|
var touchIDs []ebiten.TouchID |
|
|
|
|
t := time.NewTicker(pressDuration) |
|
|
|
|
for range t.C { |
|
|
|
|
touchIDs = ebiten.AppendTouchIDs(touchIDs[:0]) |
|
|
|
|
|
|
|
|
|
var found bool |
|
|
|
|
for _, touchID := range touchIDs { |
|
|
|
|
if id == touchID { |
|
|
|
|
found = true |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
key.pressed = false |
|
|
|
|
ebiten.ScheduleFrame() |
|
|
|
|
if found { |
|
|
|
|
tx, ty := ebiten.TouchPosition(id) |
|
|
|
|
if tx != 0 || ty != 0 { |
|
|
|
|
x, y = tx, ty |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if !found { |
|
|
|
|
key.pressed = false |
|
|
|
|
ebiten.ScheduleFrame() |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -294,44 +344,63 @@ func (k *Keyboard) drawBackground() {
@@ -294,44 +344,63 @@ func (k *Keyboard) drawBackground() {
|
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if k.background == nil || k.background.Bounds() != image.Rect(0, 0, k.w, k.h) || k.backgroundColor != k.lastBackgroundColor { |
|
|
|
|
k.background = ebiten.NewImageFromImage(image.NewRGBA(image.Rect(0, 0, k.w, k.h))) |
|
|
|
|
k.background.Fill(k.backgroundColor) |
|
|
|
|
if k.backgroundLower.Bounds() != image.Rect(0, 0, k.w, k.h) || k.backgroundUpper.Bounds() != image.Rect(0, 0, k.w, k.h) || k.backgroundColor != k.lastBackgroundColor { |
|
|
|
|
k.backgroundLower = ebiten.NewImage(k.w, k.h) |
|
|
|
|
k.backgroundLower.Fill(k.backgroundColor) |
|
|
|
|
|
|
|
|
|
k.backgroundUpper = ebiten.NewImage(k.w, k.h) |
|
|
|
|
k.backgroundUpper.Fill(k.backgroundColor) |
|
|
|
|
|
|
|
|
|
k.lastBackgroundColor = k.backgroundColor |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for _, rowKeys := range k.keys { |
|
|
|
|
for _, key := range rowKeys { |
|
|
|
|
label := key.LowerLabel |
|
|
|
|
if k.shift { |
|
|
|
|
label = key.UpperLabel |
|
|
|
|
} |
|
|
|
|
var img *ebiten.Image |
|
|
|
|
for i := 0; i < 2; i++ { |
|
|
|
|
shift := i == 1 |
|
|
|
|
for _, rowKeys := range k.keys { |
|
|
|
|
for _, key := range rowKeys { |
|
|
|
|
if img == nil { |
|
|
|
|
img = ebiten.NewImage(key.w, key.h) |
|
|
|
|
} else { |
|
|
|
|
bounds := img.Bounds() |
|
|
|
|
if bounds.Dx() != key.w || bounds.Dy() != key.h { |
|
|
|
|
img = ebiten.NewImage(key.w, key.h) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Draw key background
|
|
|
|
|
img := ebiten.NewImageFromImage(image.NewRGBA(image.Rect(0, 0, key.w, key.h))) |
|
|
|
|
img.Fill(color.RGBA{100, 100, 100, 255}) |
|
|
|
|
label := key.LowerLabel |
|
|
|
|
if shift { |
|
|
|
|
label = key.UpperLabel |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Draw key label
|
|
|
|
|
bounds := text.BoundString(k.labelFont, label) |
|
|
|
|
x := (key.w - bounds.Dx()) / 2 |
|
|
|
|
if x < 0 { |
|
|
|
|
x = 0 |
|
|
|
|
} |
|
|
|
|
y := key.h / 2 |
|
|
|
|
text.Draw(img, label, k.labelFont, x, y, color.White) |
|
|
|
|
// Draw key background
|
|
|
|
|
img.Fill(color.RGBA{100, 100, 100, 255}) |
|
|
|
|
|
|
|
|
|
// Draw key
|
|
|
|
|
k.op.GeoM.Reset() |
|
|
|
|
k.op.GeoM.Translate(float64(key.x), float64(key.y)) |
|
|
|
|
k.background.DrawImage(img, k.op) |
|
|
|
|
// Draw key label
|
|
|
|
|
bounds := text.BoundString(k.labelFont, label) |
|
|
|
|
x := (key.w - bounds.Dx()) / 2 |
|
|
|
|
if x < 0 { |
|
|
|
|
x = 0 |
|
|
|
|
} |
|
|
|
|
y := key.h / 2 |
|
|
|
|
text.Draw(img, label, k.labelFont, x, y, color.White) |
|
|
|
|
|
|
|
|
|
k.op.ColorM.Reset() |
|
|
|
|
// Draw key
|
|
|
|
|
k.op.GeoM.Reset() |
|
|
|
|
k.op.GeoM.Translate(float64(key.x), float64(key.y)) |
|
|
|
|
|
|
|
|
|
if !shift { |
|
|
|
|
k.backgroundLower.DrawImage(img, k.op) |
|
|
|
|
} else { |
|
|
|
|
k.backgroundUpper.DrawImage(img, k.op) |
|
|
|
|
} |
|
|
|
|
k.op.ColorM.Reset() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Draw draws the widget on the provided image.
|
|
|
|
|
// Draw draws the widget on the provided image. This function is called by Ebiten.
|
|
|
|
|
func (k *Keyboard) Draw(target *ebiten.Image) { |
|
|
|
|
if !k.visible { |
|
|
|
|
return |
|
|
|
@ -342,10 +411,17 @@ func (k *Keyboard) Draw(target *ebiten.Image) {
@@ -342,10 +411,17 @@ func (k *Keyboard) Draw(target *ebiten.Image) {
|
|
|
|
|
k.backgroundDirty = false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var background *ebiten.Image |
|
|
|
|
if !k.shift { |
|
|
|
|
background = k.backgroundLower |
|
|
|
|
} else { |
|
|
|
|
background = k.backgroundUpper |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
target.DrawImage(background, k.op) |
|
|
|
|
k.op.ColorM.Reset() |
|
|
|
|
|
|
|
|
|
// Draw pressed keys
|
|
|
|
@ -360,7 +436,7 @@ func (k *Keyboard) Draw(target *ebiten.Image) {
@@ -360,7 +436,7 @@ func (k *Keyboard) Draw(target *ebiten.Image) {
|
|
|
|
|
k.op.GeoM.Translate(float64(k.x+key.x), float64(k.y+key.y)) |
|
|
|
|
k.op.ColorM.Scale(0.75, 0.75, 0.75, k.alpha) |
|
|
|
|
|
|
|
|
|
target.DrawImage(k.background.SubImage(image.Rect(key.x, key.y, key.x+key.w, key.y+key.h)).(*ebiten.Image), k.op) |
|
|
|
|
target.DrawImage(background.SubImage(image.Rect(key.x, key.y, key.x+key.w, key.y+key.h)).(*ebiten.Image), k.op) |
|
|
|
|
k.op.ColorM.Reset() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -394,6 +470,7 @@ func (k *Keyboard) Show(inputBuffer chan *Input) {
@@ -394,6 +470,7 @@ func (k *Keyboard) Show(inputBuffer chan *Input) {
|
|
|
|
|
k.visible = true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Visible returns whether the keyboard is currently shown.
|
|
|
|
|
func (k *Keyboard) Visible() bool { |
|
|
|
|
return k.visible |
|
|
|
|
} |
|
|
|
|