417 lines
14 KiB
Go
417 lines
14 KiB
Go
package shell
|
|
|
|
import (
|
|
"aego/core/ids"
|
|
"aego/editor/core/assetops"
|
|
edclip "aego/editor/core/clipboard"
|
|
"aego/editor/core/command"
|
|
"aego/editor/core/context"
|
|
"aego/editor/core/document"
|
|
"aego/editor/core/play"
|
|
"aego/editor/core/templates"
|
|
"aego/editor/core/workspace"
|
|
"aego/editor/panels/assets"
|
|
"aego/editor/panels/inspector"
|
|
"aego/editor/panels/sceneview"
|
|
"aego/platform/input"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
CommandQuit = "app.quit"
|
|
CommandCloseScene = "file.close_scene"
|
|
)
|
|
|
|
const (
|
|
CommandPlayBack = "play.back"
|
|
CommandPlayForward = "play.forward"
|
|
)
|
|
|
|
const (
|
|
CommandAssetsRename = assets.CommandRename
|
|
CommandAssetsDelete = assets.CommandDelete
|
|
CommandAssetsDuplicate = assets.CommandDuplicate
|
|
CommandAssetsUndo = assets.CommandUndo
|
|
CommandAssetsRedo = assets.CommandRedo
|
|
CommandResetLayout = "window.reset_layout"
|
|
CommandToggleOrtho = "scene.toggle_ortho"
|
|
)
|
|
|
|
func (a *App) registerCommands() error {
|
|
reg := a.ctx.Commands
|
|
ctx := a.ctx
|
|
sc := command.DefaultShortcuts()
|
|
hasDoc := func() bool { return ctx.Doc() != nil }
|
|
inAssets := func() bool { return a.ws.Focused() == workspace.Assets }
|
|
assetSel := func() bool { return inAssets() && a.assetsPanel.HasSelection() }
|
|
hasSel := func() bool { return ctx.Doc() != nil && len(ctx.Selected()) > 0 }
|
|
playing := func() bool { return a.play.State() != play.Stopped }
|
|
historyLabel := func(undo bool) func() string {
|
|
return func() string {
|
|
base := "Redo"
|
|
if undo {
|
|
base = "Undo"
|
|
}
|
|
h := ctx.History()
|
|
if h == nil {
|
|
return base
|
|
}
|
|
u, r := h.Labels(1)
|
|
l := r
|
|
if undo {
|
|
l = u
|
|
}
|
|
if len(l) > 0 {
|
|
return base + " " + l[0]
|
|
}
|
|
return base
|
|
}
|
|
}
|
|
tool := func(t uint8) func() bool { return func() bool { return ctx.Settings.Tool == t } }
|
|
list := []command.Desc{
|
|
{ID: command.FileNewScene, Name: "New Scene", Category: "File", Order: 100, Run: a.newScene},
|
|
{ID: CommandNewScene3D, Name: "New 3D Scene", Category: "File", Order: 110, Run: a.newScene3D},
|
|
{ID: command.FileOpen, Name: "Open Scene…", Category: "File", Order: 120, Run: a.openScene},
|
|
{ID: command.FileSave, Name: "Save", Category: "File", Order: 200, Enabled: hasDoc, Run: a.save},
|
|
{ID: command.FileSaveAs, Name: "Save As…", Category: "File", Order: 205, Enabled: hasDoc,
|
|
Run: func() error {
|
|
assetops.PromptSaveAs(ctx, ctx.Doc())
|
|
return nil
|
|
}},
|
|
{ID: command.FileSaveAll, Name: "Save All", Category: "File", Order: 210, Enabled: ctx.Docs.Dirty, Run: a.saveAll},
|
|
{ID: CommandCloseScene, Name: "Close Scene", Category: "File", Order: 300,
|
|
Shortcut: command.Key(input.KeyW, command.ModPrimary), Enabled: hasDoc, Run: a.closeActive},
|
|
{ID: CommandQuit, Name: "Quit", Category: "File", Order: 900,
|
|
Shortcut: command.Key(input.KeyQ, command.ModPrimary), Run: func() error {
|
|
a.requestQuit()
|
|
return nil
|
|
}},
|
|
|
|
{ID: command.EditUndo, Name: "Undo", Category: "Edit", Order: 100, Label: historyLabel(true),
|
|
Enabled: func() bool {
|
|
h := ctx.History()
|
|
return h != nil && h.CanUndo()
|
|
}, Run: ctx.Undo},
|
|
{ID: command.EditRedo, Name: "Redo", Category: "Edit", Order: 110, Label: historyLabel(false),
|
|
Enabled: func() bool {
|
|
h := ctx.History()
|
|
return h != nil && h.CanRedo()
|
|
}, Run: ctx.Redo},
|
|
{ID: command.EditCut, Name: "Cut", Category: "Edit", Order: 200, Enabled: hasSel, Run: func() error { return edclip.Cut(ctx) }},
|
|
{ID: command.EditCopy, Name: "Copy", Category: "Edit", Order: 210, Enabled: hasSel, Run: func() error { return edclip.Copy(ctx) }},
|
|
{ID: command.EditPaste, Name: "Paste", Category: "Edit", Order: 220, Enabled: hasDoc, Run: func() error { return edclip.Paste(ctx) }},
|
|
{ID: command.EditDuplicate, Name: "Duplicate", Category: "Edit", Order: 230, Enabled: hasSel, Run: func() error { return edclip.Duplicate(ctx) }},
|
|
{ID: command.EditDelete, Name: "Delete", Category: "Edit", Order: 240, Enabled: hasSel, Run: func() error { return edclip.Delete(ctx) }},
|
|
{ID: command.EditRename, Name: "Rename", Category: "Edit", Order: 300, Enabled: hasSel, Run: func() error {
|
|
if it, ok := ctx.Selection.Active(); ok {
|
|
a.ws.Show(workspace.Hierarchy)
|
|
a.hierarchy.StartRename(it.Node)
|
|
}
|
|
return nil
|
|
}},
|
|
{ID: command.EditSelectAll, Name: "Select All", Category: "Edit", Order: 310, Enabled: hasDoc, Run: a.selectAll},
|
|
|
|
{ID: command.SceneToolSelect, Name: "Select Tool", Category: "Scene", Order: 100, Checked: tool(context.ToolSelect), Run: a.setTool(context.ToolSelect)},
|
|
{ID: command.SceneToolMove, Name: "Move Tool", Category: "Scene", Order: 110, Checked: tool(context.ToolMove), Run: a.setTool(context.ToolMove)},
|
|
{ID: command.SceneToolRotate, Name: "Rotate Tool", Category: "Scene", Order: 120, Checked: tool(context.ToolRotate), Run: a.setTool(context.ToolRotate)},
|
|
{ID: command.SceneToolScale, Name: "Scale Tool", Category: "Scene", Order: 130, Checked: tool(context.ToolScale), Run: a.setTool(context.ToolScale)},
|
|
{ID: command.SceneFrame, Name: "Frame Selection", Category: "Scene", Order: 200, Enabled: hasDoc, Run: func() error {
|
|
a.ws.Show(workspace.Scene)
|
|
a.sceneView.Frame()
|
|
return nil
|
|
}},
|
|
{ID: command.SceneToggleGrid, Name: "Grid", Category: "Scene", Order: 300,
|
|
Checked: func() bool { return ctx.Settings.Grid }, Run: func() error {
|
|
ctx.Settings.Grid = !ctx.Settings.Grid
|
|
return nil
|
|
}},
|
|
{ID: command.SceneToggleSnap, Name: "Snap", Category: "Scene", Order: 310,
|
|
Checked: func() bool { return ctx.Settings.Snap }, Run: func() error {
|
|
ctx.Settings.Snap = !ctx.Settings.Snap
|
|
return nil
|
|
}},
|
|
{ID: CommandToggleOrtho, Name: "Orthographic 3D View", Category: "Scene", Order: 320, Enabled: hasDoc,
|
|
Checked: a.sceneView.Ortho, Run: func() error {
|
|
a.sceneView.ToggleOrtho()
|
|
return nil
|
|
}},
|
|
{ID: command.SceneCreateNode, Name: "Create Empty", Category: "Scene", Order: 400,
|
|
Shortcut: command.Key(input.KeyN, command.ModPrimary|command.ModShift), Enabled: hasDoc,
|
|
Run: func() error {
|
|
t, ok := a.templates.Get("engine.empty")
|
|
if !ok {
|
|
return nil
|
|
}
|
|
_, err := templates.Instantiate(ctx, t, ids.NodeID{}, -1, nil)
|
|
return err
|
|
}},
|
|
|
|
{ID: command.PlayStart, Name: "Play", Category: "Play", Order: 100,
|
|
Label: func() string {
|
|
if playing() {
|
|
return "Stop"
|
|
}
|
|
return "Play"
|
|
},
|
|
Enabled: func() bool { return playing() || (a.scriptErrors() == 0 && (a.ctx.Docs.Edit() == nil || a.canPlay())) },
|
|
Run: a.togglePlay},
|
|
{ID: command.PlayPause, Name: "Pause", Category: "Play", Order: 110, Enabled: playing,
|
|
Checked: func() bool { return a.play.State() == play.Paused },
|
|
Run: func() error {
|
|
a.play.Pause()
|
|
return nil
|
|
}},
|
|
{ID: command.PlayStep, Name: "Step Frame", Category: "Play", Order: 120,
|
|
Enabled: func() bool { return a.play.State() == play.Paused },
|
|
Run: func() error {
|
|
a.play.Step()
|
|
return nil
|
|
}},
|
|
{ID: CommandPlayBack, Name: "Step Back", Category: "Play", Order: 121,
|
|
Shortcut: command.Key(input.KeyLeft, command.ModPrimary),
|
|
Enabled: a.play.CanSeekBack,
|
|
Run: a.play.StepBack},
|
|
{ID: CommandPlayForward, Name: "Step Forward", Category: "Play", Order: 122,
|
|
Shortcut: command.Key(input.KeyRight, command.ModPrimary),
|
|
Enabled: playing,
|
|
Run: a.play.StepForward},
|
|
{ID: command.PlayStop, Name: "Stop", Category: "Play", Order: 130, Enabled: playing,
|
|
Run: func() error {
|
|
a.stopPlay()
|
|
return nil
|
|
}},
|
|
|
|
{ID: command.AssetsReimport, Name: "Reimport Asset", Category: "Assets", Order: 100,
|
|
Enabled: a.assetsPanel.HasSelection,
|
|
Run: func() error { return a.assetsPanel.ReimportSelected() }},
|
|
{ID: CommandAssetsRename, Name: "Rename Asset", Category: "Assets", Order: 110, Scope: command.ScopeAssets,
|
|
Shortcut: command.Key(input.KeyF2, 0),
|
|
Enabled: func() bool { return inAssets() && a.assetsPanel.Single() },
|
|
Run: func() error {
|
|
a.assetsPanel.StartRename()
|
|
return nil
|
|
}},
|
|
{ID: CommandAssetsDuplicate, Name: "Duplicate Asset", Category: "Assets", Order: 115, Scope: command.ScopeAssets,
|
|
Shortcut: command.Key(input.KeyD, command.ModPrimary), Enabled: assetSel,
|
|
Run: func() error { return a.assetsPanel.DuplicateSelected() }},
|
|
{ID: CommandAssetsDelete, Name: "Delete Asset", Category: "Assets", Order: 120, Scope: command.ScopeAssets,
|
|
Shortcut: command.Key(input.KeyDelete, 0), Enabled: assetSel,
|
|
Run: func() error {
|
|
a.assetsPanel.DeleteSelected()
|
|
return nil
|
|
}},
|
|
{ID: CommandAssetsUndo, Name: "Undo File Operation", Category: "Assets", Order: 200, Scope: command.ScopeAssets,
|
|
Shortcut: command.Key(input.KeyZ, command.ModPrimary),
|
|
Enabled: func() bool { return inAssets() && a.assetsPanel.CanUndo() },
|
|
Run: func() error { return a.assetsPanel.Undo() }},
|
|
{ID: CommandAssetsRedo, Name: "Redo File Operation", Category: "Assets", Order: 210, Scope: command.ScopeAssets,
|
|
Shortcut: command.Key(input.KeyZ, command.ModPrimary|command.ModShift),
|
|
Enabled: func() bool { return inAssets() && a.assetsPanel.CanRedo() },
|
|
Run: func() error { return a.assetsPanel.Redo() }},
|
|
{ID: assets.CommandCopy, Name: "Copy Asset", Category: "Assets", Order: 130, Scope: command.ScopeAssets,
|
|
Shortcut: command.Key(input.KeyC, command.ModPrimary), Enabled: assetSel,
|
|
Run: func() error { return a.assetsPanel.CopySelected() }},
|
|
{ID: assets.CommandCut, Name: "Cut Asset", Category: "Assets", Order: 135, Scope: command.ScopeAssets,
|
|
Shortcut: command.Key(input.KeyX, command.ModPrimary), Enabled: assetSel,
|
|
Run: func() error { return a.assetsPanel.CutSelected() }},
|
|
{ID: assets.CommandPaste, Name: "Paste into Folder", Category: "Assets", Order: 140, Scope: command.ScopeAssets,
|
|
Shortcut: command.Key(input.KeyV, command.ModPrimary), Enabled: inAssets,
|
|
Run: func() error { return a.assetsPanel.PasteHere() }},
|
|
{ID: sceneview.CommandPasteImage, Name: "Paste Image as Sprite", Category: "Scene", Order: 410, Scope: command.ScopeScene,
|
|
Shortcut: command.Key(input.KeyV, command.ModPrimary),
|
|
Enabled: func() bool { return a.sceneView.CanPasteImage() },
|
|
Run: func() error { return a.sceneView.PasteImage() }},
|
|
|
|
{ID: inspector.CommandCopy, Name: "Copy Field", Scope: inspector.Scope,
|
|
Shortcut: command.Key(input.KeyC, command.ModPrimary),
|
|
Enabled: a.inspectorPanel.CanCopy,
|
|
Run: func() error { return a.inspectorPanel.CopyHovered() }},
|
|
{ID: inspector.CommandPaste, Name: "Paste Field", Scope: inspector.Scope,
|
|
Shortcut: command.Key(input.KeyV, command.ModPrimary),
|
|
Enabled: hasDoc,
|
|
Run: func() error { return a.inspectorPanel.PasteHovered() }},
|
|
{ID: inspector.CommandWatch, Name: "Add Field to Watch", Scope: inspector.Scope,
|
|
Shortcut: command.Key(input.KeyW, command.ModPrimary|command.ModAlt),
|
|
Enabled: a.inspectorPanel.CanWatch,
|
|
Run: func() error { return a.inspectorPanel.WatchHovered() }},
|
|
}
|
|
for _, d := range list {
|
|
if d.Shortcut.IsZero() {
|
|
d.Shortcut = sc[d.ID]
|
|
}
|
|
if d.Scope == "" {
|
|
d.Scope = command.ScopeOf(d.ID)
|
|
}
|
|
if err := reg.Register(d); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *App) registerWindowCommands() error {
|
|
reg := a.ctx.Commands
|
|
for _, d := range a.ws.Menu() {
|
|
id := d.Panel.ID()
|
|
if _, exists := reg.Get(command.WindowPrefix + id); exists {
|
|
continue
|
|
}
|
|
if err := reg.Register(command.Desc{
|
|
ID: command.WindowPrefix + id,
|
|
Name: d.Panel.Title(),
|
|
Category: "Window",
|
|
Order: d.Order,
|
|
Icon: panelIcon(d),
|
|
Tip: "Show or hide the " + d.Panel.Title() + " panel",
|
|
Checked: func() bool { return a.ws.Visible(id) },
|
|
Run: func() error {
|
|
a.ws.Toggle(id)
|
|
return nil
|
|
},
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if _, exists := reg.Get(CommandResetLayout); exists {
|
|
return nil
|
|
}
|
|
return reg.Register(command.Desc{
|
|
ID: CommandResetLayout,
|
|
Name: "Reset Layout",
|
|
Category: "Window",
|
|
Order: 900,
|
|
Run: func() error {
|
|
a.ws.ResetLayout()
|
|
return nil
|
|
},
|
|
})
|
|
}
|
|
|
|
func (a *App) stopPlay() {
|
|
if a.play.State() == play.Stopped {
|
|
return
|
|
}
|
|
a.play.Stop()
|
|
a.ws.Show(workspace.Scene)
|
|
}
|
|
|
|
func (a *App) togglePlay() error {
|
|
if a.play.State() != play.Stopped {
|
|
a.stopPlay()
|
|
return nil
|
|
}
|
|
if a.opt.Code != nil {
|
|
a.opt.Code.FlushAutosave()
|
|
}
|
|
var err error
|
|
if a.ctx.Docs.Edit() != nil {
|
|
src, perr := a.playSource()
|
|
if perr != nil {
|
|
return perr
|
|
}
|
|
err = a.play.Start(src)
|
|
} else {
|
|
err = a.play.StartProject()
|
|
}
|
|
if err == nil {
|
|
a.ws.Show(workspace.Game)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (a *App) selectAll() error {
|
|
s := a.ctx.Scene()
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
root := s.Root()
|
|
var all []ids.NodeID
|
|
for _, h := range s.PreOrder() {
|
|
if h != root && s.IsAlive(h) {
|
|
all = append(all, s.ID(h))
|
|
}
|
|
}
|
|
a.ctx.Select(all...)
|
|
return nil
|
|
}
|
|
|
|
func (a *App) setTool(t uint8) func() error {
|
|
return func() error {
|
|
a.ctx.Settings.Tool = t
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (a *App) newScene() error {
|
|
return a.newUnsaved("scenes/untitled.aescene", false)
|
|
}
|
|
|
|
func (a *App) saveDoc(d *document.SceneDocument) error {
|
|
if strings.HasSuffix(d.Path, document.PrefabExt) {
|
|
return a.prefabs.SaveDocument(d)
|
|
}
|
|
return a.ctx.Docs.Save(d.ID)
|
|
}
|
|
|
|
func (a *App) save() error {
|
|
d := a.ctx.Doc()
|
|
if d == nil {
|
|
return nil
|
|
}
|
|
if d.IsPlay() {
|
|
d = a.ctx.Docs.Get(d.ID)
|
|
}
|
|
if err := a.saveDoc(d); err != nil {
|
|
return err
|
|
}
|
|
a.ctx.Notify(context.LevelInfo, "Saved "+d.Path)
|
|
return nil
|
|
}
|
|
|
|
func (a *App) saveAll() error {
|
|
var first error
|
|
for _, d := range a.ctx.Docs.Docs() {
|
|
if !d.Dirty() {
|
|
continue
|
|
}
|
|
if err := a.saveDoc(d); err != nil && first == nil {
|
|
first = err
|
|
}
|
|
}
|
|
for _, d := range a.ctx.Docs.Scripts() {
|
|
if !d.Dirty() {
|
|
continue
|
|
}
|
|
if err := a.ctx.Docs.SaveScript(d); err != nil && first == nil {
|
|
first = err
|
|
}
|
|
}
|
|
if first != nil {
|
|
return first
|
|
}
|
|
a.ctx.Notify(context.LevelInfo, "Saved all scenes")
|
|
return nil
|
|
}
|
|
|
|
func (a *App) closeActive() error {
|
|
d := a.ctx.Doc()
|
|
if d == nil {
|
|
return nil
|
|
}
|
|
if d.Dirty() {
|
|
a.push(modal{kind: modalClose, doc: d.ID, path: d.Path})
|
|
return nil
|
|
}
|
|
a.ctx.Selection.DropDoc(d.ID)
|
|
return a.ctx.Docs.Close(d.ID, false)
|
|
}
|
|
|
|
func panelIcon(d workspace.Desc) string {
|
|
if ic, ok := d.Panel.(workspace.Iconed); ok {
|
|
if s := ic.Icon(); s != "" {
|
|
return s
|
|
}
|
|
}
|
|
return d.Icon
|
|
}
|