doodle/ui/supervisor.go

114 lines
2.1 KiB
Go

package ui
import (
"sync"
"git.kirsle.net/apps/doodle/events"
"git.kirsle.net/apps/doodle/render"
)
// Event is a named event that the supervisor will send.
type Event int
// Events.
const (
NullEvent Event = iota
MouseOver
MouseOut
MouseDown
MouseUp
Click
KeyDown
KeyUp
KeyPress
)
// Supervisor keeps track of widgets of interest to notify them about
// interaction events such as mouse hovers and clicks in their general
// vicinity.
type Supervisor struct {
lock sync.RWMutex
widgets []Widget
hovering map[int]interface{}
clicked map[int]interface{}
}
// NewSupervisor creates a supervisor.
func NewSupervisor() *Supervisor {
return &Supervisor{
widgets: []Widget{},
hovering: map[int]interface{}{},
clicked: map[int]interface{}{},
}
}
// Loop to check events and pass them to managed widgets.
func (s *Supervisor) Loop(ev *events.State) {
var (
XY = render.Point{
X: ev.CursorX.Now,
Y: ev.CursorY.Now,
}
)
// See if we are hovering over any widgets.
for id, w := range s.widgets {
var (
P = w.Point()
S = w.Size()
P2 = render.Point{
X: P.X + S.W,
Y: P.Y + S.H,
}
)
if XY.X >= P.X && XY.X <= P2.X && XY.Y >= P.Y && XY.Y <= P2.Y {
// Cursor has intersected the widget.
if _, ok := s.hovering[id]; !ok {
w.Event(MouseOver, XY)
s.hovering[id] = nil
}
_, isClicked := s.clicked[id]
if ev.Button1.Now {
if !isClicked {
w.Event(MouseDown, XY)
s.clicked[id] = nil
}
} else if isClicked {
w.Event(MouseUp, XY)
w.Event(Click, XY)
delete(s.clicked, id)
}
} else {
// Cursor is not intersecting the widget.
if _, ok := s.hovering[id]; ok {
w.Event(MouseOut, XY)
delete(s.hovering, id)
}
if _, ok := s.clicked[id]; ok {
w.Event(MouseUp, XY)
delete(s.clicked, id)
}
}
}
}
// Present all widgets managed by the supervisor.
func (s *Supervisor) Present(e render.Engine) {
s.lock.RLock()
defer s.lock.RUnlock()
for _, w := range s.widgets {
w.Present(e, w.Point())
}
}
// Add a widget to be supervised.
func (s *Supervisor) Add(w Widget) {
s.lock.Lock()
s.widgets = append(s.widgets, w)
s.lock.Unlock()
}