Noah Petherbridge
73421d27f2
* 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.
141 lines
2.7 KiB
Go
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
|
|
}
|