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.
This commit is contained in:
parent
546b5705db
commit
73421d27f2
|
@ -29,6 +29,7 @@ var (
|
||||||
CheatPlayAsBird = "fly like a bird"
|
CheatPlayAsBird = "fly like a bird"
|
||||||
CheatGodMode = "god mode"
|
CheatGodMode = "god mode"
|
||||||
CheatDebugLoadScreen = "test load screen"
|
CheatDebugLoadScreen = "test load screen"
|
||||||
|
CheatDebugWaitScreen = "test wait screen"
|
||||||
CheatUnlockLevels = "master key"
|
CheatUnlockLevels = "master key"
|
||||||
CheatSkipLevel = "warp whistle"
|
CheatSkipLevel = "warp whistle"
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
||||||
|
"git.kirsle.net/SketchyMaze/doodle/pkg/modal"
|
||||||
"git.kirsle.net/SketchyMaze/doodle/pkg/modal/loadscreen"
|
"git.kirsle.net/SketchyMaze/doodle/pkg/modal/loadscreen"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -168,6 +169,15 @@ func (c Command) cheatCommand(d *Doodle) bool {
|
||||||
loadscreen.Hide()
|
loadscreen.Hide()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
case balance.CheatDebugWaitScreen:
|
||||||
|
m := modal.Wait("Crunching some numbers...").WithTitle("Please hold").Then(func() {
|
||||||
|
d.Flash("Wait modal dismissed.")
|
||||||
|
})
|
||||||
|
go func() {
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
m.Dismiss(true)
|
||||||
|
}()
|
||||||
|
|
||||||
case balance.CheatUnlockLevels:
|
case balance.CheatUnlockLevels:
|
||||||
balance.CheatEnabledUnlockLevels = !balance.CheatEnabledUnlockLevels
|
balance.CheatEnabledUnlockLevels = !balance.CheatEnabledUnlockLevels
|
||||||
if balance.CheatEnabledUnlockLevels {
|
if balance.CheatEnabledUnlockLevels {
|
||||||
|
|
|
@ -66,13 +66,13 @@ func Handled(ev *event.State) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enter key submits the default button.
|
// Enter key submits the default button.
|
||||||
if keybind.Enter(ev) {
|
if keybind.Enter(ev) && !current.force {
|
||||||
current.Dismiss(true)
|
current.Dismiss(true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Escape key cancels the modal.
|
// Escape key cancels the modal.
|
||||||
if keybind.Shutdown(ev) && current.cancelable {
|
if keybind.Shutdown(ev) && current.cancelable && !current.force {
|
||||||
current.Dismiss(false)
|
current.Dismiss(false)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -124,6 +124,8 @@ type Modal struct {
|
||||||
window *ui.Window
|
window *ui.Window
|
||||||
callback func()
|
callback func()
|
||||||
cancelable bool // Escape key can cancel the modal
|
cancelable bool // Escape key can cancel the modal
|
||||||
|
force bool // Enter key can not close the modal (e.g. Wait)
|
||||||
|
teardown func() // Optional teardown logic a modal can attach.
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTitle sets the title of the modal.
|
// WithTitle sets the title of the modal.
|
||||||
|
@ -144,4 +146,8 @@ func (m *Modal) Dismiss(call bool) {
|
||||||
if call && m.callback != nil {
|
if call && m.callback != nil {
|
||||||
m.callback()
|
m.callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.teardown != nil {
|
||||||
|
m.teardown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
140
pkg/modal/wait.go
Normal file
140
pkg/modal/wait.go
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user