From 1ae824f5648f2cf5057b2452eb6443a2c2ed7328 Mon Sep 17 00:00:00 2001 From: Nils <> Date: Sun, 4 Jul 2021 00:00:54 +0200 Subject: [PATCH] Better undo for measures with mods --- engine/api.py | 52 +++++++++++++++++++++++++++++++++++-------------- engine/track.py | 6 +++--- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/engine/api.py b/engine/api.py index 2485bab..b146326 100644 --- a/engine/api.py +++ b/engine/api.py @@ -737,33 +737,57 @@ def _setTrackStructure(trackId, structure, undoMessage): callbacks._trackStructureChanged(track) +def _removeMeasureModificationsWithUndo(trackId, position): + """Must be run in a history sequence context manager!""" + track = session.data.trackById(trackId) + if position in track.whichPatternsAreScaleTransposed: + setSwitchScaleTranspose(trackId, position, 0) #set to default value. track export will remove the value from the data + if position in track.whichPatternsAreHalftoneTransposed: + setSwitchHalftoneTranspose(trackId, position, 0) #set to default value. track export will remove the value from the data + if position in track.whichPatternsAreStepDelayed: + setSwitchStepDelay(trackId, position, 0) #set to default value. track export will remove the value from the data + if position in track.whichPatternsHaveAugmentationFactor: + setSwitchAugmentationsFactor(trackId, position, 1.0) #set to default value. track export will remove the value from the data + + def setSwitches(trackId, setOfPositions, newBool): """Used in the GUI to select multiple switches in a row by dragging the mouse""" track = session.data.trackById(trackId) if not track: return - session.history.register(lambda tr=trackId, v=track.structure.copy(): _setTrackStructure(trackId, v, "Set Measures"), descriptionString="Set Measures") + with session.history.sequence("Set Measures"): + session.history.register(lambda tr=trackId, v=track.structure.copy(): _setTrackStructure(trackId, v, "Set Measures"), descriptionString="Set Measures") + + if newBool: + track.structure = track.structure.union(setOfPositions) #merge: add setOfPositions to the existing one + else: + track.structure = track.structure.difference(setOfPositions) #replace: remove everything from setOfPositions that is in the existing one, ignore entries from setOfPositions not in existing. + + for position in setOfPositions: + _removeMeasureModificationsWithUndo(trackId, position) - if newBool: - track.structure = track.structure.union(setOfPositions) #merge: add setOfPositions to the existing one - else: - track.structure = track.structure.difference(setOfPositions) #replace: remove everything from setOfPositions that is in the existing one, ignore entries from setOfPositions not in existing. track.buildTrack() updatePlayback() callbacks._trackStructureChanged(track) def setSwitch(trackId, position, newBool): + """e.g. for GUI Single click operations. Switch on and off a measure""" + track = session.data.trackById(trackId) if not track: return - session.history.register(lambda trId=trackId, v=track.structure.copy(): _setTrackStructure(trId, v, "Set Measures"), descriptionString="Set Measures") + with session.history.sequence("Set Measures"): + session.history.register(lambda trId=trackId, v=track.structure.copy(): _setTrackStructure(trId, v, "Set Measures"), descriptionString="Set Measures") + + if newBool: + if position in track.structure: return + track.structure.add(position) + else: + if not position in track.structure: return + track.structure.remove(position) + + _removeMeasureModificationsWithUndo(trackId, position) - if newBool: - if position in track.structure: return - track.structure.add(position) - else: - if not position in track.structure: return - track.structure.remove(position) track.buildTrack() updatePlayback() callbacks._trackStructureChanged(track) @@ -948,7 +972,6 @@ def _registerHistoryWholeTrackSwitches(track): It assumes that it runs inside this context: with session.history.sequence("asdasd"): """ - print ("reg all", track) trackId = id(track) session.history.register(lambda trId=trackId, v=track.patternLengthMultiplicator: setTrackPatternLengthMultiplicator(trId, v), descriptionString="Pattern Multiplier") session.history.register(lambda trId=trackId, v=track.structure.copy(): _setTrackStructure(trId, v, "Change Group"), descriptionString="Change Group") @@ -956,7 +979,6 @@ def _registerHistoryWholeTrackSwitches(track): session.history.register(lambda trId=trackId, v=track.whichPatternsAreHalftoneTransposed.copy(): _setSwitchHalftoneTranspose(trId, v), descriptionString="Set Half Tone Shift") session.history.register(lambda trId=trackId, v=track.whichPatternsAreStepDelayed.copy(): _setSwitchStepDelay(trId, v), descriptionString="Set Step Delay") session.history.register(lambda trId=trackId, v=track.whichPatternsHaveAugmentationFactor.copy(): _setSwitchAugmentationsFactor(trId, v), descriptionString="Set Augmentation Factor") - print ("reg done") def insertSilence(howMany:int, beforeMeasureNumber:int): """Insert empty measures into all tracks. @@ -1238,7 +1260,7 @@ def setStep(trackId, stepExportDict): callbacks._stepChanged(track, stepExportDict) def removeStep(trackId, index, pitch): - """Reverse of setStep""" + """Reverse of setStep. e.g. GUI-Click on an existing step.""" track = session.data.trackById(trackId) if not track: return session.history.register(lambda trId=trackId, v=track.pattern.copyData(): setPattern(trId, v, "Remove Step"), descriptionString="Remove Step") diff --git a/engine/track.py b/engine/track.py index a70f504..75fb9fa 100644 --- a/engine/track.py +++ b/engine/track.py @@ -112,9 +112,9 @@ class Track(object): #injection at the bottom of this file! assert ourCalfboxTrack, (ourCalfboxTrack, self, self.group, self.parentData, self.sequencerInterface) #First clean all modifications from the default value. - #We test for k in self.structure to not have modifications for measures that will be switched on later - #TODO: Can this possibly lead to a race condition where we load modifications first and then load the structure, which results in the mods getting perma-deleted here. e.g. we could not set default value in init, even for testing purposes. - #TODO: Yes, this happened with undo. every {comprehension} had a "and if k in self.structure" at the end. We took that out without remembering what that was for. + #We test for k in self.structure to not have modifications for measures that will be switched on later. This way when you deactivate a measure it will delete modifications. However, that was inconvenient. See below: + #Can this possibly lead to a race condition where we load modifications first and then load the structure, which results in the mods getting perma-deleted here. e.g. we could not set default value in init, even for testing purposes. + #Yes, this happened with undo. every {comprehension} had a "and if k in self.structure" at the end. We took that out without remembering what that was for. self.whichPatternsAreScaleTransposed = {k:v for k,v in self.whichPatternsAreScaleTransposed.items() if v!=0} self.whichPatternsAreHalftoneTransposed = {k:v for k,v in self.whichPatternsAreHalftoneTransposed.items() if v!=0} self.whichPatternsAreStepDelayed = {k:v for k,v in self.whichPatternsAreStepDelayed.items() if v!=0}