296 lines
9.2 KiB
Go
296 lines
9.2 KiB
Go
package inspector
|
|
|
|
import (
|
|
"aego/core/mathx"
|
|
"aego/editor/core/clipboard"
|
|
"aego/editor/core/context"
|
|
"aego/editor/core/document"
|
|
"aego/editor/core/prefabs"
|
|
"aego/editor/core/presets"
|
|
"aego/platform/clip"
|
|
"aego/scene"
|
|
"aego/scene/prefab"
|
|
"aego/schema"
|
|
"aego/ui/core"
|
|
"aego/ui/draw"
|
|
"aego/ui/widgets"
|
|
)
|
|
|
|
type card struct {
|
|
open bool
|
|
more bool
|
|
action bool
|
|
hid core.ID
|
|
hdr mathx.Rect
|
|
bg int
|
|
top float32
|
|
typ schema.TypeID
|
|
}
|
|
|
|
func (p *Panel) beginCard(key, title, badge, icon, action string, menu bool) card {
|
|
ui := p.f.UI
|
|
th := &ui.Theme
|
|
cur := ui.Cursor()
|
|
c := card{bg: ui.List().ReserveBox(), top: cur.Y}
|
|
hdr := cur.Next(th.RowHeight + 4)
|
|
c.hdr = hdr
|
|
c.hid = ui.ID("card")
|
|
corners := draw.CornersAll
|
|
if p.d.Collapsed == nil || !p.d.Collapsed[key] {
|
|
corners = draw.CornersTop
|
|
}
|
|
ui.List().RoundRect(hdr, th.Radius.Card, th.CardHeader, corners)
|
|
right := hdr.X + hdr.W - 4
|
|
bh := hdr.H - 6
|
|
if menu {
|
|
mr := mathx.Rect{X: right - bh, Y: hdr.Y + 3, W: bh, H: bh}
|
|
c.more = widgets.ButtonRect(ui, ui.ID("more"), mr, widgets.Glyph(ui, '…', "..."))
|
|
right = mr.X - 4
|
|
}
|
|
if action != "" {
|
|
w := ui.TextSize(action).X + 14
|
|
ar := mathx.Rect{X: right - w, Y: hdr.Y + 3, W: w, H: bh}
|
|
c.action = widgets.ButtonRect(ui, ui.ID("action"), ar, action)
|
|
right = ar.X - 6
|
|
}
|
|
area := mathx.Rect{X: hdr.X, Y: hdr.Y, W: max(0, right-hdr.X), H: hdr.H}
|
|
if ui.Pressed(c.hid, area) && p.d.Collapsed != nil {
|
|
p.d.Collapsed[key] = !p.d.Collapsed[key]
|
|
}
|
|
c.open = p.d.Collapsed == nil || !p.d.Collapsed[key]
|
|
if ui.Hot() == c.hid {
|
|
hl := th.WidgetHot
|
|
hl.A = 0.5
|
|
ui.List().RoundRect(area, th.Radius.Card, hl, corners)
|
|
}
|
|
x := hdr.X + 2
|
|
widgets.Chevron(ui, mathx.Rect{X: x, Y: hdr.Y, W: th.Indent, H: hdr.H}, c.open, th.TextDim)
|
|
x += th.Indent + 2
|
|
widgets.IconAt(ui, mathx.Rect{X: x, Y: hdr.Y + (hdr.H-16)/2, W: 16, H: 16}, icon, th.TextDim)
|
|
x += 22
|
|
if badge != "" {
|
|
bw := ui.TextSize(badge).X + 10
|
|
if x+ui.TextSize(title).X+bw+8 <= right {
|
|
lh := ui.LineHeight() + 2
|
|
br := mathx.Rect{X: right - bw, Y: hdr.Y + (hdr.H-lh)/2, W: bw, H: lh}
|
|
ui.Rect(br, th.Badge)
|
|
ui.TextIn(br, badge, th.Unit, core.AlignCenter)
|
|
right = br.X - 6
|
|
}
|
|
}
|
|
ui.PushClip(mathx.Rect{X: x, Y: hdr.Y, W: max(0, right-x), H: hdr.H})
|
|
ui.Text(title, mathx.Vec2{X: x, Y: ui.TextY(hdr)}, th.Text)
|
|
ui.PopClip()
|
|
if c.open {
|
|
cur.X += 6
|
|
cur.W -= 12
|
|
cur.Y += 2
|
|
}
|
|
return c
|
|
}
|
|
|
|
func (p *Panel) endCard(ui *core.Context, c card) {
|
|
cur := ui.Cursor()
|
|
if c.open {
|
|
cur.X -= 6
|
|
cur.W += 12
|
|
cur.Y += 2
|
|
}
|
|
rect := mathx.Rect{X: cur.X, Y: c.top, W: cur.W, H: cur.Y - c.top}
|
|
ui.List().PatchBox(c.bg, rect, ui.Theme.Radius.Card, ui.Theme.CardBg, mathx.Color{})
|
|
if c.typ != 0 && ui.Hit(rect) && !ui.MouseInOverlay() {
|
|
p.hoverType = c.typ
|
|
}
|
|
cur.Y += 4
|
|
}
|
|
|
|
func (p *Panel) componentMenu(d *document.SceneDocument, td *schema.TypeDescriptor) {
|
|
ui := p.f.UI
|
|
if widgets.MenuItemIcon(ui, "Reset", "", "ui.reset", true, false) {
|
|
p.reset(d, td)
|
|
}
|
|
if widgets.MenuItemIcon(ui, "Remove", "", "edit.delete", true, false) {
|
|
p.remove(d, td)
|
|
}
|
|
widgets.Separator(ui)
|
|
if widgets.MenuItemIcon(ui, "Copy Component", p.d.Commands.Shortcut(CommandCopy), "edit.copy", true, false) {
|
|
if err := p.d.Clip.CopyComponent(d.Scene(), p.nodes[0], td.ID); err != nil {
|
|
p.d.Notices.Notify(context.LevelError, err.Error())
|
|
} else {
|
|
p.d.Notices.Notify(context.LevelInfo, "Copied "+p.reg.Meta(td).DisplayName)
|
|
}
|
|
}
|
|
same := false
|
|
if p.d.Clip.Peek() == clipboard.KindComponent {
|
|
if src, _, ok := p.d.Clip.ReadComponent(); ok && src.ID == td.ID {
|
|
same = true
|
|
}
|
|
}
|
|
if widgets.MenuItemIcon(ui, "Paste Component Values", p.d.Commands.Shortcut(CommandPaste), "edit.paste", same, false) {
|
|
p.confirm(d, "Paste "+p.reg.Meta(td).DisplayName, func() error { return p.d.Clip.PasteComponent(d, p.nodes) })
|
|
}
|
|
p.presetItems(d, td)
|
|
if p.info != nil {
|
|
owner := ownerOf(td)
|
|
p.prefabItems(d, prefabs.Change{Node: p.nodes[0], Level: prefabs.LevelComponent, Prop: prefab.PropKey{Owner: owner}},
|
|
p.info.ComponentTouched(p.nodes[0], owner))
|
|
}
|
|
if p.d.Docs.Documents().InPlay() && p.d.Play != nil {
|
|
widgets.Separator(ui)
|
|
if widgets.MenuItemIcon(ui, "Copy values to edit scene", "", "play.copy_back", p.d.Play.CanCopyToEdit(p.nodes, td.ID), false) {
|
|
if err := p.d.Play.CopyToEdit(p.nodes, td.ID); err != nil {
|
|
p.d.Notices.Notify(context.LevelError, err.Error())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Panel) lookup(d *document.SceneDocument, ref fieldRef) (*schema.TypeDescriptor, *schema.FieldDescriptor, bool) {
|
|
td, ok := d.Scene().Registry().ByID(ref.t)
|
|
if !ok {
|
|
return nil, nil, false
|
|
}
|
|
fd, ok := td.FieldByID(ref.f)
|
|
if !ok {
|
|
return nil, nil, false
|
|
}
|
|
return td, fd, true
|
|
}
|
|
|
|
func (p *Panel) copyValue(d *document.SceneDocument, td *schema.TypeDescriptor, fd *schema.FieldDescriptor) {
|
|
v, mixed, err := d.Props.Common(p.nodes, td.ID, fd.ID)
|
|
if err != nil {
|
|
p.d.Notices.Notify(context.LevelError, err.Error())
|
|
return
|
|
}
|
|
text := p.summary(d, fd, v, mixed)
|
|
if err := p.d.Clip.CopyValue(d.Scene(), p.nodes[0], td.ID, fd.ID, text); err != nil {
|
|
p.d.Notices.Notify(context.LevelError, err.Error())
|
|
}
|
|
}
|
|
|
|
func (p *Panel) pasteValue(d *document.SceneDocument, td *schema.TypeDescriptor, fd *schema.FieldDescriptor) {
|
|
v, ok := p.d.Clip.ReadValue(fd)
|
|
if !ok {
|
|
p.d.Notices.Notify(context.LevelInfo, "The clipboard does not fit "+p.labelOf(fd))
|
|
return
|
|
}
|
|
p.confirm(d, "Paste "+p.labelOf(fd), func() error { return d.Props.Set(nil, p.nodes, td.ID, fd.ID, v) })
|
|
}
|
|
|
|
func (p *Panel) defaultValue(reg *schema.Registry, td *schema.TypeDescriptor, fd *schema.FieldDescriptor) schema.Value {
|
|
if fd.HasDefault {
|
|
return fd.Default
|
|
}
|
|
for _, fv := range p.reg.Defaults(td.ID) {
|
|
if fv.Field == fd.ID {
|
|
return fv.Value
|
|
}
|
|
}
|
|
return zeroValue(reg, fd)
|
|
}
|
|
|
|
func (p *Panel) fieldMenu(d *document.SceneDocument) {
|
|
ui := p.f.UI
|
|
td, fd, ok := p.lookup(d, p.menuField)
|
|
if !ok {
|
|
return
|
|
}
|
|
if td.Kind == schema.TypeScript && p.code() != nil {
|
|
if widgets.MenuItemIcon(ui, "Go to Declaration", "Ctrl+Click", "code.implementation", true, false) {
|
|
p.gotoScript(td.FullName, fd.Name)
|
|
}
|
|
if widgets.MenuItemIcon(ui, "Find Usages", "", "ui.search", true, false) {
|
|
p.code().FindUsages(td.FullName, fd.Name)
|
|
}
|
|
widgets.Separator(ui)
|
|
}
|
|
ro := fd.Flags.Has(schema.FlagReadOnly)
|
|
if widgets.MenuItemIcon(ui, "Copy Value", p.d.Commands.Shortcut(CommandCopy), "edit.copy", true, false) {
|
|
p.copyValue(d, td, fd)
|
|
}
|
|
_, canPaste := p.d.Clip.ReadValue(fd)
|
|
if widgets.MenuItemIcon(ui, "Paste Value", p.d.Commands.Shortcut(CommandPaste), "edit.paste", canPaste && !ro, false) {
|
|
p.pasteValue(d, td, fd)
|
|
}
|
|
if textureField(fd) {
|
|
if widgets.MenuItemIcon(ui, "Paste Image", "", "file.texture", !ro && clip.HasImage(p.d.Clip.Clip), false) {
|
|
if err := p.pasteImage(d, td, fd); err != nil {
|
|
p.d.Notices.Notify(context.LevelError, err.Error())
|
|
}
|
|
}
|
|
}
|
|
if widgets.MenuItemIcon(ui, "Reset to Default", "", "ui.reset", !ro, false) {
|
|
def := p.defaultValue(d.Scene().Registry(), td, fd)
|
|
p.confirm(d, "Reset "+p.labelOf(fd), func() error { return d.Props.Set(nil, p.nodes, td.ID, fd.ID, def) })
|
|
}
|
|
if p.overridden(td, fd) {
|
|
p.prefabItems(d, prefabs.Change{Node: p.nodes[0], Level: prefabs.LevelProperty, Prop: prefab.PropKey{Owner: ownerOf(td), Field: fd.Name}}, true)
|
|
}
|
|
if td.Kind == schema.TypeScript {
|
|
return
|
|
}
|
|
widgets.Separator(ui)
|
|
if widgets.MenuItemIcon(ui, "Copy Component", "", "edit.copy", true, false) {
|
|
if err := p.d.Clip.CopyComponent(d.Scene(), p.nodes[0], td.ID); err != nil {
|
|
p.d.Notices.Notify(context.LevelError, err.Error())
|
|
}
|
|
}
|
|
if widgets.MenuItemIcon(ui, "Paste Component Values", "", "edit.paste", p.d.Clip.Peek() == clipboard.KindComponent, false) {
|
|
p.confirm(d, "Paste "+p.reg.Meta(td).DisplayName, func() error { return p.d.Clip.PasteComponent(d, p.nodes) })
|
|
}
|
|
}
|
|
|
|
func (p *Panel) resetTransform(d *document.SceneDocument) {
|
|
err := d.Props.EditTransform(nil, p.nodes, func(tr *scene.Transform2D) {
|
|
tr.Position = mathx.Vec2{}
|
|
tr.Rotation = 0
|
|
tr.Scale = mathx.Vec2{X: 1, Y: 1}
|
|
})
|
|
if err != nil {
|
|
p.d.Notices.Notify(context.LevelError, err.Error())
|
|
}
|
|
}
|
|
|
|
func (p *Panel) presetItems(d *document.SceneDocument, td *schema.TypeDescriptor) {
|
|
if p.d.Presets == nil || d.IsPlay() {
|
|
return
|
|
}
|
|
ui := p.f.UI
|
|
widgets.Separator(ui)
|
|
if widgets.MenuItemIcon(ui, "Save as Preset…", "", "file.preset", len(p.nodes) == 1, false) {
|
|
node := p.nodes[0]
|
|
p.d.Notices.Ask(context.Question{
|
|
Title: "Save preset",
|
|
Message: "Saved to " + presets.Dir,
|
|
Fields: []string{"Name"},
|
|
Values: []string{p.reg.Meta(td).DisplayName},
|
|
Buttons: []string{"Save", "Cancel"},
|
|
Submit: func(v []string) {
|
|
rel, err := p.d.Presets.Save(d, node, td, v[0])
|
|
if err != nil {
|
|
p.report(err)
|
|
return
|
|
}
|
|
p.d.Notices.Notify(context.LevelInfo, "Saved "+rel)
|
|
},
|
|
})
|
|
}
|
|
list := p.d.Presets.For(td.FullName)
|
|
if len(list) == 0 {
|
|
widgets.MenuItemIcon(ui, "Apply Preset", "", "file.preset", false, false)
|
|
return
|
|
}
|
|
if widgets.SubMenuIcon(ui, "Apply Preset", "file.preset", 220) {
|
|
for i, pr := range list {
|
|
ui.PushIDInt(i)
|
|
if widgets.MenuItemIcon(ui, pr.Name, "", "file.preset", true, false) {
|
|
id, name := pr.ID, pr.Name
|
|
p.confirm(d, "Apply Preset "+name, func() error { return p.d.Presets.Apply(d, p.nodes, id) })
|
|
}
|
|
ui.PopID()
|
|
}
|
|
widgets.EndSubMenu(ui)
|
|
}
|
|
}
|