243 lines
5.7 KiB
Go
243 lines
5.7 KiB
Go
package shell
|
|
|
|
import (
|
|
"math"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"aego/core/mathx"
|
|
"aego/editor/core/command"
|
|
"aego/editor/core/context"
|
|
"aego/editor/core/document"
|
|
"aego/editor/core/play"
|
|
"aego/editor/core/workspace"
|
|
edocs "aego/editor/panels/docs"
|
|
"aego/editor/panels/help"
|
|
"aego/editor/panels/welcome"
|
|
"aego/engine"
|
|
"aego/engine/docs"
|
|
"aego/scene"
|
|
"aego/scene/scriptcomp"
|
|
"aego/ui/draw"
|
|
)
|
|
|
|
const (
|
|
CommandGettingStarted = "help.getting_started"
|
|
CommandReference = "help.reference"
|
|
CommandKeymap = "help.keymap"
|
|
CommandAbout = "help.about"
|
|
pulseTime = 2.5
|
|
pulseTarget = "play"
|
|
hintErrors = "hint.errors"
|
|
)
|
|
|
|
type pulse struct {
|
|
target string
|
|
until float64
|
|
}
|
|
|
|
func (a *App) registerHelp() error {
|
|
list := []command.Desc{
|
|
{ID: CommandGettingStarted, Name: "Getting Started", Category: "Help", Order: 100,
|
|
Run: func() error {
|
|
a.ws.Show(welcome.ID)
|
|
return nil
|
|
}},
|
|
{ID: CommandReference, Name: "Documentation", Category: "Help", Order: 110,
|
|
Run: func() error {
|
|
a.ctx.ShowDocs(edocs.Language, "")
|
|
return nil
|
|
}},
|
|
{ID: CommandKeymap, Name: "Keymap", Category: "Help", Order: 120,
|
|
Run: func() error {
|
|
a.ws.Show(help.KeymapID)
|
|
return nil
|
|
}},
|
|
{ID: CommandAbout, Name: "About Aego", Category: "Help", Order: 900,
|
|
Run: func() error {
|
|
a.ctx.Ask(context.Question{
|
|
Title: "About Aego",
|
|
Message: "Aego engine " + engine.Version + " · " + runtime.Version() + " · " + runtime.GOOS + "/" + runtime.GOARCH,
|
|
Buttons: []string{"Close"},
|
|
})
|
|
return nil
|
|
}},
|
|
}
|
|
for _, d := range list {
|
|
if err := a.ctx.Commands.Register(d); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
a.play.Subscribe(func(st play.State) {
|
|
if st != play.Stopped && !a.wasPlaying && a.ctx.Scripts != nil {
|
|
a.genAtPlay, _ = a.ctx.Scripts.Status()
|
|
}
|
|
a.wasPlaying = st != play.Stopped
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (a *App) highlight(target string) {
|
|
a.pulse = pulse{target: target, until: a.ui.Time + pulseTime}
|
|
}
|
|
|
|
func (a *App) showPanel(id string) {
|
|
a.ws.Show(id)
|
|
a.highlight(id)
|
|
}
|
|
|
|
func (a *App) drawPulse() {
|
|
if a.pulse.target == "" || a.ui.Time > a.pulse.until {
|
|
return
|
|
}
|
|
r := a.playRect
|
|
if a.pulse.target != pulseTarget {
|
|
pr, ok := a.ws.Tree.PanelRect(a.pulse.target)
|
|
if !ok {
|
|
return
|
|
}
|
|
r = pr
|
|
}
|
|
col := a.ui.Theme.Accent
|
|
col.A = float32(0.5 + 0.5*math.Sin(a.ui.Time*8))
|
|
l := a.ui.List()
|
|
l.PushLayer(draw.LayerTop)
|
|
l.RectOutline(mathx.Rect{X: r.X - 3, Y: r.Y - 3, W: r.W + 6, H: r.H + 6}, col, 2)
|
|
l.PopLayer()
|
|
}
|
|
|
|
func (a *App) attachedAny() bool {
|
|
for _, d := range a.ctx.Docs.Docs() {
|
|
found := false
|
|
scene.Store[scriptcomp.ScriptComponent](d.Scene()).EachAll(func(_ scene.NodeHandle, sc *scriptcomp.ScriptComponent) {
|
|
if len(sc.Attachments) > 0 {
|
|
found = true
|
|
}
|
|
})
|
|
if found {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (a *App) helpEntry(id string) (string, string) {
|
|
if a.opt.Help == nil {
|
|
return id, ""
|
|
}
|
|
e, ok := a.opt.Help.Get(id)
|
|
if !ok {
|
|
return id, ""
|
|
}
|
|
return e.Title, docs.Plain(e.Body)
|
|
}
|
|
|
|
func (a *App) welcomeChecks() []welcome.Check {
|
|
base := map[document.ID]int{}
|
|
checks := []welcome.Check{
|
|
{
|
|
ID: "node",
|
|
Done: func() bool {
|
|
d := a.ctx.Docs.Edit()
|
|
if d == nil {
|
|
return false
|
|
}
|
|
n := d.Scene().Stats().Alive
|
|
b, ok := base[d.ID]
|
|
if !ok {
|
|
base[d.ID] = n
|
|
return false
|
|
}
|
|
return n > b
|
|
},
|
|
Show: func() { a.showPanel(workspace.Hierarchy) },
|
|
},
|
|
{
|
|
ID: "attach",
|
|
Done: a.attachedAny,
|
|
Show: func() {
|
|
if a.ctx.RevealPath != nil {
|
|
a.ctx.RevealPath("scripts/hello")
|
|
}
|
|
a.showPanel(workspace.Inspector)
|
|
},
|
|
},
|
|
{
|
|
ID: "play",
|
|
Done: func() bool { return a.play.State() != play.Stopped },
|
|
Show: func() { a.highlight(pulseTarget) },
|
|
},
|
|
{
|
|
ID: "tweak",
|
|
Done: func() bool {
|
|
h := a.play.History()
|
|
return a.play.State() != play.Stopped && h != nil && h.CanUndo()
|
|
},
|
|
Show: func() { a.showPanel(workspace.Inspector) },
|
|
},
|
|
{
|
|
ID: "reload",
|
|
Done: func() bool {
|
|
if a.ctx.Scripts == nil || a.play.State() == play.Stopped {
|
|
return false
|
|
}
|
|
g, _ := a.ctx.Scripts.Status()
|
|
return g > a.genAtPlay
|
|
},
|
|
Show: func() {
|
|
if a.ctx.OpenScript != nil {
|
|
a.ctx.OpenScript(welcome.ExamplePath, 18)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
ID: "error",
|
|
Done: func() bool { return a.problemsPanel.Opened > 0 },
|
|
Show: func() {
|
|
if a.ctx.ShowProblems != nil {
|
|
a.ctx.ShowProblems("")
|
|
}
|
|
a.highlight(workspace.Problems)
|
|
},
|
|
},
|
|
}
|
|
for i := range checks {
|
|
checks[i].Title, checks[i].Hint = a.helpEntry("welcome/" + checks[i].ID)
|
|
}
|
|
return checks
|
|
}
|
|
|
|
func (a *App) firstRun() {
|
|
p := a.opt.Project
|
|
if !welcome.FirstRun(p.Root) {
|
|
return
|
|
}
|
|
if wrote, err := welcome.Example(p.ScriptsDir()); err != nil {
|
|
a.log.Warn("cannot write the example script: " + err.Error())
|
|
} else if wrote {
|
|
a.ctx.Notify(context.LevelInfo, "Created "+welcome.ExamplePath+" to get you started")
|
|
}
|
|
a.welcome.Save()
|
|
a.ws.Show(welcome.ID)
|
|
}
|
|
|
|
func (a *App) contextHint() string {
|
|
f := a.ctx.FocusedPanel
|
|
switch {
|
|
case strings.HasPrefix(f, "script:"):
|
|
if _, _, bad := a.ctx.FirstError(""); bad {
|
|
return "F2 next error · Ctrl+Q explain · Ctrl+Click go to"
|
|
}
|
|
return "Ctrl+Space completion · F1 documentation · Ctrl+Click go to"
|
|
case f == workspace.Scene:
|
|
return "Q W E R tools · F frame selection · Ctrl+D duplicate"
|
|
case f == workspace.Hierarchy:
|
|
return "F2 rename · Ctrl+D duplicate · drag to reparent"
|
|
case f == workspace.Inspector:
|
|
return "Ctrl+Click a script field to open its declaration"
|
|
case f == workspace.Assets:
|
|
return "Double-click to open · F2 rename · drop files to import"
|
|
}
|
|
return "Double Shift searches everywhere · Help → Keymap lists every shortcut"
|
|
}
|