render/canvas/events.go

220 lines
4.1 KiB
Go

package canvas
import (
"syscall/js"
"git.kirsle.net/go/render/event"
)
// EventClass to categorize JavaScript events.
type EventClass int
// EventClass values.
const (
MouseEvent EventClass = iota
ClickEvent
KeyEvent
ResizeEvent
WindowEvent
)
// Event object queues up asynchronous JavaScript events to be processed linearly.
type Event struct {
Name string // mouseup, keydown, etc.
Class EventClass
// Mouse events.
X int
Y int
LeftClick bool
RightClick bool
// Key events.
KeyName string
State bool
Repeat bool
}
// AddEventListeners sets up bindings to collect events from the browser.
func (e *Engine) AddEventListeners() {
// Window resize.
js.Global().Get("window").Call(
"addEventListener",
"resize",
js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e.queue <- Event{
Name: "resize",
Class: WindowEvent,
}
return nil
}),
)
// Mouse movement.
e.canvas.Value.Call(
"addEventListener",
"mousemove",
js.FuncOf(func(this js.Value, args []js.Value) interface{} {
var (
x = args[0].Get("pageX").Int()
y = args[0].Get("pageY").Int()
)
e.queue <- Event{
Name: "mousemove",
Class: MouseEvent,
X: x,
Y: y,
}
return nil
}),
)
// Mouse clicks.
for _, ev := range []string{"mouseup", "mousedown"} {
ev := ev
e.canvas.Value.Call(
"addEventListener",
ev,
js.FuncOf(func(this js.Value, args []js.Value) interface{} {
var (
x = args[0].Get("pageX").Int()
y = args[0].Get("pageY").Int()
which = args[0].Get("which").Int()
)
// Is a mouse button pressed down?
checkDown := func(number int) bool {
if which == number {
return ev == "mousedown"
}
return false
}
e.queue <- Event{
Name: ev,
Class: ClickEvent,
X: x,
Y: y,
LeftClick: checkDown(1),
RightClick: checkDown(3),
}
return false
}),
)
}
// Supress context menu.
e.canvas.Value.Call(
"addEventListener",
"contextmenu",
js.FuncOf(func(this js.Value, args []js.Value) interface{} {
args[0].Call("preventDefault")
return false
}),
)
// Keyboard keys
for _, ev := range []string{"keydown", "keyup"} {
ev := ev
js.Global().Get("document").Call(
"addEventListener",
ev,
js.FuncOf(func(this js.Value, args []js.Value) interface{} {
var (
event = args[0]
key = event.Get("key").String()
repeat = event.Get("repeat").Bool()
pressed = ev == "keydown"
)
if key == "F3" {
args[0].Call("preventDefault")
}
e.queue <- Event{
Name: ev,
Class: KeyEvent,
KeyName: key,
Repeat: repeat,
State: pressed,
}
return nil
}),
)
}
}
// PollEvent returns the next event in the queue, or null.
func (e *Engine) PollEvent() *Event {
select {
case event := <-e.queue:
return &event
default:
return nil
}
return nil
}
// Poll for events.
func (e *Engine) Poll() (*event.State, error) {
s := e.events
// Reset some event states.
s.WindowResized = false
for event := e.PollEvent(); event != nil; event = e.PollEvent() {
switch event.Class {
case WindowEvent:
s.WindowResized = true
case MouseEvent:
s.CursorX = event.X
s.CursorY = event.Y
case ClickEvent:
s.CursorX = event.X
s.CursorY = event.Y
s.Button1 = event.LeftClick
s.Button2 = event.RightClick
case KeyEvent:
switch event.KeyName {
case "Escape":
if event.Repeat {
continue
}
s.Escape = event.State
case "Enter":
if event.Repeat {
continue
}
s.Enter = event.State
case "F3":
s.SetKeyDown("F3", event.State)
case "ArrowUp":
s.Up = event.State
case "ArrowLeft":
s.Left = event.State
case "ArrowRight":
s.Right = event.State
case "ArrowDown":
s.Down = event.State
case "Shift":
s.Shift = event.State
continue
case "Alt":
s.Alt = event.State
case "Control":
s.Ctrl = event.State
case "Backspace":
s.SetKeyDown(`\b`, event.State)
default:
s.SetKeyDown(event.KeyName, event.State)
}
}
}
return e.events, nil
}