86 lines
2.2 KiB
Go
86 lines
2.2 KiB
Go
package input
|
||
|
||
import (
|
||
"fmt"
|
||
"math"
|
||
"time"
|
||
|
||
"github.com/egoist/mygo/ui"
|
||
|
||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||
)
|
||
|
||
type JoystickOptions struct {
|
||
Disabled bool
|
||
}
|
||
|
||
type JoystickResult struct {
|
||
Element *ui.Element
|
||
// X and Y are in −1…1; Y grows downward.
|
||
X, Y float64
|
||
Moved bool
|
||
}
|
||
|
||
type stickKey struct{}
|
||
|
||
// stick is the knob and its last key nudge.
|
||
type stick struct {
|
||
x, y float64
|
||
key time.Time
|
||
}
|
||
|
||
const (
|
||
stickSize = 40
|
||
// stickReach is how far the knob travels, in dots.
|
||
stickReach = 12
|
||
)
|
||
|
||
// Joystick drags a knob in a ring; arrows nudge it.
|
||
func Joystick(c *ui.Context, text string, o JoystickOptions) JoystickResult {
|
||
k := pixel.K(c)
|
||
shell, base, st := pixel.Shell(c, func() *ui.Element {
|
||
return ui.Box(c).Focusable().Role(ui.RoleGroup).Label(text).Disabled(o.Disabled)
|
||
})
|
||
s := ui.Local(base, stickKey{}, func() stick { return stick{} })
|
||
ox, oy, now := s.x, s.y, k.Now()
|
||
for _, m := range []struct {
|
||
key ui.Key
|
||
dx, dy float64
|
||
}{{ui.KeyLeft, -0.25, 0}, {ui.KeyRight, 0.25, 0}, {ui.KeyUp, 0, -0.25}, {ui.KeyDown, 0, 0.25}} {
|
||
if base.Shortcut(0, m.key) {
|
||
s.x, s.y, s.key = s.x+m.dx, s.y+m.dy, now
|
||
}
|
||
}
|
||
_, _, held := base.Dragged()
|
||
switch {
|
||
case held && !o.Disabled:
|
||
px, py, _ := base.PointerPosition()
|
||
s.x = (float64(px/k.Dot()) - stickSize/2) / stickReach
|
||
s.y = (float64(py/k.Dot()) - stickSize/2) / stickReach
|
||
s.key = time.Time{}
|
||
case now.Sub(s.key) > repeatGap:
|
||
s.x, s.y = 0, 0
|
||
default:
|
||
k.Tick(c, 10)
|
||
}
|
||
if l := math.Hypot(s.x, s.y); l > 1 {
|
||
s.x, s.y = s.x/l, s.y/l
|
||
}
|
||
base.Description(fmt.Sprintf("%.2f, %.2f", s.x, s.y))
|
||
x, y := stickSize/2+int(math.Round(s.x*stickReach)), stickSize/2+int(math.Round(s.y*stickReach))
|
||
shell.Size(k.D(stickSize), k.D(stickSize)).Draw(func(p *ui.Painter, r ui.Rect) {
|
||
g := k.Canvas(p, r)
|
||
const m = stickSize / 2
|
||
g.Disc(m, m, m-1, pixel.Edge)
|
||
g.Disc(m, m, m-2, pixel.Well)
|
||
g.Dither(m-2, 4, 4, stickSize-8, pixel.Well, pixel.Shade, 4)
|
||
g.Dither(4, m-2, stickSize-8, 4, pixel.Well, pixel.Shade, 4)
|
||
g.Disc(x+1, y+1, 7, pixel.Shade)
|
||
g.Disc(x, y, 7, pixel.Edge)
|
||
g.Disc(x, y, 6, pixel.Accent)
|
||
g.Disc(x-2, y-2, 2, pixel.AccentLit)
|
||
finish(g, st)
|
||
})
|
||
return JoystickResult{Element: shell, X: s.x, Y: s.y, Moved: s.x != ox || s.y != oy}
|
||
}
|