Files
aego/editor/panels/undohistory/panel.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

114 lines
2.8 KiB
Go

package undohistory
import (
"strconv"
"aego/core/mathx"
"aego/editor/core/context"
"aego/editor/core/history"
"aego/editor/core/services"
"aego/engine/i18n"
"aego/ui/core"
"aego/ui/widgets"
)
const ID = "undo_history"
type Deps struct {
History func() *history.History
Notices services.Notices
}
type Panel struct {
d Deps
src *history.History
ver uint64
steps []string
scroll float32
}
func New(d Deps) *Panel {
return &Panel{d: d}
}
func (p *Panel) ID() string {
return ID
}
func (p *Panel) Title() string {
return i18n.Tr("editor.undohistory.title", "Undo History")
}
func (p *Panel) notify(l context.Level, s string) {
if p.d.Notices != nil {
p.d.Notices.Notify(l, s)
}
}
func (p *Panel) Draw(f *services.Frame, r mathx.Rect) {
ui := f.UI
th := &ui.Theme
var h *history.History
if p.d.History != nil {
h = p.d.History()
}
if h == nil {
widgets.LabelDim(ui, i18n.Tr("editor.undohistory.open_a_scene_to_see", "Open a scene to see its history"))
return
}
if h != p.src || h.Version() != p.ver {
if h != p.src {
p.scroll = 0
}
p.steps = h.Steps(p.steps[:0])
p.src, p.ver = h, h.Version()
}
cur := h.Cursor()
reason, locked := h.Locked()
busy := locked || h.InTransaction()
switch {
case locked:
widgets.LabelDim(ui, reason)
case h.InTransaction():
widgets.LabelDim(ui, i18n.Tr("editor.undohistory.an_edit_is_in_progress", "An edit is in progress"))
case h.Mode() == history.ModePlay:
widgets.LabelDim(ui, i18n.Tr("editor.undohistory.play_history_cleared_on_stop", "Play history, cleared on Stop"))
default:
widgets.LabelDim(ui, i18n.Tr("editor.undohistory.cur_of_steps_step_s", "{cur} of {steps} step(s), click a step to go back to it", i18n.Str("cur", strconv.Itoa(cur)), i18n.Str("steps", strconv.Itoa(len(p.steps)))))
}
top := ui.Cursor().Y
list := mathx.Rect{X: r.X, Y: top, W: r.W, H: max(0, r.Y+r.H-top)}
base := ui.ID("row")
widgets.VirtualList(ui, ui.ID("rows"), list, len(p.steps)+1, th.RowHeight, &p.scroll, func(i int, row mathx.Rect) {
id := core.IDSeq(base, i)
if ui.Pressed(id, row) && i != cur {
switch {
case busy:
p.notify(context.LevelInfo, i18n.Note("editor.undohistory.finish_the_current_edit_first", "Finish the current edit first"))
default:
if err := h.Seek(i); err != nil {
p.notify(context.LevelError, err.Error())
}
}
}
switch {
case i == cur:
ui.Rect(row, th.Selection)
case ui.Hot() == id:
ui.Rect(row, th.WidgetHot)
}
label := i18n.Tr("editor.undohistory.initial_state", "Initial state")
if i > 0 {
label = p.steps[i-1]
}
col := th.Text
if i > cur || busy {
col = th.TextDim
}
ui.PushClip(row)
ui.Text(strconv.Itoa(i), mathx.Vec2{X: row.X + 6, Y: ui.TextY(row)}, th.TextDisabled)
ui.Text(label, mathx.Vec2{X: row.X + 40, Y: ui.TextY(row)}, col)
ui.PopClip()
})
}