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"
|
||||
CheatGodMode = "god mode"
|
||||
CheatDebugLoadScreen = "test load screen"
|
||||
CheatDebugWaitScreen = "test wait screen"
|
||||
CheatUnlockLevels = "master key"
|
||||
CheatSkipLevel = "warp whistle"
|
||||
)
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"time"
|
||||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/modal"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/modal/loadscreen"
|
||||
)
|
||||
|
||||
|
@ -168,6 +169,15 @@ func (c Command) cheatCommand(d *Doodle) bool {
|
|||
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:
|
||||
balance.CheatEnabledUnlockLevels = !balance.CheatEnabledUnlockLevels
|
||||
if balance.CheatEnabledUnlockLevels {
|
||||
|
|
|
@ -66,13 +66,13 @@ func Handled(ev *event.State) bool {
|
|||
}
|
||||
|
||||
// Enter key submits the default button.
|
||||
if keybind.Enter(ev) {
|
||||
if keybind.Enter(ev) && !current.force {
|
||||
current.Dismiss(true)
|
||||
return true
|
||||
}
|
||||
|
||||
// Escape key cancels the modal.
|
||||
if keybind.Shutdown(ev) && current.cancelable {
|
||||
if keybind.Shutdown(ev) && current.cancelable && !current.force {
|
||||
current.Dismiss(false)
|
||||
return true
|
||||
}
|
||||
|
@ -123,7 +123,9 @@ type Modal struct {
|
|||
message string
|
||||
window *ui.Window
|
||||
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.
|
||||
|
@ -144,4 +146,8 @@ func (m *Modal) Dismiss(call bool) {
|
|||
if call && m.callback != nil {
|
||||
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