feat(tracker): undo entire modelData, not just Song

The modelData is moving towards clear meaning: it's the part of the
GUI state that is undone and also recovered from disk. This changes
the recovery data so that the undo and redo stacks are not undone,
but that is unlikely a good idea anyway, as it grows the recovery
data into unreasonable sizes.

This has also the nice benefit of undoing the cursor position, which
closes #64.
This commit is contained in:
5684185+vsariola@users.noreply.github.com 2023-10-20 17:30:54 +03:00
parent 486bab4185
commit 391b14493c

View File

@ -46,15 +46,16 @@ type (
InstrEnlarged bool
RecoveryFilePath string
ChangedSinceRecovery bool
PrevUndoType string
UndoSkipCounter int
UndoStack []sointu.Song
RedoStack []sointu.Song
}
Model struct {
d modelData
d modelData
prevUndoType string
undoSkipCounter int
undoStack []modelData
redoStack []modelData
PlayerMessages <-chan PlayerMessage
modelMessages chan<- interface{}
}
@ -223,13 +224,15 @@ func (m *Model) SetSong(song sointu.Song) {
m.setSongNoUndo(song)
}
// Returns the current octave for jamming and inputting nodes
func (m *Model) Octave() int {
return m.d.Octave
}
// Sets the current octave for jamming and inputting nodes and returns true if
// it changed. The value is clamped to 0..9
func (m *Model) SetOctave(value int) bool {
if value < 0 {
value = 0
}
if value > 9 {
value = 9
}
value = clamp(value, 0, 9)
if m.d.Octave == value {
return false
}
@ -1025,40 +1028,42 @@ func (m *Model) Undo() {
if !m.CanUndo() {
return
}
m.d.RedoStack = append(m.d.RedoStack, m.d.Song.Copy())
m.setSongNoUndo(m.d.UndoStack[len(m.d.UndoStack)-1])
m.d.UndoStack = m.d.UndoStack[:len(m.d.UndoStack)-1]
m.redoStack = append(m.redoStack, m.d.Copy())
m.d = m.undoStack[len(m.undoStack)-1]
m.undoStack = m.undoStack[:len(m.undoStack)-1]
m.limitUndoRedoLengths()
m.d.PrevUndoType = ""
m.prevUndoType = ""
m.send(m.d.Song.Copy())
}
func (m *Model) CanUndo() bool {
return len(m.d.UndoStack) > 0
return len(m.undoStack) > 0
}
func (m *Model) ClearUndoHistory() {
if len(m.d.UndoStack) > 0 {
m.d.UndoStack = m.d.UndoStack[:0]
if len(m.undoStack) > 0 {
m.undoStack = m.undoStack[:0]
}
if len(m.d.RedoStack) > 0 {
m.d.RedoStack = m.d.RedoStack[:0]
if len(m.redoStack) > 0 {
m.redoStack = m.redoStack[:0]
}
m.d.PrevUndoType = ""
m.prevUndoType = ""
}
func (m *Model) Redo() {
if !m.CanRedo() {
return
}
m.d.UndoStack = append(m.d.UndoStack, m.d.Song.Copy())
m.setSongNoUndo(m.d.RedoStack[len(m.d.RedoStack)-1])
m.d.RedoStack = m.d.RedoStack[:len(m.d.RedoStack)-1]
m.undoStack = append(m.undoStack, m.d.Copy())
m.d = m.redoStack[len(m.redoStack)-1]
m.redoStack = m.redoStack[:len(m.redoStack)-1]
m.limitUndoRedoLengths()
m.d.PrevUndoType = ""
m.prevUndoType = ""
m.send(m.d.Song.Copy())
}
func (m *Model) CanRedo() bool {
return len(m.d.RedoStack) > 0
return len(m.redoStack) > 0
}
func (m *Model) SetNoteTracking(value bool) {
@ -1069,10 +1074,6 @@ func (m *Model) NoteTracking() bool {
return m.d.NoteTracking
}
func (m *Model) Octave() int {
return m.d.Octave
}
func (m *Model) Song() sointu.Song {
return m.d.Song
}
@ -1128,11 +1129,11 @@ func (m *Model) ParamIndex() int {
}
func (m *Model) limitUndoRedoLengths() {
if len(m.d.UndoStack) >= maxUndo {
m.d.UndoStack = m.d.UndoStack[len(m.d.UndoStack)-maxUndo:]
if len(m.undoStack) >= maxUndo {
m.undoStack = m.undoStack[len(m.undoStack)-maxUndo:]
}
if len(m.d.RedoStack) >= maxUndo {
m.d.RedoStack = m.d.RedoStack[len(m.d.RedoStack)-maxUndo:]
if len(m.redoStack) >= maxUndo {
m.redoStack = m.redoStack[len(m.redoStack)-maxUndo:]
}
}
@ -1419,14 +1420,14 @@ func (m *Model) send(message interface{}) {
func (m *Model) saveUndo(undoType string, undoSkipping int) {
m.d.ChangedSinceSave = true
m.d.ChangedSinceRecovery = true
if m.d.PrevUndoType == undoType && m.d.UndoSkipCounter < undoSkipping {
m.d.UndoSkipCounter++
if m.prevUndoType == undoType && m.undoSkipCounter < undoSkipping {
m.undoSkipCounter++
return
}
m.d.PrevUndoType = undoType
m.d.UndoSkipCounter = 0
m.d.UndoStack = append(m.d.UndoStack, m.d.Song.Copy())
m.d.RedoStack = m.d.RedoStack[:0]
m.prevUndoType = undoType
m.undoSkipCounter = 0
m.undoStack = append(m.undoStack, m.d.Copy())
m.redoStack = m.redoStack[:0]
m.limitUndoRedoLengths()
}
@ -1492,6 +1493,21 @@ func NoteIDTrack(track int, note byte) NoteID {
return NoteID{IsInstr: false, Track: track, Note: note}
}
func (d *modelData) Copy() modelData {
ret := *d
ret.Song = d.Song.Copy()
ret.PatternUseCount = make([][]int, len(d.PatternUseCount))
for i := range ret.PatternUseCount {
ret.PatternUseCount[i] = make([]int, len(d.PatternUseCount[i]))
copy(ret.PatternUseCount[i], d.PatternUseCount[i])
}
ret.UsedIDs = make(map[int]bool)
for k, v := range d.UsedIDs {
ret.UsedIDs[k] = v
}
return ret
}
func clamp(a, min, max int) int {
if a < min {
return min