231 lines
5.6 KiB
Go
231 lines
5.6 KiB
Go
package playdiff
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
"aego/core/mathx"
|
|
"aego/editor/core/context"
|
|
"aego/editor/core/document"
|
|
pd "aego/editor/core/playdiff"
|
|
"aego/editor/core/services"
|
|
"aego/platform/input"
|
|
"aego/ui/core"
|
|
"aego/ui/widgets"
|
|
)
|
|
|
|
type Deps struct {
|
|
Docs services.Documents
|
|
Notices services.Notices
|
|
}
|
|
|
|
type Dialog struct {
|
|
d Deps
|
|
id core.ID
|
|
open bool
|
|
doc document.ID
|
|
res pd.Result
|
|
scroll float32
|
|
}
|
|
|
|
func New(d Deps) *Dialog {
|
|
return &Dialog{d: d, id: core.IDOf("playdiff")}
|
|
}
|
|
|
|
func (g *Dialog) Active() bool {
|
|
return g.open
|
|
}
|
|
|
|
func (g *Dialog) Open(ui *core.Context, doc document.ID, res pd.Result) {
|
|
g.open, g.doc, g.res, g.scroll = true, doc, res, 0
|
|
ui.OpenModal(g.id)
|
|
}
|
|
|
|
func (g *Dialog) close(ui *core.Context) {
|
|
g.open = false
|
|
g.res = pd.Result{}
|
|
ui.CloseModal()
|
|
}
|
|
|
|
func check(ui *core.Context, id core.ID, r mathx.Rect, state int) bool {
|
|
th := &ui.Theme
|
|
clicked := ui.Clicked(id, r)
|
|
box := mathx.Rect{X: r.X + 3, Y: r.Y + 3, W: r.W - 6, H: r.H - 6}
|
|
switch state {
|
|
case 1:
|
|
ui.Rect(box, th.Accent)
|
|
widgets.CheckMark(ui, box, th.Background)
|
|
case 2:
|
|
ui.Rect(box, ui.WidgetColor(id))
|
|
ui.Outline(box, th.Border)
|
|
ui.Rect(mathx.Rect{X: box.X + 3, Y: box.Y + box.H/2 - 1, W: box.W - 6, H: 2}, th.Accent)
|
|
default:
|
|
ui.Rect(box, ui.WidgetColor(id))
|
|
ui.Outline(box, th.Border)
|
|
}
|
|
return clicked
|
|
}
|
|
|
|
type row struct {
|
|
header bool
|
|
first int
|
|
last int
|
|
}
|
|
|
|
func (g *Dialog) rows() []row {
|
|
var out []row
|
|
ch := g.res.Changes
|
|
for i := 0; i < len(ch); {
|
|
j := i
|
|
for j < len(ch) && ch[j].Node == ch[i].Node {
|
|
j++
|
|
}
|
|
out = append(out, row{header: true, first: i, last: j})
|
|
for k := i; k < j; k++ {
|
|
out = append(out, row{first: k, last: k + 1})
|
|
}
|
|
i = j
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (g *Dialog) state(a, b int) int {
|
|
on := 0
|
|
for i := a; i < b; i++ {
|
|
if g.res.Changes[i].Checked {
|
|
on++
|
|
}
|
|
}
|
|
switch on {
|
|
case 0:
|
|
return 0
|
|
case b - a:
|
|
return 1
|
|
}
|
|
return 2
|
|
}
|
|
|
|
func (g *Dialog) set(a, b int, v bool) {
|
|
for i := a; i < b; i++ {
|
|
g.res.Changes[i].Checked = v
|
|
}
|
|
}
|
|
|
|
func (g *Dialog) apply() {
|
|
d := g.d.Docs.Documents().Get(g.doc)
|
|
if d == nil {
|
|
g.d.Notices.Notify(context.LevelError, "The scene was closed, the changes are dropped")
|
|
return
|
|
}
|
|
n, err := pd.Apply(d, g.res.Changes)
|
|
switch {
|
|
case err != nil:
|
|
g.d.Notices.Notify(context.LevelError, err.Error())
|
|
case n > 0:
|
|
g.d.Notices.Notify(context.LevelInfo, "Applied "+strconv.Itoa(n)+" change(s) from Play, Undo reverts them")
|
|
}
|
|
}
|
|
|
|
func (g *Dialog) Draw(ui *core.Context) {
|
|
if !g.open {
|
|
return
|
|
}
|
|
th := &ui.Theme
|
|
sz := ui.Size()
|
|
w := min(760, sz.X-40)
|
|
h := min(520, sz.Y-60)
|
|
r := mathx.Rect{X: (sz.X - w) / 2, Y: max(20, sz.Y*0.1), W: w, H: h}
|
|
if !ui.BeginModal(g.id, r) {
|
|
g.open = false
|
|
return
|
|
}
|
|
if ui.KeyPressed(input.KeyEscape) {
|
|
ui.EndModal()
|
|
g.close(ui)
|
|
return
|
|
}
|
|
title := ui.Cursor().Next(0)
|
|
ui.Text("Changes made during Play", mathx.Vec2{X: title.X, Y: ui.TextY(title)}, th.Text)
|
|
all := g.state(0, len(g.res.Changes))
|
|
ar := mathx.Rect{X: title.X + title.W - title.H, Y: title.Y, W: title.H, H: title.H}
|
|
if len(g.res.Changes) > 0 && check(ui, ui.ID("all"), ar, all) {
|
|
g.set(0, len(g.res.Changes), all != 1)
|
|
}
|
|
widgets.LabelDim(ui, "Fields you changed in the Inspector during Play are checked. Everything else changed by the game itself.")
|
|
rows := g.rows()
|
|
foot := th.RowHeight*2 + th.Padding*2
|
|
if len(g.res.Created)+len(g.res.Removed) > 0 {
|
|
foot += th.RowHeight
|
|
}
|
|
cur := ui.Cursor()
|
|
list := mathx.Rect{X: cur.X, Y: cur.Y, W: cur.W, H: max(0, r.Y+r.H-cur.Y-foot)}
|
|
rowH := th.RowHeight + 2
|
|
widgets.VirtualList(ui, ui.ID("list"), list, len(rows), rowH, &g.scroll, func(i int, rr mathx.Rect) {
|
|
g.drawRow(ui, i, rows[i], rr)
|
|
})
|
|
if len(rows) == 0 {
|
|
ui.TextIn(list, "No fields differ from the scene", th.TextDisabled, core.AlignCenter)
|
|
}
|
|
ui.Cursor().Y = list.Y + list.H + th.Spacing
|
|
if n, m := len(g.res.Created), len(g.res.Removed); n+m > 0 {
|
|
widgets.LabelDim(ui, strconv.Itoa(n)+" node(s) created and "+strconv.Itoa(m)+" removed during Play are not transferred")
|
|
}
|
|
btn := ui.Cursor().Next(0)
|
|
bw := float32(140)
|
|
if widgets.ButtonRect(ui, ui.ID("discard"), mathx.Rect{X: btn.X + btn.W - bw, Y: btn.Y, W: bw, H: btn.H}, "Discard") {
|
|
ui.EndModal()
|
|
g.close(ui)
|
|
return
|
|
}
|
|
n := 0
|
|
for i := range g.res.Changes {
|
|
if g.res.Changes[i].Checked {
|
|
n++
|
|
}
|
|
}
|
|
label := "Apply " + strconv.Itoa(n) + " to scene"
|
|
if widgets.ButtonRect(ui, ui.ID("apply"), mathx.Rect{X: btn.X + btn.W - 2*bw - 6, Y: btn.Y, W: bw, H: btn.H}, label) && n > 0 {
|
|
g.apply()
|
|
ui.EndModal()
|
|
g.close(ui)
|
|
return
|
|
}
|
|
ui.EndModal()
|
|
}
|
|
|
|
func (g *Dialog) drawRow(ui *core.Context, i int, rw row, rr mathx.Rect) {
|
|
th := &ui.Theme
|
|
id := core.IDSeq(ui.ID("row"), i)
|
|
cb := mathx.Rect{X: rr.X, Y: rr.Y, W: rr.H, H: rr.H}
|
|
if rw.header {
|
|
ui.Rect(rr, th.CardHeader)
|
|
st := g.state(rw.first, rw.last)
|
|
if check(ui, id, cb, st) {
|
|
g.set(rw.first, rw.last, st != 1)
|
|
}
|
|
ui.PushClip(rr)
|
|
ui.Text(g.res.Changes[rw.first].Path, mathx.Vec2{X: cb.X + cb.W + 4, Y: ui.TextY(rr)}, th.Text)
|
|
ui.PopClip()
|
|
return
|
|
}
|
|
c := &g.res.Changes[rw.first]
|
|
cb.X += th.Indent
|
|
if check(ui, id, cb, map[bool]int{false: 0, true: 1}[c.Checked]) {
|
|
c.Checked = !c.Checked
|
|
}
|
|
x := cb.X + cb.W + 4
|
|
name := c.Owner
|
|
if c.Kind == pd.KindField {
|
|
name += " · " + c.Label
|
|
}
|
|
nameW := min(rr.W*0.3, ui.TextSize(name).X+8)
|
|
ui.PushClip(mathx.Rect{X: x, Y: rr.Y, W: nameW, H: rr.H})
|
|
ui.Text(name, mathx.Vec2{X: x, Y: ui.TextY(rr)}, th.TextDim)
|
|
ui.PopClip()
|
|
x += nameW + 8
|
|
text := c.Text(false) + " → " + c.Text(true)
|
|
ui.PushClip(mathx.Rect{X: x, Y: rr.Y, W: max(0, rr.X+rr.W-x), H: rr.H})
|
|
ui.Text(text, mathx.Vec2{X: x, Y: ui.TextY(rr)}, th.Text)
|
|
ui.PopClip()
|
|
widgets.Tooltip(ui, id, c.Text(false)+"\n→ "+c.Text(true))
|
|
}
|