Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cffa66514b | ||
|
|
2744cff481 | ||
|
|
91052a54db | ||
|
|
7f4d87ef2c |
+8
-1
File diff suppressed because one or more lines are too long
@@ -98,6 +98,7 @@ vet: bootstrap
|
||||
|
||||
lint: bootstrap
|
||||
go run honnef.co/go/tools/cmd/staticcheck@$(STATICCHECK_VERSION) $(LINT_PKGS)
|
||||
go run honnef.co/go/tools/cmd/staticcheck@$(STATICCHECK_VERSION) -tags aego_editor ./editor/... ./app/... ./tests/...
|
||||
|
||||
# All graph variants, the semantic pass and the negative rule tests.
|
||||
depcheck:
|
||||
|
||||
@@ -116,12 +116,8 @@ func TestPlayLeavesEditSceneByteIdentical(t *testing.T) {
|
||||
t.Fatal("Spin is not registered")
|
||||
}
|
||||
speed, _ := td.Field("speed")
|
||||
add := &commands.AddComponent{
|
||||
Node: spinner,
|
||||
Type: td.ID,
|
||||
Name: td.FullName,
|
||||
Values: []commands.FieldValue{{Field: speed.ID, Value: schema.Float32(3)}},
|
||||
}
|
||||
add := commands.NewMutation("Add "+td.FullName, commands.EnvFor(es),
|
||||
commands.AddComp(es, spinner, td, []commands.FieldValue{{Field: speed.ID, Value: schema.Float32(3)}}))
|
||||
if err := d.History.Exec(add); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func TestColliderFitsSprite(t *testing.T) {
|
||||
}
|
||||
for _, name := range []string{"Crate", "Bare"} {
|
||||
id := es.ID(es.Find(es.Root(), name))
|
||||
if err := d.History.Exec(&commands.AddComponent{Node: id, Type: td.ID, Name: td.FullName, OnAdd: ctx.OnAdd(td)}); err != nil {
|
||||
if err := d.History.Exec(commands.NewMutation("Add "+td.FullName, commands.WithOnAdd(commands.EnvFor(es), ctx.OnAdd), commands.AddComp(es, id, td, nil))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,12 +51,8 @@ func openSpinner(t *testing.T) *spinFixture {
|
||||
}
|
||||
fd, _ := td.Field("speed")
|
||||
f.typ, f.speed = td.ID, fd.ID
|
||||
add := &commands.AddComponent{
|
||||
Node: f.spinner,
|
||||
Type: td.ID,
|
||||
Name: td.FullName,
|
||||
Values: []commands.FieldValue{{Field: fd.ID, Value: schema.Float32(3)}},
|
||||
}
|
||||
add := commands.NewMutation("Add "+td.FullName, commands.EnvFor(es),
|
||||
commands.AddComp(es, f.spinner, td, []commands.FieldValue{{Field: fd.ID, Value: schema.Float32(3)}}))
|
||||
if err := d.History.Exec(add); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package assetops
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -12,6 +11,8 @@ import (
|
||||
"aego/core/ids"
|
||||
"aego/editor/core/context"
|
||||
"aego/editor/core/document"
|
||||
"aego/project/refactor"
|
||||
"aego/project/rename"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -19,8 +20,6 @@ const (
|
||||
opsLimit = 64
|
||||
)
|
||||
|
||||
var packageDecl = regexp.MustCompile(`(?m)^[ \t]*package[ \t]+([A-Za-z_][A-Za-z0-9_]*)`)
|
||||
|
||||
type FileOp struct {
|
||||
Dir bool
|
||||
ID ids.AssetID
|
||||
@@ -180,8 +179,8 @@ func setPackage(ctx *context.Editor, rel, pkg string) error {
|
||||
return err
|
||||
}
|
||||
c := d.Code
|
||||
if m := packageDecl.FindSubmatchIndex(c.Bytes()); m != nil {
|
||||
c.ReplaceRanges([][2]int{{m[2], m[3]}}, [][]byte{[]byte(pkg)})
|
||||
if a, b, ok := refactor.PackageSpan(c.Bytes()); ok {
|
||||
c.ReplaceRanges([][2]int{{a, b}}, [][]byte{[]byte(pkg)})
|
||||
} else {
|
||||
c.ReplaceRanges([][2]int{{0, 0}}, [][]byte{[]byte("package " + pkg + "\n\n")})
|
||||
}
|
||||
@@ -207,7 +206,7 @@ func Stem(p string) string {
|
||||
}
|
||||
|
||||
func ValidName(s string) bool {
|
||||
return s != "" && s != "." && s != ".." && !strings.HasPrefix(s, ".") && !strings.ContainsAny(s, "/\\")
|
||||
return rename.ValidName(s)
|
||||
}
|
||||
|
||||
func ValidIdent(s string) bool {
|
||||
@@ -270,11 +269,7 @@ func ScriptsRel(dir string) (string, bool) {
|
||||
}
|
||||
|
||||
func PackageOf(ctx *context.Editor, dir string) string {
|
||||
if rel, ok := ScriptsRel(dir); ok && rel != "" {
|
||||
return path.Base(rel)
|
||||
}
|
||||
m := moduleOf(ctx)
|
||||
return m[strings.LastIndexByte(m, '/')+1:]
|
||||
return refactor.PackageOf(ctx.Project, dir)
|
||||
}
|
||||
|
||||
func FileStem(name string) string {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"aego/core/ids"
|
||||
"aego/editor/core/context"
|
||||
"aego/editor/core/history/commands"
|
||||
"aego/project/refactor"
|
||||
"aego/scene/scriptcomp"
|
||||
"aego/schema"
|
||||
)
|
||||
@@ -20,10 +21,7 @@ func shortName(full string) string {
|
||||
}
|
||||
|
||||
func moduleOf(ctx *context.Editor) string {
|
||||
if ctx.Project != nil && ctx.Project.Module != "" {
|
||||
return ctx.Project.Module
|
||||
}
|
||||
return "game"
|
||||
return refactor.Module(ctx.Project)
|
||||
}
|
||||
|
||||
func ScriptPrefix(ctx *context.Editor, rel string) string {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"aego/editor/core/history/commands"
|
||||
"aego/format/aetext"
|
||||
"aego/scene"
|
||||
"aego/scene/mutate"
|
||||
"aego/scene/sceneio"
|
||||
)
|
||||
|
||||
@@ -237,12 +238,24 @@ func Paste(ctx *context.Editor) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
frags, err := commands.Fragments(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parent, index := pasteTarget(ctx)
|
||||
cmd := &commands.Create{Parent: parent, Index: index, Fragment: data, Name: "Paste"}
|
||||
ops := make([]mutate.Op, len(frags))
|
||||
for i, f := range frags {
|
||||
at := -1
|
||||
if index >= 0 {
|
||||
at = index + i
|
||||
}
|
||||
ops[i] = commands.Insert(parent, at, f)
|
||||
}
|
||||
cmd := commands.NewMutation("Create Paste", commands.EnvFor(d.Scene()), ops...)
|
||||
if err := d.History.Exec(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Select(append([]ids.NodeID(nil), cmd.Roots()...)...)
|
||||
ctx.Select(cmd.Created()...)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -267,17 +280,13 @@ func Duplicate(ctx *context.Editor) error {
|
||||
tx.Abort()
|
||||
return err
|
||||
}
|
||||
c := &commands.Create{
|
||||
Parent: s.ID(s.Parent(h)),
|
||||
Index: commands.ChildIndex(s, h) + 1,
|
||||
Fragment: data,
|
||||
Name: s.Name(h),
|
||||
}
|
||||
c := commands.NewMutation("Create "+s.Name(h), commands.EnvFor(s),
|
||||
commands.Insert(s.ID(s.Parent(h)), commands.ChildIndex(s, h)+1, string(data)))
|
||||
if err := tx.Exec(c); err != nil {
|
||||
tx.Abort()
|
||||
return err
|
||||
}
|
||||
created = append(created, c.Roots()...)
|
||||
created = append(created, c.Created()...)
|
||||
}
|
||||
tx.Commit()
|
||||
ctx.Select(created...)
|
||||
@@ -294,18 +303,13 @@ func Delete(ctx *context.Editor) error {
|
||||
if len(tops) == 0 {
|
||||
return nil
|
||||
}
|
||||
targets := make([]ids.NodeID, len(tops))
|
||||
ops := make([]mutate.Op, len(tops))
|
||||
for i, h := range tops {
|
||||
targets[i] = s.ID(h)
|
||||
ops[i] = mutate.RemoveNode(commands.Ref(s.ID(h)))
|
||||
}
|
||||
tx := d.History.Begin("Delete")
|
||||
for _, id := range targets {
|
||||
if err := tx.Exec(&commands.Delete{Node: id}); err != nil {
|
||||
tx.Abort()
|
||||
return err
|
||||
}
|
||||
if err := d.History.Exec(commands.NewMutation("Delete", commands.EnvFor(s), ops...)); err != nil {
|
||||
return err
|
||||
}
|
||||
tx.Commit()
|
||||
ctx.Select()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -113,8 +113,7 @@ func (e Env) encodeComponent(td *schema.TypeDescriptor, values []commands.FieldV
|
||||
sc := e.scratch()
|
||||
h := sc.Create(sc.Root(), "component")
|
||||
sc.Flush()
|
||||
add := &commands.AddComponent{Node: sc.ID(h), Type: td.ID, Name: td.FullName, Values: values}
|
||||
if err := add.Do(target{sc}); err != nil {
|
||||
if err := putComponent(sc, h, td, values); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sceneio.SaveSubtree(sc, h, true), nil
|
||||
@@ -122,18 +121,14 @@ func (e Env) encodeComponent(td *schema.TypeDescriptor, values []commands.FieldV
|
||||
|
||||
func (e Env) decodeComponent(data []byte) (*schema.TypeDescriptor, []commands.FieldValue, error) {
|
||||
sc := e.scratch()
|
||||
c := &commands.Create{Index: -1, Fragment: data}
|
||||
if err := c.Do(target{sc}); err != nil {
|
||||
root, err := loadScratch(sc, data)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
roots := c.Roots()
|
||||
if len(roots) == 0 {
|
||||
return nil, nil, errs.New(errs.EditorCommand, "clipboard component is empty")
|
||||
}
|
||||
reg := sc.Registry()
|
||||
unknown, hasUnknown := reg.ByName("engine.Unknown")
|
||||
var td *schema.TypeDescriptor
|
||||
sc.ComponentsOf(sc.Handle(roots[0]), func(t schema.TypeID) {
|
||||
sc.ComponentsOf(sc.Handle(root), func(t schema.TypeID) {
|
||||
if td != nil || (hasUnknown && t == unknown.ID) {
|
||||
return
|
||||
}
|
||||
@@ -142,7 +137,7 @@ func (e Env) decodeComponent(data []byte) (*schema.TypeDescriptor, []commands.Fi
|
||||
if td == nil {
|
||||
return nil, nil, errs.New(errs.EditorCommand, "clipboard component type is not loaded in this editor")
|
||||
}
|
||||
values, err := componentValues(sc, roots[0], td)
|
||||
values, err := componentValues(sc, root, td)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -189,13 +184,6 @@ func (e Env) ReadComponent() (*schema.TypeDescriptor, []commands.FieldValue, boo
|
||||
return td, values, true
|
||||
}
|
||||
|
||||
func (e Env) onAdd(td *schema.TypeDescriptor) func(*scene.Scene, scene.NodeHandle) {
|
||||
if e.OnAdd == nil {
|
||||
return nil
|
||||
}
|
||||
return e.OnAdd(td)
|
||||
}
|
||||
|
||||
func (e Env) PasteComponent(d *document.SceneDocument, nodes []ids.NodeID) error {
|
||||
td, values, ok := e.ReadComponent()
|
||||
if !ok {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package clipboard
|
||||
|
||||
import (
|
||||
"aego/core/errs"
|
||||
"aego/core/ids"
|
||||
"aego/editor/core/history/commands"
|
||||
"aego/scene"
|
||||
"aego/scene/sceneio"
|
||||
"aego/schema"
|
||||
)
|
||||
|
||||
func loadScratch(sc *scene.Scene, data []byte) (ids.NodeID, error) {
|
||||
root := sc.Root()
|
||||
created, _, err := sceneio.LoadInto(sc, root, data, "fragment", sceneio.LoadOptions{KeepIDs: true})
|
||||
if err != nil {
|
||||
return ids.NodeID{}, err
|
||||
}
|
||||
for _, h := range created {
|
||||
if sc.Parent(h) == root {
|
||||
return sc.ID(h), nil
|
||||
}
|
||||
}
|
||||
return ids.NodeID{}, errs.New(errs.EditorCommand, "the fragment holds no node")
|
||||
}
|
||||
|
||||
func putComponent(sc *scene.Scene, h scene.NodeHandle, td *schema.TypeDescriptor, list []commands.FieldValue) error {
|
||||
return commands.NewMutation("", commands.EnvFor(sc), commands.AddRaw(sc, sc.ID(h), td, list)).Do(target{sc})
|
||||
}
|
||||
@@ -22,6 +22,7 @@ type compData struct {
|
||||
|
||||
func (e Env) pasteInto(tx *history.Transaction, d *document.SceneDocument, nodes []ids.NodeID, td *schema.TypeDescriptor, values []commands.FieldValue) error {
|
||||
s := d.Scene()
|
||||
env := commands.WithOnAdd(commands.EnvFor(s), e.OnAdd)
|
||||
for _, n := range nodes {
|
||||
h := s.Handle(n)
|
||||
if h.IsZero() || !s.IsAlive(h) {
|
||||
@@ -31,7 +32,7 @@ func (e Env) pasteInto(tx *history.Transaction, d *document.SceneDocument, nodes
|
||||
if !s.Accepts(h, td.ID) {
|
||||
continue
|
||||
}
|
||||
if err := tx.Exec(&commands.AddComponent{Node: n, Type: td.ID, Name: td.FullName, Values: values, OnAdd: e.onAdd(td)}); err != nil {
|
||||
if err := tx.Exec(commands.NewMutation("Add "+td.FullName, env, commands.AddComp(s, n, td, values))); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
@@ -94,8 +95,7 @@ func (e Env) CopyComponents(s *scene.Scene, node ids.NodeID, types []schema.Type
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
add := &commands.AddComponent{Node: sc.ID(h), Type: td.ID, Name: td.FullName, Values: values}
|
||||
if err := add.Do(target{sc}); err != nil {
|
||||
if err := putComponent(sc, h, td, values); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -118,11 +118,10 @@ func (e Env) readComponents() ([]compData, bool) {
|
||||
data = []byte(strings.TrimPrefix(t, componentsText))
|
||||
}
|
||||
sc := e.scratch()
|
||||
cr := &commands.Create{Index: -1, Fragment: data}
|
||||
if err := cr.Do(target{sc}); err != nil || len(cr.Roots()) == 0 {
|
||||
root, err := loadScratch(sc, data)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
root := cr.Roots()[0]
|
||||
reg := sc.Registry()
|
||||
unknown, hasUnknown := reg.ByName("engine.Unknown")
|
||||
var out []compData
|
||||
@@ -186,14 +185,11 @@ func (e Env) EncodeScript(s *scene.Scene, node ids.NodeID, name string) ([]byte,
|
||||
|
||||
func (e Env) decodeScript(data []byte) (string, schema.PropertyBag, error) {
|
||||
sc := e.scratch()
|
||||
cr := &commands.Create{Index: -1, Fragment: data}
|
||||
if err := cr.Do(target{sc}); err != nil {
|
||||
root, err := loadScratch(sc, data)
|
||||
if err != nil {
|
||||
return "", schema.PropertyBag{}, err
|
||||
}
|
||||
if len(cr.Roots()) == 0 {
|
||||
return "", schema.PropertyBag{}, errs.New(errs.EditorCommand, "the preset is empty")
|
||||
}
|
||||
scs, ok := scriptcomp.ScriptsOf(sc, sc.Handle(cr.Roots()[0]))
|
||||
scs, ok := scriptcomp.ScriptsOf(sc, sc.Handle(root))
|
||||
if !ok || len(scs.Attachments) == 0 {
|
||||
return "", schema.PropertyBag{}, errs.New(errs.EditorCommand, "the preset holds no script")
|
||||
}
|
||||
|
||||
+110
-99
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@ import (
|
||||
"aego/data"
|
||||
"aego/editor/core/context"
|
||||
"aego/editor/core/document"
|
||||
"aego/scene/mutate"
|
||||
"aego/schema"
|
||||
)
|
||||
|
||||
@@ -108,6 +109,18 @@ func (s *Service) file(id ids.AssetID) (string, []byte, error) {
|
||||
return rel, raw, nil
|
||||
}
|
||||
|
||||
func (s *Service) env() mutate.Env {
|
||||
env := mutate.Env{Types: s.ctx.Types}
|
||||
if pl := s.ctx.Pipeline; pl != nil {
|
||||
env.Lookup = pl.Lookup
|
||||
env.Assets = func(id ids.AssetID) bool {
|
||||
_, ok := pl.PathOf(id)
|
||||
return ok || asset.IsBuiltin(id)
|
||||
}
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
func (s *Service) Doc(id ids.AssetID) (*Doc, error) {
|
||||
d := s.docs[id]
|
||||
if d != nil && d.rev == s.ctx.AssetRev {
|
||||
@@ -125,11 +138,7 @@ func (s *Service) Doc(id ids.AssetID) (*Doc, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsed, err := data.ParseDoc(raw, rel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nd, err := NewDoc(s.reg(), lib, id, rel, parsed)
|
||||
nd, err := NewDoc(s.env(), lib, id, rel, raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -151,11 +160,7 @@ func (s *Service) Table(id ids.AssetID) (*Table, error) {
|
||||
t.Path, t.rev = rel, s.ctx.AssetRev
|
||||
return t, nil
|
||||
}
|
||||
parsed, err := data.ParseTable(raw, rel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nt, err := NewTable(s.reg(), id, rel, parsed)
|
||||
nt, err := NewTable(s.env(), id, rel, raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+128
-123
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,7 @@ import (
|
||||
"aego/editor/core/selection"
|
||||
"aego/engine"
|
||||
"aego/engine/module"
|
||||
"aego/project"
|
||||
"aego/scene"
|
||||
"aego/scene/sceneio"
|
||||
)
|
||||
@@ -441,29 +442,7 @@ func (m *Manager) UniquePath(rel string) string {
|
||||
}
|
||||
|
||||
func WriteAtomic(abs string, data []byte) error {
|
||||
dir := filepath.Dir(abs)
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
tmp, err := os.CreateTemp(dir, ".aego-*.tmp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name := tmp.Name()
|
||||
if _, err := tmp.Write(data); err != nil {
|
||||
tmp.Close()
|
||||
os.Remove(name)
|
||||
return err
|
||||
}
|
||||
if err := tmp.Close(); err != nil {
|
||||
os.Remove(name)
|
||||
return err
|
||||
}
|
||||
if err := os.Rename(name, abs); err != nil {
|
||||
os.Remove(name)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return project.WriteAtomic(abs, data)
|
||||
}
|
||||
|
||||
func encode(d *SceneDocument) []byte {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package gizmo
|
||||
|
||||
import (
|
||||
"aego/core/errs"
|
||||
"aego/editor/core/history/commands"
|
||||
"aego/editor/core/props"
|
||||
"aego/schema"
|
||||
@@ -14,15 +15,25 @@ func SetProperty(e *EditCtx, t schema.TypeID, fd *schema.FieldDescriptor, v sche
|
||||
if h.IsZero() {
|
||||
return
|
||||
}
|
||||
td, ok := e.Scene.Registry().ByID(t)
|
||||
if !ok {
|
||||
e.Err = errs.New(errs.SchemaUnknownType, "component type is not registered")
|
||||
return
|
||||
}
|
||||
nv, err := props.Normalize(fd, v)
|
||||
if err != nil {
|
||||
e.Err = err
|
||||
return
|
||||
}
|
||||
before, err := e.Scene.GetProperty(h, t, fd.ID)
|
||||
if err != nil {
|
||||
e.Err = err
|
||||
return
|
||||
}
|
||||
if props.Equal(before, v) {
|
||||
if props.Equal(before, nv) {
|
||||
return
|
||||
}
|
||||
if err := e.Tx.Exec(&commands.SetProperty{Node: e.Node, Type: t, Field: fd.ID, Before: before, After: v, Name: props.Label(fd)}); err != nil {
|
||||
if err := e.Tx.Exec(commands.SetField(e.Scene, e.Node, td, fd, nv, props.Label(fd))); err != nil {
|
||||
e.Err = err
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,61 +3,10 @@ package commands
|
||||
import (
|
||||
"aego/core/errs"
|
||||
"aego/core/ids"
|
||||
"aego/core/log"
|
||||
"aego/editor/core/history"
|
||||
"aego/scene"
|
||||
"aego/scene/sceneio"
|
||||
)
|
||||
|
||||
type Replace struct {
|
||||
Node ids.NodeID
|
||||
Before []byte
|
||||
After []byte
|
||||
Name string
|
||||
Post func(s *scene.Scene)
|
||||
parent ids.NodeID
|
||||
index int
|
||||
ready bool
|
||||
}
|
||||
|
||||
func (c *Replace) Label() string {
|
||||
if c.Name != "" {
|
||||
return c.Name
|
||||
}
|
||||
return "Replace"
|
||||
}
|
||||
|
||||
func (c *Replace) Do(t history.Target) error {
|
||||
s := t.Scene()
|
||||
if err := c.swap(s, c.After); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Post != nil {
|
||||
c.Post(s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Replace) Undo(t history.Target) error {
|
||||
return c.swap(t.Scene(), c.Before)
|
||||
}
|
||||
|
||||
func (c *Replace) Merge(history.Command) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Replace) swap(s *scene.Scene, data []byte) error {
|
||||
if !c.ready {
|
||||
h := s.Handle(c.Node)
|
||||
if h.IsZero() || !s.IsAlive(h) {
|
||||
return errs.New(errs.SceneDeadNode, "node is not in the scene", log.F("node", c.Node.String()))
|
||||
}
|
||||
c.parent, c.index, c.ready = s.ID(s.Parent(h)), ChildIndex(s, h), true
|
||||
}
|
||||
_, err := ReplaceNode(s, c.Node, c.parent, c.index, data, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func ReplaceNode(s *scene.Scene, node, parent ids.NodeID, index int, data []byte, lib *sceneio.Library) (scene.NodeHandle, error) {
|
||||
if h := s.Handle(node); !h.IsZero() && s.IsAlive(h) {
|
||||
s.Destroy(h)
|
||||
|
||||
@@ -18,7 +18,7 @@ func TestReparent3DPreservesWorld(t *testing.T) {
|
||||
s.Flush()
|
||||
before := s.World4(n)
|
||||
h := history.New(target{s})
|
||||
if err := h.Exec(&Reparent{Node: s.ID(n), NewParent: s.ID(p), NewIndex: -1, KeepWorld: true}); err != nil {
|
||||
if err := h.Exec(reparent(s, n, p)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s.Parent(n) != p {
|
||||
@@ -39,7 +39,7 @@ func TestReparent3DUnderTwoDFails(t *testing.T) {
|
||||
n := s.Create3D(s.Root(), "N")
|
||||
s.Flush()
|
||||
h := history.New(target{s})
|
||||
err := h.Exec(&Reparent{Node: s.ID(n), NewParent: s.ID(flat), NewIndex: -1, KeepWorld: true})
|
||||
err := h.Exec(reparent(s, n, flat))
|
||||
if !errs.Has(err, errs.SceneSpaceMismatch) {
|
||||
t.Fatalf("err = %v", err)
|
||||
}
|
||||
@@ -57,7 +57,7 @@ func TestReparent2DIntoThreeDKeepsWorld(t *testing.T) {
|
||||
s.Flush()
|
||||
before := s.World4(n)
|
||||
h := history.New(target{s})
|
||||
if err := h.Exec(&Reparent{Node: s.ID(n), NewParent: s.ID(p), NewIndex: -1, KeepWorld: true}); err != nil {
|
||||
if err := h.Exec(reparent(s, n, p)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !s.Under3D(n) || !s.World4(n).NearlyEqual(before, 1e-4) {
|
||||
@@ -97,7 +97,7 @@ func TestDeleteUndoKeepsSpace(t *testing.T) {
|
||||
s.Flush()
|
||||
id := s.ID(n)
|
||||
h := history.New(target{s})
|
||||
if err := h.Exec(&Delete{Node: id}); err != nil {
|
||||
if err := h.Exec(del(s, id)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h.Undo()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -480,6 +480,7 @@ func (c *Controller) CopyToEdit(nodes []ids.NodeID, t schema.TypeID) error {
|
||||
if !ok {
|
||||
return errs.New(errs.SchemaUnknownType, "component type is not registered")
|
||||
}
|
||||
env := commands.EnvFor(es)
|
||||
tx := real.History.Begin("Copy " + td.FullName + " from Play")
|
||||
for _, n := range nodes {
|
||||
if _, has := es.GetByType(es.Handle(n), t); !has {
|
||||
@@ -492,7 +493,7 @@ func (c *Controller) CopyToEdit(nodes []ids.NodeID, t schema.TypeID) error {
|
||||
}
|
||||
values = append(values, commands.FieldValue{Field: td.Fields[i].ID, Value: v})
|
||||
}
|
||||
if err := tx.Exec(&commands.AddComponent{Node: n, Type: t, Name: td.FullName, Values: values}); err != nil {
|
||||
if err := tx.Exec(commands.NewMutation("Add "+td.FullName, env, commands.AddComp(es, n, td, values))); err != nil {
|
||||
tx.Abort()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package playdiff
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"aego/core/curve"
|
||||
"aego/core/ids"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"aego/editor/core/history/commands"
|
||||
"aego/editor/core/props"
|
||||
"aego/scene"
|
||||
"aego/scene/mutate"
|
||||
"aego/scene/scriptcomp"
|
||||
"aego/schema"
|
||||
)
|
||||
@@ -94,8 +96,16 @@ func Touched(h *history.History) map[Key]bool {
|
||||
}
|
||||
h.Walk(func(c history.Command) {
|
||||
switch c := c.(type) {
|
||||
case *commands.SetProperty:
|
||||
out[Key{Node: c.Node, Type: c.Type, Field: c.Field}] = true
|
||||
case *commands.Mutation:
|
||||
for _, ch := range c.Changes() {
|
||||
switch {
|
||||
case ch.Op != mutate.KindSet && ch.Op != mutate.KindReset:
|
||||
case ch.Type != 0:
|
||||
out[Key{Node: ch.NodeID, Type: ch.Type, Field: ch.Field}] = true
|
||||
case strings.HasPrefix(ch.Path, "transform."):
|
||||
out[Key{Node: ch.NodeID}] = true
|
||||
}
|
||||
}
|
||||
case *commands.SetTransform:
|
||||
out[Key{Node: c.Node}] = true
|
||||
case *commands.SetTransform3D:
|
||||
|
||||
@@ -45,7 +45,7 @@ func pair(t *testing.T) (*document.SceneDocument, *document.SceneDocument, ids.N
|
||||
}
|
||||
s.Flush()
|
||||
d := m.New(rel, s)
|
||||
if err := d.History.Exec(&commands.AddComponent{Node: id, Type: td.ID, Name: td.FullName}); err != nil {
|
||||
if err := d.History.Exec(commands.NewMutation("Add "+td.FullName, commands.EnvFor(s), commands.AddComp(s, id, td, nil))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
docs[i] = d
|
||||
@@ -108,3 +108,14 @@ func TestApplyIsOneStep(t *testing.T) {
|
||||
t.Fatal("undo must restore the edit scene")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTouchedSeesInspectorEdits(t *testing.T) {
|
||||
_, play, id, td := pair(t)
|
||||
fd, _ := td.Field("tint")
|
||||
if err := play.Props.Set(nil, []ids.NodeID{id}, td.ID, fd.ID, schema.Color(mathx.Color{G: 1, A: 1})); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !playdiff.Touched(play.History)[playdiff.Key{Node: id, Type: td.ID, Field: fd.ID}] {
|
||||
t.Fatal("a field set through the Inspector is not touched")
|
||||
}
|
||||
}
|
||||
|
||||
+36
-202
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,6 @@ package prefabs
|
||||
import (
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"aego/core/errs"
|
||||
"aego/core/ids"
|
||||
@@ -11,6 +10,7 @@ import (
|
||||
"aego/editor/core/document"
|
||||
"aego/editor/core/history"
|
||||
"aego/editor/core/history/commands"
|
||||
"aego/project/refactor"
|
||||
"aego/scene"
|
||||
"aego/scene/prefab"
|
||||
"aego/scene/sceneio"
|
||||
@@ -80,25 +80,18 @@ func readFile(abs string) ([]byte, error) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *Service) files(edits map[ids.AssetID]*prefab.Node) ([]fileChange, error) {
|
||||
reg := s.ctx.Types.Schema()
|
||||
var out []fileChange
|
||||
for id, m := range edits {
|
||||
rel, ok := s.pathOf(id)
|
||||
if !ok {
|
||||
return nil, errs.New(errs.EditorDocument, "prefab "+id.Short()+" is not in the project")
|
||||
}
|
||||
before, err := readFile(s.ctx.Pipeline.Abs(rel))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
after := sceneio.EncodePrefab(reg, m)
|
||||
if string(after) == string(before) {
|
||||
continue
|
||||
}
|
||||
out = append(out, fileChange{id: id, rel: rel, before: before, after: after})
|
||||
func (s *Service) files(plan *refactor.ApplyPlan) ([]fileChange, error) {
|
||||
if s.ctx.Pipeline == nil {
|
||||
return nil, errs.New(errs.EditorCommand, "the asset pipeline is not available")
|
||||
}
|
||||
edits, err := plan.Edits(s.ctx.Types.Schema(), s.ctx.Pipeline)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]fileChange, len(edits))
|
||||
for i, e := range edits {
|
||||
out[i] = fileChange{id: e.ID, rel: e.Path, before: e.Before, after: e.After}
|
||||
}
|
||||
slices.SortFunc(out, func(a, b fileChange) int { return strings.Compare(a.rel, b.rel) })
|
||||
return out, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package prefabs
|
||||
import (
|
||||
"aego/core/ids"
|
||||
"aego/editor/core/document"
|
||||
"aego/project/refactor"
|
||||
"aego/scene"
|
||||
"aego/scene/prefab"
|
||||
"aego/scene/sceneio"
|
||||
@@ -259,5 +260,5 @@ func (s *Service) nearestOf(d *document.SceneDocument, node ids.NodeID) (ids.Ass
|
||||
if err != nil {
|
||||
return ids.AssetID{}, false
|
||||
}
|
||||
return newBaker(s.lib()).Nearest(pi.Prefab, key)
|
||||
return refactor.Nearest(s.lib(), pi.Prefab, key)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,11 @@ package prefabs
|
||||
|
||||
import (
|
||||
"aego/core/ids"
|
||||
"aego/project/refactor"
|
||||
"aego/scene/prefab"
|
||||
"aego/scene/sceneio"
|
||||
)
|
||||
|
||||
func NearestForTest(lib *sceneio.Library, id ids.AssetID, key prefab.Key) (ids.AssetID, bool) {
|
||||
return newBaker(lib).Nearest(id, key)
|
||||
return refactor.Nearest(lib, id, key)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ import (
|
||||
"aego/editor/core/context"
|
||||
"aego/editor/core/document"
|
||||
"aego/editor/core/history/commands"
|
||||
"aego/project/refactor"
|
||||
"aego/scene"
|
||||
"aego/scene/mutate"
|
||||
"aego/scene/prefab"
|
||||
"aego/scene/sceneio"
|
||||
)
|
||||
@@ -197,13 +199,9 @@ func (s *Service) CreateFrom(d *document.SceneDocument, nodes []ids.NodeID, dir
|
||||
}
|
||||
tx := d.History.Begin("Create Prefab")
|
||||
for _, h := range tops {
|
||||
if pi, ok := sceneio.InstanceOf(sc, h); ok && pi.Owner {
|
||||
if err := refactor.CheckCreate(sc, h); err != nil {
|
||||
tx.Abort()
|
||||
return errs.New(errs.EditorCommand, sc.Name(h)+" is already a prefab instance, use Create Variant")
|
||||
}
|
||||
if root, _, ok := sceneio.OwnerRoot(sc, h); ok && root != h {
|
||||
tx.Abort()
|
||||
return errs.New(errs.EditorCommand, sc.Name(h)+" belongs to a prefab instance, unpack it first")
|
||||
return err
|
||||
}
|
||||
rel := assetops.UniqueFile(s.ctx, dir, fileSafe(sc.Name(h)), document.PrefabExt)
|
||||
before := sceneio.SaveSubtree(sc, h, true)
|
||||
@@ -212,12 +210,12 @@ func (s *Service) CreateFrom(d *document.SceneDocument, nodes []ids.NodeID, dir
|
||||
tx.Abort()
|
||||
return err
|
||||
}
|
||||
after, err := s.linkBlock(sc, h, id)
|
||||
after, err := refactor.LinkBlock(sc, h, s.lib(), id)
|
||||
if err != nil {
|
||||
tx.Abort()
|
||||
return err
|
||||
}
|
||||
if err := tx.Exec(&commands.Replace{Node: sc.ID(h), Before: before, After: after, Name: "Create Prefab"}); err != nil {
|
||||
if err := tx.Exec(commands.NewMutation("Create Prefab", s.env(sc), mutate.Replace(commands.Ref(sc.ID(h)), string(after)))); err != nil {
|
||||
tx.Abort()
|
||||
return err
|
||||
}
|
||||
@@ -227,47 +225,6 @@ func (s *Service) CreateFrom(d *document.SceneDocument, nodes []ids.NodeID, dir
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) linkBlock(sc *scene.Scene, h scene.NodeHandle, id ids.AssetID) ([]byte, error) {
|
||||
lib := s.lib()
|
||||
p, ok := lib.Prefab(id)
|
||||
if !ok {
|
||||
return nil, errs.New(errs.EditorCommand, "the new prefab did not import")
|
||||
}
|
||||
tree, _, err := prefab.Expand(lib, p, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inst := &prefab.Instance{Prefab: id, IDs: map[prefab.Key]ids.NodeID{}}
|
||||
tree.Walk(func(n, _ *prefab.Node) {
|
||||
if sid, ok := sceneIDOf(sc, n.Key); ok {
|
||||
inst.IDs[n.Key] = sid
|
||||
}
|
||||
})
|
||||
return sceneio.EncodeInstance(sc.Registry(), sc.ID(h), inst), nil
|
||||
}
|
||||
|
||||
func sceneIDOf(sc *scene.Scene, k prefab.Key) (ids.NodeID, bool) {
|
||||
head, rest, nested := strings.Cut(string(k), "/")
|
||||
first, err := ids.ParseNodeID(head)
|
||||
if err != nil {
|
||||
return ids.NodeID{}, false
|
||||
}
|
||||
if !nested {
|
||||
return first, !sc.Handle(first).IsZero()
|
||||
}
|
||||
if pi, ok := sceneio.InstanceOf(sc, sc.Handle(first)); ok {
|
||||
for sid, kk := range pi.Keys {
|
||||
if string(kk) == rest {
|
||||
return sid, true
|
||||
}
|
||||
}
|
||||
}
|
||||
if local, ok := k.Local(); ok && !sc.Handle(local).IsZero() {
|
||||
return local, true
|
||||
}
|
||||
return ids.NodeID{}, false
|
||||
}
|
||||
|
||||
func (s *Service) CreateVariant(id ids.AssetID) error {
|
||||
if err := s.editable(); err != nil {
|
||||
return err
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"aego/editor/core/history"
|
||||
"aego/editor/core/history/commands"
|
||||
"aego/scene"
|
||||
"aego/scene/mutate"
|
||||
)
|
||||
|
||||
func (a *Access) Rename(tx *history.Transaction, node ids.NodeID, name string) error {
|
||||
@@ -13,27 +14,29 @@ func (a *Access) Rename(tx *history.Transaction, node ids.NodeID, name string) e
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
before := a.s.Name(h)
|
||||
if before == name || name == "" {
|
||||
if a.s.Name(h) == name || name == "" {
|
||||
return nil
|
||||
}
|
||||
return a.within(tx, "Rename", func(tx *history.Transaction) error {
|
||||
return tx.Exec(&commands.Rename{Node: node, Before: before, After: name})
|
||||
return tx.Exec(commands.NewMutation("Rename", commands.EnvFor(a.s), mutate.Rename(commands.Ref(node), name)))
|
||||
})
|
||||
}
|
||||
|
||||
func (a *Access) SetEnabled(tx *history.Transaction, nodes []ids.NodeID, v bool) error {
|
||||
text := "false"
|
||||
if v {
|
||||
text = "true"
|
||||
}
|
||||
return a.within(tx, "Toggle Enabled", func(tx *history.Transaction) error {
|
||||
for _, n := range nodes {
|
||||
h, err := a.handle(n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
before := a.s.Enabled(h)
|
||||
if before == v {
|
||||
if a.s.Enabled(h) == v {
|
||||
continue
|
||||
}
|
||||
if err := tx.Exec(&commands.SetEnabled{Node: n, Before: before, After: v}); err != nil {
|
||||
if err := tx.Exec(commands.NewMutation("Toggle Enabled", commands.EnvFor(a.s), mutate.Set(commands.Ref(n), "enabled", text))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func (a *Access) within(tx *history.Transaction, label string, fn func(*history.
|
||||
}
|
||||
|
||||
func (a *Access) Set(tx *history.Transaction, nodes []ids.NodeID, t schema.TypeID, f schema.FieldID, v schema.Value) error {
|
||||
_, fd, err := a.Descriptor(t, f)
|
||||
td, fd, err := a.Descriptor(t, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -131,7 +131,7 @@ func (a *Access) Set(tx *history.Transaction, nodes []ids.NodeID, t schema.TypeI
|
||||
if Equal(before, nv) {
|
||||
continue
|
||||
}
|
||||
if err := tx.Exec(&commands.SetProperty{Node: n, Type: t, Field: f, Before: before, After: nv, Name: name}); err != nil {
|
||||
if err := tx.Exec(commands.SetField(a.s, n, td, fd, nv, name)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
+49
-344
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"aego/core/ids"
|
||||
"aego/format/aetext"
|
||||
"aego/schema"
|
||||
)
|
||||
|
||||
@@ -19,17 +18,3 @@ func TestRewriteNested(t *testing.T) {
|
||||
t.Fatal("no old reference may remain")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTextPlaces(t *testing.T) {
|
||||
id := ids.AssetID{Hi: 7, Lo: 9}
|
||||
src := "scene version=1 {\n node \"n1\" name=\"Player\" {\n component \"engine.SpriteRenderer\" version=1 {\n sprite = " +
|
||||
string(token(id)) + "\n }\n }\n}\n"
|
||||
doc, err := aetext.Parse([]byte(src), "x.aescene")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := textPlaces(doc, vAsset, id.String())
|
||||
if len(got) != 1 || got[0].Path != "Player" || got[0].Owner != "engine.SpriteRenderer" || got[0].Field != "sprite" {
|
||||
t.Fatalf("places = %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,16 +372,12 @@ func InstantiatePlace(ctx *context.Editor, t Template, parent ids.NodeID, index
|
||||
if err := place(s, scratch, h, parent, pl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := &commands.Create{
|
||||
Parent: parent,
|
||||
Index: index,
|
||||
Fragment: sceneio.SaveSubtree(scratch, h, true),
|
||||
Name: t.Name,
|
||||
}
|
||||
cmd := commands.NewMutation("Create "+t.Name, commands.EnvFor(s),
|
||||
commands.Insert(parent, index, string(sceneio.SaveSubtree(scratch, h, true))))
|
||||
if err := d.History.Exec(cmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
created := append([]ids.NodeID(nil), cmd.Roots()...)
|
||||
created := cmd.Created()
|
||||
ctx.Select(created...)
|
||||
return created, nil
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@ import (
|
||||
"aego/editor/core/assetops"
|
||||
"aego/editor/core/context"
|
||||
"aego/editor/core/document"
|
||||
"aego/editor/core/rename"
|
||||
"aego/editor/core/services"
|
||||
"aego/platform/input"
|
||||
"aego/project/refactor"
|
||||
"aego/project/rename"
|
||||
"aego/ui/core"
|
||||
"aego/ui/widgets"
|
||||
)
|
||||
@@ -300,34 +301,10 @@ func (g *Dialog) applyNodes() error {
|
||||
}
|
||||
|
||||
func (g *Dialog) applyAssets() error {
|
||||
finals := map[string]bool{}
|
||||
sources := map[string]bool{}
|
||||
var ops []assetops.FileOp
|
||||
for i, rel := range g.paths {
|
||||
rw := g.rows[i]
|
||||
if !rw.Changed() {
|
||||
continue
|
||||
}
|
||||
to := path.Join(path.Dir(rel), rw.To+path.Ext(rel))
|
||||
finals[strings.ToLower(to)] = true
|
||||
sources[strings.ToLower(rel)] = true
|
||||
ops = append(ops, assetops.FileOp{ID: g.assets[i], From: rel, To: to})
|
||||
ops := refactor.RenameOps(g.paths, g.assets, g.rows)
|
||||
list := make([]assetops.FileOp, len(ops))
|
||||
for i, op := range ops {
|
||||
list[i] = assetops.FileOp{ID: op.ID, From: op.From, To: op.To}
|
||||
}
|
||||
chain := false
|
||||
for to := range finals {
|
||||
if sources[to] {
|
||||
chain = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if chain {
|
||||
var temp, final []assetops.FileOp
|
||||
for i, op := range ops {
|
||||
tmp := path.Join(path.Dir(op.From), "__rename_"+strconv.Itoa(i)+"_"+path.Base(op.From))
|
||||
temp = append(temp, assetops.FileOp{ID: op.ID, From: op.From, To: tmp})
|
||||
final = append(final, assetops.FileOp{ID: op.ID, From: tmp, To: op.To})
|
||||
}
|
||||
ops = append(temp, final...)
|
||||
}
|
||||
return g.d.Move(ops)
|
||||
return g.d.Move(list)
|
||||
}
|
||||
|
||||
@@ -236,11 +236,19 @@ func (p *Panel) act(a int, ro bool) {
|
||||
}
|
||||
switch a {
|
||||
case actAdd:
|
||||
i := t.AddRow()
|
||||
i, err := t.AddRow()
|
||||
if err != nil {
|
||||
p.notify(err)
|
||||
return
|
||||
}
|
||||
p.filter = ""
|
||||
p.Select(t.Rows[i].ID)
|
||||
case actDuplicate:
|
||||
made := t.Duplicate(p.selected())
|
||||
made, err := t.Duplicate(p.selected())
|
||||
if err != nil {
|
||||
p.notify(err)
|
||||
return
|
||||
}
|
||||
ids := make([]string, len(made))
|
||||
for k, i := range made {
|
||||
ids[k] = t.Rows[i].ID
|
||||
@@ -249,12 +257,12 @@ func (p *Panel) act(a int, ro bool) {
|
||||
p.Select(ids...)
|
||||
}
|
||||
case actDelete:
|
||||
t.Delete(p.selected())
|
||||
p.notify(t.Delete(p.selected()))
|
||||
clear(p.sel)
|
||||
case actUndo:
|
||||
t.Undo()
|
||||
p.notify(t.Undo())
|
||||
case actRedo:
|
||||
t.Redo()
|
||||
p.notify(t.Redo())
|
||||
case actExport:
|
||||
if p.d.Data == nil {
|
||||
return
|
||||
|
||||
@@ -681,33 +681,29 @@ func (p *Panel) move(d *document.SceneDocument, nodes []ids.NodeID, target ids.N
|
||||
if n == th {
|
||||
continue
|
||||
}
|
||||
cmd := &commands.Reparent{Node: s.ID(n), KeepWorld: true}
|
||||
parent, index := target, -1
|
||||
switch zone {
|
||||
case zoneInto:
|
||||
cmd.NewParent = target
|
||||
cmd.NewIndex = -1
|
||||
case zoneBefore:
|
||||
parent := s.Parent(th)
|
||||
ph := s.Parent(th)
|
||||
idx := commands.ChildIndex(s, th)
|
||||
if s.Parent(n) == parent && commands.ChildIndex(s, n) < idx {
|
||||
if s.Parent(n) == ph && commands.ChildIndex(s, n) < idx {
|
||||
idx--
|
||||
}
|
||||
cmd.NewParent = s.ID(parent)
|
||||
cmd.NewIndex = idx
|
||||
parent, index = s.ID(ph), idx
|
||||
case zoneAfter:
|
||||
parent := s.Parent(anchor)
|
||||
ph := s.Parent(anchor)
|
||||
idx := commands.ChildIndex(s, anchor) + 1
|
||||
if s.Parent(n) == parent && commands.ChildIndex(s, n) < idx {
|
||||
if s.Parent(n) == ph && commands.ChildIndex(s, n) < idx {
|
||||
idx--
|
||||
}
|
||||
cmd.NewParent = s.ID(parent)
|
||||
cmd.NewIndex = idx
|
||||
parent, index = s.ID(ph), idx
|
||||
}
|
||||
if p.d.Prefabs != nil && cmd.NewParent != s.ID(s.Parent(n)) && p.d.Prefabs.Locked(d, s.ID(n)) {
|
||||
if p.d.Prefabs != nil && parent != s.ID(s.Parent(n)) && p.d.Prefabs.Locked(d, s.ID(n)) {
|
||||
tx.Abort()
|
||||
p.d.Notices.Notify(context.LevelWarn, s.Name(n)+" comes from a prefab and keeps its parent, unpack the instance to move it")
|
||||
return
|
||||
}
|
||||
cmd := commands.NewMutation("Reparent", commands.EnvFor(s), commands.Move(s.ID(n), parent, index, true))
|
||||
if err := tx.Exec(cmd); err != nil {
|
||||
tx.Abort()
|
||||
p.d.Notices.Notify(context.LevelError, err.Error())
|
||||
|
||||
@@ -647,9 +647,10 @@ func (p *Panel) component(d *document.SceneDocument, t schema.TypeID) {
|
||||
|
||||
func (p *Panel) remove(d *document.SceneDocument, td *schema.TypeDescriptor) {
|
||||
p.confirm(d, "Remove "+p.reg.Meta(td).DisplayName, func() error {
|
||||
env := commands.EnvFor(d.Scene())
|
||||
tx := d.History.Begin("Remove " + td.FullName)
|
||||
for _, id := range p.nodes {
|
||||
if err := tx.Exec(&commands.RemoveComponent{Node: id, Type: td.ID, Name: td.FullName}); err != nil {
|
||||
if err := tx.Exec(commands.NewMutation("Remove "+td.FullName, env, commands.RemoveComp(id, td))); err != nil {
|
||||
tx.Abort()
|
||||
return err
|
||||
}
|
||||
@@ -661,13 +662,16 @@ func (p *Panel) remove(d *document.SceneDocument, td *schema.TypeDescriptor) {
|
||||
|
||||
func (p *Panel) reset(d *document.SceneDocument, td *schema.TypeDescriptor) {
|
||||
p.confirm(d, "Reset "+p.reg.Meta(td).DisplayName, func() error {
|
||||
s := d.Scene()
|
||||
env := commands.EnvFor(s)
|
||||
defaults := p.reg.Defaults(td.ID)
|
||||
tx := d.History.Begin("Reset " + td.FullName)
|
||||
for _, id := range p.nodes {
|
||||
if err := tx.Exec(&commands.RemoveComponent{Node: id, Type: td.ID, Name: td.FullName}); err != nil {
|
||||
if err := tx.Exec(commands.NewMutation("Remove "+td.FullName, env, commands.RemoveComp(id, td))); err != nil {
|
||||
tx.Abort()
|
||||
return err
|
||||
}
|
||||
if err := tx.Exec(&commands.AddComponent{Node: id, Type: td.ID, Name: td.FullName, Values: p.reg.Defaults(td.ID)}); err != nil {
|
||||
if err := tx.Exec(commands.NewMutation("Add "+td.FullName, env, commands.AddComp(s, id, td, defaults))); err != nil {
|
||||
tx.Abort()
|
||||
return err
|
||||
}
|
||||
@@ -796,12 +800,14 @@ func (p *Panel) addComponent(d *document.SceneDocument) {
|
||||
func (p *Panel) add(d *document.SceneDocument, td *schema.TypeDescriptor) {
|
||||
p.confirm(d, "Add "+p.reg.Meta(td).DisplayName, func() error {
|
||||
s := d.Scene()
|
||||
env := commands.WithOnAdd(commands.EnvFor(s), p.d.Clip.OnAdd)
|
||||
defaults := p.reg.Defaults(td.ID)
|
||||
tx := d.History.Begin("Add " + td.FullName)
|
||||
for _, n := range p.nodes {
|
||||
if _, ok := s.GetByType(s.Handle(n), td.ID); ok {
|
||||
continue
|
||||
}
|
||||
if err := tx.Exec(&commands.AddComponent{Node: n, Type: td.ID, Name: td.FullName, Values: p.reg.Defaults(td.ID), OnAdd: p.d.Clip.OnAdd(td)}); err != nil {
|
||||
if err := tx.Exec(commands.NewMutation("Add "+td.FullName, env, commands.AddComp(s, n, td, defaults))); err != nil {
|
||||
tx.Abort()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func WriteAtomic(abs string, data []byte) error {
|
||||
dir := filepath.Dir(abs)
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
tmp, err := os.CreateTemp(dir, ".aego-*.tmp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name := tmp.Name()
|
||||
if _, err := tmp.Write(data); err != nil {
|
||||
tmp.Close()
|
||||
os.Remove(name)
|
||||
return err
|
||||
}
|
||||
if err := tmp.Chmod(0o644); err != nil {
|
||||
tmp.Close()
|
||||
os.Remove(name)
|
||||
return err
|
||||
}
|
||||
if err := tmp.Close(); err != nil {
|
||||
os.Remove(name)
|
||||
return err
|
||||
}
|
||||
if err := os.Rename(name, abs); err != nil {
|
||||
os.Remove(name)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+1
-1
@@ -282,7 +282,7 @@ func (p *Project) Encode() []byte {
|
||||
}
|
||||
|
||||
func (p *Project) Save() error {
|
||||
if err := os.WriteFile(p.FilePath(), p.Encode(), 0o644); err != nil {
|
||||
if err := WriteAtomic(p.FilePath(), p.Encode()); err != nil {
|
||||
return errs.Wrap(err, errs.VFSIO, "cannot write project.aego")
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prefabs
|
||||
package refactor
|
||||
|
||||
import (
|
||||
"slices"
|
||||
@@ -309,3 +309,7 @@ func (b *baker) Nearest(id ids.AssetID, key prefab.Key) (ids.AssetID, bool) {
|
||||
}
|
||||
return d.owner, true
|
||||
}
|
||||
|
||||
func Nearest(lib *sceneio.Library, id ids.AssetID, key prefab.Key) (ids.AssetID, bool) {
|
||||
return newBaker(lib).Nearest(id, key)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package refactor
|
||||
|
||||
import (
|
||||
"aego/asset/pipeline"
|
||||
"aego/core/ids"
|
||||
"aego/project"
|
||||
"aego/project/refs"
|
||||
)
|
||||
|
||||
func Users(p *project.Project, src refs.Source, list []ids.AssetID) []refs.Ref {
|
||||
gone := make(map[ids.AssetID]bool, len(list))
|
||||
for _, id := range list {
|
||||
gone[id] = true
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
var out []refs.Ref
|
||||
for _, id := range list {
|
||||
for _, r := range refs.Referrers(p, src, id) {
|
||||
if (r.Project || !gone[r.Asset]) && !seen[r.Path] {
|
||||
seen[r.Path] = true
|
||||
out = append(out, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
refs.Sort(out)
|
||||
return out
|
||||
}
|
||||
|
||||
func Delete(pl *pipeline.Pipeline, list []ids.AssetID) (*pipeline.Batch, error) {
|
||||
all := &pipeline.Batch{Failed: map[ids.AssetID]error{}}
|
||||
for _, id := range list {
|
||||
b, err := pl.Delete(id)
|
||||
if b != nil {
|
||||
all.Imported = append(all.Imported, b.Imported...)
|
||||
all.Removed = append(all.Removed, b.Removed...)
|
||||
all.Retry = append(all.Retry, b.Retry...)
|
||||
for k, v := range b.Failed {
|
||||
all.Failed[k] = v
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return all, err
|
||||
}
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
package refs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"aego/core/ids"
|
||||
"aego/format/aetext"
|
||||
)
|
||||
|
||||
func TestTextPlaces(t *testing.T) {
|
||||
id := ids.AssetID{Hi: 7, Lo: 9}
|
||||
src := "scene version=1 {\n node \"n1\" name=\"Player\" {\n component \"engine.SpriteRenderer\" version=1 {\n sprite = " +
|
||||
string(Token(id)) + "\n }\n }\n}\n"
|
||||
doc, err := aetext.Parse([]byte(src), "x.aescene")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := textPlaces(doc, vAsset, id.String())
|
||||
if len(got) != 1 || got[0].Path != "Player" || got[0].Owner != "engine.SpriteRenderer" || got[0].Field != "sprite" {
|
||||
t.Fatalf("places = %+v", got)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user