Files
aego/editor/panels/inspector/prefab.go
sanspie 2fcbaa6c12 Stage 11g1: the editor in Russian (D348)
engine/i18n with catalogs, plural forms and module catalogs; tools/i18n extract and check; the editor UI, menus, panels and notices in Russian with the author's terms; overflow test and tooltip wrapping; the English log.
2026-10-25 15:31:22 +03:00

135 lines
4.7 KiB
Go

package inspector
import (
"path"
"strings"
"aego/core/mathx"
"aego/editor/core/context"
"aego/editor/core/document"
"aego/editor/core/prefabs"
"aego/engine/i18n"
"aego/scene/prefab"
"aego/schema"
"aego/ui/widgets"
)
func ownerOf(td *schema.TypeDescriptor) string {
if td.Kind == schema.TypeScript {
return prefab.ScriptOwner(td.FullName)
}
return td.FullName
}
func (p *Panel) report(err error) {
if err != nil {
p.d.Notices.Notify(context.LevelError, err.Error())
}
}
func (p *Panel) overridden(td *schema.TypeDescriptor, fd *schema.FieldDescriptor) bool {
return p.info != nil && len(p.nodes) == 1 && p.info.Overridden(p.nodes[0], ownerOf(td), fd.Name)
}
func (p *Panel) prefabCard(d *document.SceneDocument) {
info := p.info
if info == nil {
return
}
pf := p.d.Prefabs
ui := p.f.UI
th := &ui.Theme
ui.PushID("prefab")
defer ui.PopID()
title := i18n.Tr("editor.inspector.prefab", "Prefab")
if info.Path != "" {
title += " " + strings.TrimSuffix(path.Base(info.Path), path.Ext(info.Path))
}
c := p.beginCard("prefab", title, "", "file.prefab", i18n.Tr("editor.common.open", "Open"), false)
defer p.endCard(ui, c)
if c.action {
p.report(pf.Open(info.Prefab))
}
if !c.open {
return
}
if info.Missing {
widgets.LabelDim(ui, i18n.Tr("editor.inspector.the_prefab_is_missing_the", "The prefab is missing, the instance is kept as it is in the file"))
return
}
node := p.nodes[0]
row := ui.Cursor().Next(0)
w := (row.W - 4) / 2
if widgets.ButtonRect(ui, ui.ID("apply"), mathx.Rect{X: row.X, Y: row.Y, W: w, H: row.H}, i18n.Tr("editor.inspector.apply_all", "Apply All")) {
p.report(pf.Apply(d, prefabs.Change{Node: node, Level: prefabs.LevelAll}))
}
if widgets.ButtonRect(ui, ui.ID("revert"), mathx.Rect{X: row.X + w + 4, Y: row.Y, W: w, H: row.H}, i18n.Tr("editor.inspector.revert_all", "Revert All")) {
p.report(pf.Revert(d, prefabs.Change{Node: node, Level: prefabs.LevelAll}))
}
for i, owner := range info.Removed(node) {
ui.PushIDInt(i)
p.entryRow(i18n.Tr("editor.inspector.removed_owner", "Removed {name}", i18n.Str("name", owner)), i18n.Tr("editor.inspector.revert", "Revert"), func() {
p.report(pf.Revert(d, prefabs.Change{Node: node, Level: prefabs.LevelComponent, Prop: prefab.PropKey{Owner: owner}}))
})
ui.PopID()
}
orph := info.Orphans()
if orph.Empty() {
return
}
r := ui.Cursor().Next(0)
widgets.IconAt(ui, mathx.Rect{X: r.X, Y: r.Y + (r.H-16)/2, W: 16, H: 16}, "status.warning", th.Warn)
ui.Text(i18n.Tr("editor.inspector.unused_overrides", "Unused overrides"), mathx.Vec2{X: r.X + 20, Y: ui.TextY(r)}, th.Warn)
remove := func(c prefabs.Change) func() {
return func() { p.report(pf.Revert(d, c)) }
}
for i, o := range orph.Props {
ui.PushIDInt(1000 + i)
p.entryRow(o.Prop.String(), i18n.Tr("editor.inspector.remove", "Remove"), remove(prefabs.Change{Node: info.Root, Key: o.Node, Level: prefabs.LevelOrphan, Prop: o.Prop}))
ui.PopID()
}
for i, a := range orph.AddComps {
ui.PushIDInt(2000 + i)
p.entryRow(i18n.Tr("editor.inspector.added_owner", "added {name}", i18n.Str("name", a.Comp.Owner)), i18n.Tr("editor.inspector.remove", "Remove"), remove(prefabs.Change{Node: info.Root, Key: a.Node, Level: prefabs.LevelOrphan, Prop: prefab.PropKey{Owner: a.Comp.Owner}}))
ui.PopID()
}
for i, a := range orph.AddNodes {
ui.PushIDInt(3000 + i)
p.entryRow(i18n.Tr("editor.inspector.added_node", "added node {name}", i18n.Str("name", a.Node.Name())), i18n.Tr("editor.inspector.remove", "Remove"), remove(prefabs.Change{Node: info.Root, Key: prefab.LocalKey(a.Node.Local), Level: prefabs.LevelOrphan}))
ui.PopID()
}
}
func (p *Panel) entryRow(text, button string, fn func()) {
ui := p.f.UI
r := ui.Cursor().Next(0)
bw := ui.TextSize(button).X + 14
ui.PushClip(mathx.Rect{X: r.X, Y: r.Y, W: max(0, r.W-bw-4), H: r.H})
ui.Text(text, mathx.Vec2{X: r.X, Y: ui.TextY(r)}, ui.Theme.TextDim)
ui.PopClip()
if widgets.ButtonRect(ui, ui.ID("entry"), mathx.Rect{X: r.X + r.W - bw, Y: r.Y, W: bw, H: r.H}, button) {
fn()
}
}
func (p *Panel) prefabItems(d *document.SceneDocument, c prefabs.Change, enabled bool) {
if p.info == nil || p.info.Missing {
return
}
ui := p.f.UI
widgets.Separator(ui)
for _, t := range p.d.Prefabs.Targets(d, c.Node) {
c.Nearest = t.Nearest
if widgets.MenuItemIcon(ui, i18n.Tr("editor.inspector.apply_to", "Apply to {name}", i18n.Str("name", t.Name)), "", "prefab.apply", enabled, false) {
p.report(p.d.Prefabs.Apply(d, c))
}
}
label := i18n.Tr("editor.inspector.reset_to_prefab_value", "Reset to Prefab Value")
if c.Level != prefabs.LevelProperty {
label = i18n.Tr("editor.inspector.revert_to_prefab", "Revert to Prefab")
}
if widgets.MenuItemIcon(ui, label, "", "prefab.revert", enabled, false) {
p.report(p.d.Prefabs.Revert(d, c))
}
}