Noah Petherbridge
7355778a39
* Refactor the events used in lib/render/sdl to be more general-purpose to make librender a stand-alone library separate from Doodle.
129 lines
2.4 KiB
Go
129 lines
2.4 KiB
Go
// +build !js
|
|
|
|
package ui
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"git.kirsle.net/apps/doodle/lib/render"
|
|
"git.kirsle.net/apps/doodle/lib/render/sdl"
|
|
)
|
|
|
|
// Target frames per second for the MainWindow to render at.
|
|
var (
|
|
FPS = 60
|
|
)
|
|
|
|
// MainWindow is the parent window of a UI application.
|
|
type MainWindow struct {
|
|
engine render.Engine
|
|
supervisor *Supervisor
|
|
frame *Frame
|
|
w int
|
|
h int
|
|
}
|
|
|
|
// NewMainWindow initializes the MainWindow. You should probably only have one
|
|
// of these per application.
|
|
func NewMainWindow(title string) (*MainWindow, error) {
|
|
mw := &MainWindow{
|
|
w: 800,
|
|
h: 600,
|
|
supervisor: NewSupervisor(),
|
|
}
|
|
|
|
mw.engine = sdl.New(
|
|
title,
|
|
mw.w,
|
|
mw.h,
|
|
)
|
|
if err := mw.engine.Setup(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Add a default frame to the window.
|
|
mw.frame = NewFrame("MainWindow Body")
|
|
mw.frame.SetBackground(render.RGBA(0, 153, 255, 100))
|
|
mw.Add(mw.frame)
|
|
|
|
// Compute initial window size.
|
|
mw.resized()
|
|
|
|
return mw, nil
|
|
}
|
|
|
|
// Add a child widget to the window.
|
|
func (mw *MainWindow) Add(w Widget) {
|
|
mw.supervisor.Add(w)
|
|
}
|
|
|
|
// Pack a child widget into the window's default frame.
|
|
func (mw *MainWindow) Pack(w Widget, pack Pack) {
|
|
mw.Add(w)
|
|
mw.frame.Pack(w, pack)
|
|
}
|
|
|
|
// resized handles the window being resized.
|
|
func (mw *MainWindow) resized() {
|
|
mw.frame.Resize(render.Rect{
|
|
W: int32(mw.w),
|
|
H: int32(mw.h),
|
|
})
|
|
}
|
|
|
|
// Present the window.
|
|
func (mw *MainWindow) Present() {
|
|
mw.supervisor.Present(mw.engine)
|
|
}
|
|
|
|
// MainLoop starts the main event loop and blocks until there's an error.
|
|
func (mw *MainWindow) MainLoop() error {
|
|
for true {
|
|
if err := mw.Loop(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Loop does one loop of the UI.
|
|
func (mw *MainWindow) Loop() error {
|
|
mw.engine.Clear(render.White)
|
|
|
|
// Record how long this loop took.
|
|
start := time.Now()
|
|
|
|
// Poll for events.
|
|
ev, err := mw.engine.Poll()
|
|
if err != nil {
|
|
return fmt.Errorf("event poll error: %s", err)
|
|
}
|
|
|
|
if ev.WindowResized {
|
|
w, h := mw.engine.WindowSize()
|
|
if w != mw.w || h != mw.h {
|
|
mw.w = w
|
|
mw.h = h
|
|
mw.resized()
|
|
}
|
|
}
|
|
|
|
mw.frame.Compute(mw.engine)
|
|
|
|
// Render the child widgets.
|
|
mw.supervisor.Present(mw.engine)
|
|
mw.engine.Present()
|
|
|
|
// Delay to maintain target frames per second.
|
|
var delay uint32
|
|
var targetFPS = 1000 / FPS
|
|
elapsed := time.Now().Sub(start) / time.Millisecond
|
|
if targetFPS-int(elapsed) > 0 {
|
|
delay = uint32(targetFPS - int(elapsed))
|
|
}
|
|
mw.engine.Delay(delay)
|
|
|
|
return nil
|
|
}
|