doodle/pkg/modal/wait.go
Noah Petherbridge 73421d27f2 Wait Modal
* Add modal.Wait() that creates a global progress bar modal which is not
  dismissable by the user; the caller must Dismiss() the modal
  themselves when ready.
* It will be useful in the future in case e.g. saving a Level needs to
  take a while to rebalance chunks and the modal prevents ALL
  interaction with the game so the user can't further modify the level
  while it's busy refactoring itself.
* Cheat code: "test wait screen" to show the Wait modal for 10 seconds.
2022-09-24 18:39:02 -07:00

141 lines
2.7 KiB
Go

package modal
import (
"fmt"
"time"
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
"git.kirsle.net/go/render"
"git.kirsle.net/go/ui"
)
// Wait pops up a non-dismissable modal that the caller can close when they're ready.
func Wait(message string, args ...interface{}) *Modal {
if !ready {
panic("modal.Wait(): not ready")
} else if current != nil {
current.Dismiss(false)
}
// Reset the supervisor.
supervisor = ui.NewSupervisor()
m := &Modal{
title: "Wait",
message: fmt.Sprintf(message, args...),
force: true,
}
m.window = makeWaitModal(m)
center(m.window)
current = m
return m
}
// creates the ui.Window for the Wait modal.
func makeWaitModal(m *Modal) *ui.Window {
win := ui.NewWindow("Wait")
_, title := win.TitleBar()
title.TextVariable = &m.title
msgFrame := ui.NewFrame("Wait Message")
win.Pack(msgFrame, ui.Pack{
Side: ui.N,
})
msg := ui.NewLabel(ui.Label{
TextVariable: &m.message,
Font: balance.UIFont,
})
msgFrame.Pack(msg, ui.Pack{
Side: ui.N,
})
// Create a bouncing progress bar.
var (
trough *ui.Frame
troughW = 250
progressBar *ui.Frame
progressX int
progressW = 64
progressH = 30
progressSpeed = 8
progressFreq = 16 * time.Millisecond
)
trough = ui.NewFrame("Progress Trough")
trough.Configure(ui.Config{
Width: troughW,
Height: progressH,
BorderSize: 1,
BorderStyle: ui.BorderSunken,
Background: render.Grey,
})
win.Pack(trough, ui.Pack{
Side: ui.N,
Padding: 4,
})
progressBar = ui.NewFrame("Progress Bar")
progressBar.Configure(ui.Config{
Width: progressW,
Height: 30,
Background: render.Green,
})
trough.Place(progressBar, ui.Place{
Left: progressX,
Top: 0,
})
trough.Compute(engine)
win.Compute(engine)
win.Supervise(supervisor)
// Animate the bouncing of the progress bar in a background goroutine,
// and allow canceling it when the modal is dismissed.
var (
cancel = make(chan interface{})
ping = time.NewTicker(progressFreq)
)
go func() {
for {
select {
case <-cancel:
ping.Stop()
return
case <-ping.C:
// Have room to move the progress bar?
progressX += progressSpeed
// Cap it to within bounds.
if progressX+progressW >= troughW {
progressX = troughW - progressW
if progressSpeed > 0 {
progressSpeed *= -1
}
} else if progressX < 0 {
progressX = 0
if progressSpeed < 0 {
progressSpeed *= -1
}
}
trough.Place(progressBar, ui.Place{
Left: progressX,
Top: 0,
})
trough.Compute(engine)
}
}
}()
// Cancel the goroutine on modal teardown.
m.teardown = func() {
cancel <- nil
}
return win
}