2020-11-16 02:02:35 +00:00
|
|
|
// Package modal provides UI pop-up modals for Doodle.
|
|
|
|
package modal
|
|
|
|
|
|
|
|
import (
|
2022-09-24 22:17:25 +00:00
|
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
|
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/cursor"
|
|
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/keybind"
|
|
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/modal/loadscreen"
|
|
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
|
2020-11-16 02:02:35 +00:00
|
|
|
"git.kirsle.net/go/render"
|
|
|
|
"git.kirsle.net/go/render/event"
|
|
|
|
"git.kirsle.net/go/ui"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Package global variables.
|
|
|
|
var (
|
|
|
|
ready bool // Has been initialized with a render.Engine
|
|
|
|
current *Modal // Current modal object, nil if no modal active.
|
|
|
|
engine render.Engine
|
|
|
|
window render.Rect // cached window dimensions
|
|
|
|
|
|
|
|
supervisor *ui.Supervisor
|
|
|
|
screen *ui.Frame
|
|
|
|
)
|
|
|
|
|
|
|
|
// Initialize the modal package.
|
|
|
|
func Initialize(e render.Engine) {
|
|
|
|
engine = e
|
|
|
|
supervisor = ui.NewSupervisor()
|
|
|
|
|
|
|
|
width, height := engine.WindowSize()
|
|
|
|
window = render.NewRect(width, height)
|
|
|
|
|
|
|
|
screen = ui.NewFrame("Modal Screen")
|
2020-11-17 07:20:24 +00:00
|
|
|
screen.SetBackground(balance.ModalBackdrop)
|
2020-11-16 02:02:35 +00:00
|
|
|
screen.Resize(window)
|
|
|
|
screen.Compute(e)
|
|
|
|
|
|
|
|
ready = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset the modal state (closing all modals).
|
|
|
|
func Reset() {
|
|
|
|
supervisor = nil
|
|
|
|
current = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handled runs the modal manager's logic. Returns true if a modal
|
|
|
|
// is presently active, to signal to Doodle not to run game logic.
|
2021-07-19 03:04:24 +00:00
|
|
|
//
|
|
|
|
// This function also returns true if the pkg/modal/loadscreen is
|
|
|
|
// currently active.
|
2020-11-16 02:02:35 +00:00
|
|
|
func Handled(ev *event.State) bool {
|
2021-07-19 03:04:24 +00:00
|
|
|
// The loadscreen counts as a modal for this purpose.
|
|
|
|
if loadscreen.IsActive() {
|
2022-03-06 20:07:59 +00:00
|
|
|
// Pass in window resize events in case the user maximizes the window during loading.
|
|
|
|
if ev.WindowResized {
|
|
|
|
loadscreen.Resized()
|
|
|
|
}
|
2021-07-19 03:04:24 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we have a modal currently active.
|
2020-11-16 02:02:35 +00:00
|
|
|
if !ready || current == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
WIP Publish Dialog + UI Improvements
* File->Publish Level in the Level Editor opens the Publish window,
where you can embed custom doodads into your level and export a
portable .level file you can share with others.
* Currently does not actually export a level file yet.
* The dialog lists all unique doodad names in use in your level, and
designates which are built-ins and which are custom (paginated).
* A checkbox would let the user embed built-in doodads into their level,
as well, locking it in to those versions and not using updated
versions from future game releases.
UI Improvements:
* Added styling for a "Primary" UI button, rendered in deep blue.
* Pop-up modals (Alert, Confirm) color their Ok button as Primary.
* The Enter key pressed during an Alert or Confirm modal will invoke its
default button and close the modal, corresponding to its Primary
button.
* The developer console is now opened with the tilde/grave key ` instead
of the Enter key, so that the Enter key is free to click through
modals.
* In the "Open/Edit Drawing" window, a "Browse..." button is added to
the level and doodad sections, spawning a native File Open dialog to
pick a .level or .doodad outside the config root.
2021-06-11 05:31:30 +00:00
|
|
|
// Enter key submits the default button.
|
|
|
|
if keybind.Enter(ev) {
|
|
|
|
current.Dismiss(true)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2022-02-21 21:09:51 +00:00
|
|
|
// Escape key cancels the modal.
|
|
|
|
if keybind.Shutdown(ev) && current.cancelable {
|
|
|
|
current.Dismiss(false)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-11-16 02:02:35 +00:00
|
|
|
supervisor.Loop(ev)
|
|
|
|
|
|
|
|
// Has the window changed size?
|
|
|
|
size := render.NewRect(engine.WindowSize())
|
|
|
|
if size != window {
|
|
|
|
window = size
|
|
|
|
screen.Resize(window)
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw the modal UI to the screen.
|
|
|
|
func Draw() {
|
|
|
|
if ready && current != nil {
|
2022-05-08 03:18:44 +00:00
|
|
|
cursor.Current = cursor.NewPointer(engine)
|
2020-11-16 02:02:35 +00:00
|
|
|
screen.Present(engine, render.Origin)
|
|
|
|
supervisor.Present(engine)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Center the window on screen.
|
|
|
|
func center(win *ui.Window) {
|
2021-12-31 00:31:45 +00:00
|
|
|
var (
|
|
|
|
modSize = win.Size()
|
|
|
|
width, height = shmem.CurrentRenderEngine.WindowSize()
|
|
|
|
)
|
2020-11-16 02:02:35 +00:00
|
|
|
var moveTo = render.Point{
|
2021-12-31 00:31:45 +00:00
|
|
|
X: (width / 2) - (modSize.W / 2),
|
|
|
|
Y: (height / 4) - (modSize.H / 2),
|
2020-11-16 02:02:35 +00:00
|
|
|
}
|
|
|
|
win.MoveTo(moveTo)
|
|
|
|
|
|
|
|
// HACK: ideally the modal should auto-size itself, but currently
|
|
|
|
// the body of the window juts out the right and bottom side by
|
|
|
|
// a few pixels. Fix the underlying problem later, for now we
|
|
|
|
// set the modal size to big enough to hide the problem.
|
|
|
|
win.Children()[0].Resize(render.NewRect(modSize.W+12, modSize.H+12))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Modal is an instance of a modal, i.e. Alert or Confirm.
|
|
|
|
type Modal struct {
|
2022-02-21 21:09:51 +00:00
|
|
|
title string
|
|
|
|
message string
|
|
|
|
window *ui.Window
|
|
|
|
callback func()
|
|
|
|
cancelable bool // Escape key can cancel the modal
|
2020-11-16 02:02:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// WithTitle sets the title of the modal.
|
|
|
|
func (m *Modal) WithTitle(title string) *Modal {
|
|
|
|
m.title = title
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then calls a function after the modal is answered.
|
|
|
|
func (m *Modal) Then(f func()) *Modal {
|
|
|
|
m.callback = f
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dismiss the modal and optionally call the callback function.
|
|
|
|
func (m *Modal) Dismiss(call bool) {
|
2022-03-27 18:51:14 +00:00
|
|
|
Reset()
|
2020-11-16 02:02:35 +00:00
|
|
|
if call && m.callback != nil {
|
|
|
|
m.callback()
|
|
|
|
}
|
|
|
|
}
|