mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-25 18:00:37 -04:00
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:
parent
486bab4185
commit
391b14493c
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user