doodle/pkg/editor_ui_menubar.go

400 lines
10 KiB
Go

package doodle
// Menu Bar features for Edit Mode.
// In here is the SetupMenuBar() and menu item functions.
// The rest of it is controlled in editor_ui.go
import (
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
"git.kirsle.net/SketchyMaze/doodle/pkg/drawtool"
"git.kirsle.net/SketchyMaze/doodle/pkg/enum"
"git.kirsle.net/SketchyMaze/doodle/pkg/level/giant_screenshot"
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
"git.kirsle.net/SketchyMaze/doodle/pkg/modal"
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
"git.kirsle.net/SketchyMaze/doodle/pkg/userdir"
"git.kirsle.net/SketchyMaze/doodle/pkg/windows"
"git.kirsle.net/go/render"
"git.kirsle.net/go/ui"
)
// SetupMenuBar sets up the menu bar.
func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
menu := ui.NewMenuBar("Main Menu")
// Save and Save As common menu handler
var (
saveFunc func(filename string)
)
switch u.Scene.DrawingType {
case enum.LevelDrawing:
saveFunc = func(filename string) {
if err := u.Scene.SaveLevel(filename); err != nil {
d.FlashError("Error: %s", err)
} else {
d.Flash("Saved level: %s", filename)
}
}
case enum.DoodadDrawing:
saveFunc = func(filename string) {
if err := u.Scene.SaveDoodad(filename); err != nil {
d.FlashError("Error: %s", err)
} else {
d.Flash("Saved doodad: %s", filename)
}
}
default:
d.FlashError("Error: Scene.DrawingType is not a valid type")
}
////////
// File menu
fileMenu := menu.AddMenu("File")
fileMenu.AddItemAccel("New level", "Ctrl-N", u.Scene.MenuNewLevel)
fileMenu.AddItem("New doodad", u.Scene.MenuNewDoodad)
fileMenu.AddItemAccel("Save", "Ctrl-S", u.Scene.MenuSave(false))
fileMenu.AddItemAccel("Save as...", "Shift-Ctrl-S", func() {
d.Prompt("Save as filename>", func(answer string) {
if answer != "" {
saveFunc(answer)
}
})
})
fileMenu.AddItemAccel("Open...", "Ctrl-O", u.Scene.MenuOpen)
fileMenu.AddSeparator()
fileMenu.AddItem("Exit to menu", func() {
u.Scene.ConfirmUnload(func() {
d.Goto(&MainScene{})
})
})
fileMenu.AddItemAccel("Quit", "Escape", func() {
d.ConfirmExit()
})
////////
// Edit menu
editMenu := menu.AddMenu("Edit")
editMenu.AddItemAccel("Undo", "Ctrl-Z", func() {
u.Canvas.UndoStroke()
})
editMenu.AddItemAccel("Redo", "Ctrl-Y", func() {
u.Canvas.RedoStroke()
})
editMenu.AddSeparator()
editMenu.AddItem("Settings", func() {
if u.settingsWindow == nil {
u.settingsWindow = d.MakeSettingsWindow(u.Supervisor)
}
u.settingsWindow.Show()
})
////////
// Level menu
if u.Scene.DrawingType == enum.LevelDrawing {
levelMenu := menu.AddMenu("Level")
levelMenu.AddItem("Level Properties", func() {
log.Info("Opening the window")
// Open the New Level window in edit-settings mode.
u.levelSettingsWindow.Hide()
u.levelSettingsWindow = nil
u.SetupPopups(u.d)
u.levelSettingsWindow.Show()
})
levelMenu.AddItem("Attached files", func() {
log.Info("Opening the FileSystem window")
u.OpenFileSystemWindow()
})
levelMenu.AddItemAccel("Playtest", "P", func() {
u.Scene.Playtest()
})
if balance.DPP {
levelMenu.AddItem("Publish", func() {
u.OpenPublishWindow()
})
}
levelMenu.AddSeparator()
levelMenu.AddItem("Screenshot", func() {
// It takes a LONG TIME to render for medium+ maps.
// Do so on a background thread.
go func() {
filename, err := giant_screenshot.SaveCroppedScreenshot(u.Scene.Level, u.Scene.GetDrawing().Viewport())
if err != nil {
d.FlashError("Error: %s", err.Error())
return
}
d.FlashError("Screenshot saved as: %s", filename)
}()
})
levelMenu.AddItem("Giant Screenshot", func() {
// It takes a LONG TIME to render for medium+ maps.
modal.Confirm(
"Do you want to make a 'Giant Screenshot' of\n" +
"your WHOLE level? Note: this may take several\n" +
"seconds for very large maps!",
).WithTitle("Giant Screenshot").Then(func() {
// Show the wait modal and generate the screenshot on a background thread.
m := modal.Wait("Generating a giant screenshot...").WithTitle("Please hold")
go func() {
defer m.Dismiss(true)
filename, err := giant_screenshot.SaveGiantScreenshot(u.Scene.Level)
if err != nil {
d.FlashError("Error: %s", err.Error())
return
}
d.FlashError("Giant screenshot saved as: %s", filename)
}()
})
})
levelMenu.AddItem("Open screenshot folder", func() {
native.OpenLocalURL(userdir.ScreenshotDirectory)
})
if balance.Feature.ViewportWindow {
levelMenu.AddSeparator()
levelMenu.AddItemAccel("New viewport", "v", func() {
pip := windows.MakePiPWindow(d.width, d.height, windows.PiP{
Supervisor: u.Supervisor,
Engine: u.d.Engine,
Level: u.Scene.Level,
Event: u.d.event,
Tool: &u.Scene.UI.Canvas.Tool,
BrushSize: &u.Scene.UI.Canvas.BrushSize,
})
pip.Show()
})
}
}
////////
// Doodad Menu
if u.Scene.DrawingType == enum.DoodadDrawing {
levelMenu := menu.AddMenu("Doodad")
levelMenu.AddItem("Doodad Properties", func() {
log.Info("Opening the window")
// Open the New Level window in edit-settings mode.
u.doodadPropertiesWindow.Hide()
u.doodadPropertiesWindow = nil
u.SetupPopups(u.d)
u.doodadPropertiesWindow.Show()
})
levelMenu.AddItem("Layers", func() {
u.OpenLayersWindow()
})
}
////////
// View menu
viewMenu := menu.AddMenu("View")
viewMenu.AddItemAccel("Zoom in", "+", func() {
u.Canvas.Zoom++
})
viewMenu.AddItemAccel("Zoom out", "-", func() {
u.Canvas.Zoom--
})
viewMenu.AddItemAccel("Reset zoom", "1", func() {
u.Canvas.Zoom = 0
})
viewMenu.AddItemAccel("Scroll drawing to origin", "0", func() {
u.Canvas.ScrollTo(render.Origin)
})
viewMenu.AddSeparator()
viewMenu.AddItemAccel("Close window", "←", func() {
u.Supervisor.CloseActiveWindow()
})
viewMenu.AddItemAccel("Close all windows", "Shift-←", func() {
u.Supervisor.CloseAllWindows()
})
////////
// Tools menu
toolMenu := menu.AddMenu("Tools")
toolMenu.AddItemAccel("Debug overlay", "F3", func() {
DebugOverlay = !DebugOverlay
if DebugOverlay {
d.Flash("Debug overlay enabled. Press F3 to turn it off.")
}
})
toolMenu.AddItemAccel("Command shell", "`", func() {
d.shell.Open = true
})
toolMenu.AddSeparator()
toolMenu.AddItem("Edit Palette", func() {
u.OpenPaletteWindow()
})
// Draw Tools
toolMenu.AddItemAccel("Pencil Tool", "F", func() {
u.Canvas.Tool = drawtool.PencilTool
u.activeTool = u.Canvas.Tool.String()
d.Flash("Pencil Tool selected.")
})
toolMenu.AddItemAccel("Line Tool", "L", func() {
u.Canvas.Tool = drawtool.LineTool
u.activeTool = u.Canvas.Tool.String()
d.Flash("Line Tool selected.")
})
toolMenu.AddItemAccel("Rectangle Tool", "R", func() {
u.Canvas.Tool = drawtool.RectTool
u.activeTool = u.Canvas.Tool.String()
d.Flash("Rectangle Tool selected.")
})
toolMenu.AddItemAccel("Ellipse Tool", "C", func() {
u.Canvas.Tool = drawtool.EllipseTool
u.activeTool = u.Canvas.Tool.String()
d.Flash("Ellipse Tool selected.")
})
toolMenu.AddItemAccel("Eraser Tool", "x", func() {
u.Canvas.Tool = drawtool.EraserTool
u.activeTool = u.Canvas.Tool.String()
d.Flash("Eraser Tool selected.")
})
if u.Scene.DrawingType == enum.LevelDrawing {
toolMenu.AddItemAccel("Doodads", "q", func() {
log.Info("Open the DoodadDropper")
u.OpenDoodadDropper()
})
toolMenu.AddItem("Link Tool", func() {
u.Canvas.Tool = drawtool.LinkTool
u.activeTool = u.Canvas.Tool.String()
d.Flash("Link Tool selected. Click a doodad in your level to link it to another.")
})
}
////////
// Help menu
var (
helpMenu = u.d.MakeHelpMenu(menu, u.Supervisor)
)
// Registration item for Doodle++ builds.
if balance.DPP {
var registerText = "Register"
if dpp.Driver.IsRegistered() {
registerText = "Registration"
}
helpMenu.AddSeparator()
helpMenu.AddItem(registerText, func() {
u.licenseWindow.Show()
u.Supervisor.FocusWindow(u.licenseWindow)
})
}
menu.Supervise(u.Supervisor)
menu.Compute(d.Engine)
return menu
}
// Menu functions that have keybind callbacks below.
// File->New level, or Ctrl-N
func (s *EditorScene) MenuNewLevel() {
s.ConfirmUnload(func() {
s.d.GotoNewMenu()
})
}
func (s *EditorScene) MenuNewDoodad() {
s.ConfirmUnload(func() {
// New doodad size with prompt.
s.d.GotoNewDoodadMenu()
})
}
// File->Open, or Ctrl-O
func (s *EditorScene) MenuOpen() {
s.ConfirmUnload(func() {
if s.winOpenLevel == nil {
s.winOpenLevel = windows.NewOpenDrawingWindow(windows.OpenDrawing{
Supervisor: s.UI.Supervisor,
Engine: shmem.CurrentRenderEngine,
OnOpenDrawing: func(filename string) {
s.d.EditFile(filename)
},
OnCloseWindow: func() {
s.winOpenLevel.Destroy()
s.winOpenLevel = nil
},
})
}
s.winOpenLevel.MoveTo(render.Point{
X: (s.d.width / 2) - (s.winOpenLevel.Size().W / 2),
Y: (s.d.height / 2) - (s.winOpenLevel.Size().H / 2),
})
s.winOpenLevel.Show()
})
}
// File->Save, or Ctrl-S
// File->Save As, or Shift-Ctrl-S
// NOTICE: this one returns a func() so you need to call that one!
func (s *EditorScene) MenuSave(as bool) func() {
return func() {
var (
// drawingType string
saveFunc func(filename string)
)
switch s.DrawingType {
case enum.LevelDrawing:
// drawingType = "level"
saveFunc = func(filename string) {
if err := s.SaveLevel(filename); err != nil {
s.d.FlashError("Error: %s", err)
} else {
s.d.Flash("Saved level: %s", filename)
}
}
case enum.DoodadDrawing:
// drawingType = "doodad"
saveFunc = func(filename string) {
if err := s.SaveDoodad(filename); err != nil {
s.d.FlashError("Error: %s", err)
} else {
s.d.Flash("Saved doodad: %s", filename)
}
}
default:
s.d.FlashError("Error: Scene.DrawingType is not a valid type")
}
// "Save As"?
if as {
s.d.Prompt("Save as filename>", func(answer string) {
if answer != "" {
saveFunc(answer)
}
})
return
}
// "Save", write to existing filename or prompt for it.
if s.filename != "" {
saveFunc(s.filename)
} else {
s.d.Prompt("Save filename>", func(answer string) {
if answer != "" {
saveFunc(answer)
}
})
}
}
}