doodle/ui/supervisor.go

98 lines
2.0 KiB
Go
Raw Normal View History

package ui
import (
"sync"
"git.kirsle.net/apps/doodle/events"
"git.kirsle.net/apps/doodle/render"
)
// 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()
}