Noah Petherbridge
b67c4b67b2
* Add a tab bar to the top of the Palette window that has two radiobuttons for "Palette" and "Doodads" * UI: add the concept of a Hidden() widget and the corresponding Hide() and Show() methods. Hidden widgets are skipped over when evaluating Frame packing, rendering, and event supervision. * The Palette Window in editor mode now displays one of two tabs: * Palette: the old color swatch palette now lives here. * Doodads: the new Doodad palette. * The Doodad Palette shows a grid of buttons (2 per row) showing the available Doodad drawings in the user's config folder. * The Doodad buttons act as radiobuttons for now and have no other effect. TODO will be making them react to drag-drop events. * UI: added a `Children()` method as the inverse of `Parent()` for container widgets (like Frame, Window and Button) to expose their children. The BaseWidget just returns an empty []Widget. * Console: added a `repl` command that keeps the dev console open and prefixes every command with `$` filled out -- for rapid JavaScript console evaluation.
120 lines
2.3 KiB
Go
120 lines
2.3 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 {
|
|
if w.Hidden() {
|
|
// TODO: somehow the Supervisor wasn't triggering hidden widgets
|
|
// anyway, but I don't know why. Adding this check for safety.
|
|
continue
|
|
}
|
|
|
|
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()
|
|
}
|