From 78a6bcee926a6c7b197a3ec0b26b427f3b080c44 Mon Sep 17 00:00:00 2001 From: Paul Walker Date: Thu, 7 Apr 2022 09:46:08 -0400 Subject: [PATCH] More Undo/Redo capabilities Addresses #694 - Step Sequencer undo --- src/surge-xt/gui/SurgeGUIEditor.cpp | 7 ++ src/surge-xt/gui/SurgeGUIEditor.h | 1 + src/surge-xt/gui/UndoManager.cpp | 43 +++++++++++- src/surge-xt/gui/UndoManager.h | 2 + .../gui/widgets/LFOAndStepDisplay.cpp | 70 ++++++++++++++----- src/surge-xt/gui/widgets/LFOAndStepDisplay.h | 22 ++++++ 6 files changed, 124 insertions(+), 21 deletions(-) diff --git a/src/surge-xt/gui/SurgeGUIEditor.cpp b/src/surge-xt/gui/SurgeGUIEditor.cpp index 0a0abc8a57e..70bb62e5d78 100644 --- a/src/surge-xt/gui/SurgeGUIEditor.cpp +++ b/src/surge-xt/gui/SurgeGUIEditor.cpp @@ -2174,6 +2174,12 @@ void SurgeGUIEditor::setParamFromUndo(int paramId, pdata val) synth->refresh_editor = true; } +void SurgeGUIEditor::setStepSequencerFromUndo(int scene, int lfoid, const StepSequencerStorage &val) +{ + synth->storage.getPatch().stepsequences[scene][lfoid] = val; + synth->refresh_editor = true; +} + void SurgeGUIEditor::pushParamToUndoRedo(int paramId, Surge::GUI::UndoManager::Target which) { auto p = synth->storage.getPatch().param_ptr[paramId]; @@ -5315,6 +5321,7 @@ SurgeGUIEditor::layoutComponentForSkin(std::shared_ptrsetLFOStorage(&synth->storage.getPatch().scene[current_scene].lfo[lfo_id]); lfoDisplay->setModSource((modsources)p->ctrlgroup_entry); lfoDisplay->setLFOID(lfo_id); + lfoDisplay->setScene(current_scene); auto msi = 0; if (gui_modsrc[p->ctrlgroup_entry]) diff --git a/src/surge-xt/gui/SurgeGUIEditor.h b/src/surge-xt/gui/SurgeGUIEditor.h index b4def1c602a..662687d0161 100644 --- a/src/surge-xt/gui/SurgeGUIEditor.h +++ b/src/surge-xt/gui/SurgeGUIEditor.h @@ -479,6 +479,7 @@ class SurgeGUIEditor : public Surge::GUI::IComponentTagValue::Listener, void setModulationFromUndo(int paramId, modsources ms, int scene, int idx, float val); void pushModulationToUndoRedo(int paramId, modsources ms, int scene, int idx, Surge::GUI::UndoManager::Target which); + void setStepSequencerFromUndo(int scene, int lfoid, const StepSequencerStorage &val); private: juce::Rectangle positionForModulationGrid(modsources entry); diff --git a/src/surge-xt/gui/UndoManager.cpp b/src/surge-xt/gui/UndoManager.cpp index a4977569f61..173235bc729 100644 --- a/src/surge-xt/gui/UndoManager.cpp +++ b/src/surge-xt/gui/UndoManager.cpp @@ -59,10 +59,16 @@ struct UndoManagerImpl int type; std::vector> paramIdValues; }; + struct UndoStep + { + int scene; + int lfoid; + StepSequencerStorage storageCopy; + }; // If you add a new type here add it both to aboutTheSameThing, toString, and // to undo. - typedef std::variant UndoAction; + typedef std::variant UndoAction; struct UndoRecord { UndoAction action; @@ -88,7 +94,11 @@ struct UndoManagerImpl return (pa->paramId == pb->paramId) && (pa->scene == pb->scene) && (pa->ms == pb->ms) && (pa->index == pb->index); } - + if (auto pa = std::get_if(&a)) + { + auto pb = std::get_if(&b); + return (pa->lfoid == pb->lfoid) && (pa->scene == pb->scene); + } return false; } @@ -113,6 +123,10 @@ struct UndoManagerImpl { return fmt::format("FX[slot={},type={}]", pa->fxslot, pa->type); } + if (auto pa = std::get_if(&a)) + { + return fmt::format("Step[scene={},lfoid={}]", pa->scene, pa->lfoid); + } return "UNK"; } @@ -217,6 +231,19 @@ struct UndoManagerImpl pushRedo(r); } + void pushStepSequencer(int scene, int lfoid, const StepSequencerStorage &pushValue, + UndoManager::Target to = UndoManager::UNDO) + { + auto r = UndoStep(); + r.scene = scene; + r.lfoid = lfoid; + r.storageCopy = pushValue; + if (to == UndoManager::UNDO) + pushUndo(r); + else + pushRedo(r); + } + bool undoRedoImpl(UndoManager::Target which) { auto *currStack = &undoStack; @@ -284,6 +311,13 @@ struct UndoManagerImpl } return true; } + if (auto p = std::get_if(&q)) + { + pushStepSequencer(p->scene, p->lfoid, + editor->getPatch().stepsequences[p->scene][p->lfoid], opposite); + editor->setStepSequencerFromUndo(p->scene, p->lfoid, p->storageCopy); + return true; + } return false; } @@ -339,6 +373,11 @@ void UndoManager::dumpStack() { impl->dumpStack(); } void UndoManager::resetEditor(SurgeGUIEditor *ed) { impl->editor = ed; } +void UndoManager::pushStepSequencer(int scene, int lfoid, const StepSequencerStorage &pushValue) +{ + impl->pushStepSequencer(scene, lfoid, pushValue); +} + } // namespace GUI } // namespace Surge \ No newline at end of file diff --git a/src/surge-xt/gui/UndoManager.h b/src/surge-xt/gui/UndoManager.h index e512444f5a1..44b151deced 100644 --- a/src/surge-xt/gui/UndoManager.h +++ b/src/surge-xt/gui/UndoManager.h @@ -21,6 +21,7 @@ struct SurgeSynthesizer; struct SurgeGUIEditor; +struct StepSequencerStorage; namespace Surge { @@ -46,6 +47,7 @@ struct UndoManager void pushModulationChange(int paramId, modsources modsource, int scene, int index, float val, Target to = UNDO); void pushOscillator(int scene, int oscnum); + void pushStepSequencer(int scene, int lfoid, const StepSequencerStorage &pushValue); void pushFX(int fxslot); bool undo(); bool redo(); diff --git a/src/surge-xt/gui/widgets/LFOAndStepDisplay.cpp b/src/surge-xt/gui/widgets/LFOAndStepDisplay.cpp index 7e96dd972b6..564a2896ebd 100644 --- a/src/surge-xt/gui/widgets/LFOAndStepDisplay.cpp +++ b/src/surge-xt/gui/widgets/LFOAndStepDisplay.cpp @@ -93,12 +93,14 @@ LFOAndStepDisplay::LFOAndStepDisplay(SurgeGUIEditor *e) : guiEditor(e) auto q = std::make_unique>(this, sn); q->onGetValue = [this, i](auto *T) { return ss->steps[i]; }; q->onSetValue = [this, i](auto *T, float f) { + auto bscg = BeginStepGuard(this); ss->steps[i] = f; - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); return; }; q->onJogValue = [this, i](auto *t, int dir, bool isShift, bool isControl) { + auto bscg = BeginStepGuard(this); int step = i; if (step >= 0) { @@ -116,11 +118,12 @@ LFOAndStepDisplay::LFOAndStepDisplay(SurgeGUIEditor *e) : guiEditor(e) else f = limitpm1(f + delt); ss->steps[step] = f; - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); } }; q->onMinMaxDef = [this, i](auto *t, int mmd) { + auto bscg = BeginStepGuard(this); if (mmd == 1) ss->steps[i] = 1.f; if (mmd == -1) @@ -128,7 +131,7 @@ LFOAndStepDisplay::LFOAndStepDisplay(SurgeGUIEditor *e) : guiEditor(e) if (mmd == 0) ss->steps[i] = 0.f; - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); }; stepLayer->addChildComponent(*q); @@ -227,22 +230,25 @@ LFOAndStepDisplay::LFOAndStepDisplay(SurgeGUIEditor *e) : guiEditor(e) l0->step = 1; l0->onGetValue = [this](auto *) { return ss->loop_start; }; l0->onSetValue = [this](auto *, float f) { + auto bscg = BeginStepGuard(this); ss->loop_start = (int)round(f); - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); }; l0->onJogValue = [this](auto *, int dir, bool, bool) { + auto bscg = BeginStepGuard(this); auto n = limit_range(ss->loop_start + dir, 0, 15); ss->loop_start = n; - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); }; l0->onMinMaxDef = [this](auto *, int mmd) { + auto bscg = BeginStepGuard(this); if (mmd == 1) ss->loop_start = ss->loop_end; else ss->loop_start = 0; - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); }; loopEndOverlays[0] = std::move(l0); @@ -254,22 +260,25 @@ LFOAndStepDisplay::LFOAndStepDisplay(SurgeGUIEditor *e) : guiEditor(e) l0->step = 1; l0->onGetValue = [this](auto *) { return ss->loop_end; }; l0->onSetValue = [this](auto *, float f) { + auto bscg = BeginStepGuard(this); ss->loop_end = (int)round(f); - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); }; l0->onJogValue = [this](auto *, int dir, bool, bool) { + auto bscg = BeginStepGuard(this); auto n = limit_range(ss->loop_end + dir, 0, 15); ss->loop_end = n; - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); }; l0->onMinMaxDef = [this](auto *, int mmd) { + auto bscg = BeginStepGuard(this); if (mmd == -1) ss->loop_end = ss->loop_end; else ss->loop_end = 15; - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); }; loopEndOverlays[1] = std::move(l0); @@ -1469,8 +1478,9 @@ void LFOAndStepDisplay::setStepToDefault(const juce::MouseEvent &event) { if (steprect[i].contains(event.position)) { + auto bscg = BeginStepGuard(this); ss->steps[i] = 0.f; - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); } } @@ -1495,6 +1505,7 @@ void LFOAndStepDisplay::setStepValue(const juce::MouseEvent &event) float rx1 = r.getX() + r.getWidth(); if (event.position.x >= rx0 && event.position.x < rx1) { + auto bscg = BeginStepGuard(this); draggedStep = i; float f; @@ -1529,7 +1540,7 @@ void LFOAndStepDisplay::setStepValue(const juce::MouseEvent &event) } ss->steps[i] = f; - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); } @@ -1653,6 +1664,7 @@ void LFOAndStepDisplay::mouseDown(const juce::MouseEvent &event) if (r.contains(event.position)) { + auto bscg = BeginStepGuard(this); dragMode = TRIGGERS; uint64_t maski = ss->trigmask & (UINT64_C(1) << i); @@ -1697,7 +1709,7 @@ void LFOAndStepDisplay::mouseDown(const juce::MouseEvent &event) ss->trigmask &= maskOff; ss->trigmask |= maskOn; - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); return; @@ -1708,6 +1720,7 @@ void LFOAndStepDisplay::mouseDown(const juce::MouseEvent &event) void LFOAndStepDisplay::shiftLeft() { + auto bscg = BeginStepGuard(this); float t = ss->steps[0]; for (int i = 0; i < (n_stepseqsteps - 1); i++) @@ -1724,12 +1737,13 @@ void LFOAndStepDisplay::shiftLeft() (((ss->trigmask & 0x0000fffe00000000) >> 1) | (((ss->trigmask & 0x100000000) << 15) & 0xffff00000000)); - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); } void LFOAndStepDisplay::shiftRight() { + auto bscg = BeginStepGuard(this); float t = ss->steps[n_stepseqsteps - 1]; for (int i = (n_stepseqsteps - 2); i >= 0; i--) @@ -1746,7 +1760,7 @@ void LFOAndStepDisplay::shiftRight() (((ss->trigmask & 0x00007fff00000000) << 1) | (((ss->trigmask & 0x0000800000000000) >> 15) & 0xffff00000000)); - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); } @@ -1927,8 +1941,9 @@ void LFOAndStepDisplay::mouseDrag(const juce::MouseEvent &event) { if (ss->loop_start != loopStart && loopStart >= 0) { + auto bscg = BeginStepGuard(this); ss->loop_start = loopStart; - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); } } @@ -1936,8 +1951,9 @@ void LFOAndStepDisplay::mouseDrag(const juce::MouseEvent &event) { if (ss->loop_end != loopStart && loopStart >= 0) { + auto bscg = BeginStepGuard(this); ss->loop_end = loopStart; - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); } } @@ -2046,6 +2062,7 @@ void LFOAndStepDisplay::mouseUp(const juce::MouseEvent &event) if (s >= 0 && e >= 0 && s != e) // s == e is the abort gesture { + auto bscg = BeginStepGuard(this); float fs = (float)(steprect[s].getBottom() - rmStepStart.y) / steprect[s].getHeight(); float fe = (float)(steprect[e].getBottom() - rmStepCurr.y) / steprect[e].getHeight(); @@ -2067,7 +2084,6 @@ void LFOAndStepDisplay::mouseUp(const juce::MouseEvent &event) } ss->steps[s] = fs; - storage->getPatch().isDirty = true; if (s != e) { @@ -2098,10 +2114,10 @@ void LFOAndStepDisplay::mouseUp(const juce::MouseEvent &event) } ss->steps[q] = f; - storage->getPatch().isDirty = true; } } + stepSeqDirty(); repaint(); } } @@ -2128,6 +2144,8 @@ void LFOAndStepDisplay::mouseWheelMove(const juce::MouseEvent &event, return; } + auto bscg = BeginStepGuard(this); + float delta = wheel.deltaX - (wheel.isReversed ? 1 : -1) * wheel.deltaY; #if MAC delta *= 1.3; @@ -2159,7 +2177,7 @@ void LFOAndStepDisplay::mouseWheelMove(const juce::MouseEvent &event, } ss->steps[i] = v; - storage->getPatch().isDirty = true; + stepSeqDirty(); repaint(); } } @@ -2381,5 +2399,19 @@ bool LFOAndStepDisplay::keyPressed(const juce::KeyPress &key) return false; } +void LFOAndStepDisplay::prepareForEdit() +{ + jassert(stepDirtyCount == 0); + stepDirtyCount++; + undoStorageCopy = *ss; +} + +void LFOAndStepDisplay::stepSeqDirty() +{ + jassert(stepDirtyCount == 1); + storage->getPatch().isDirty = true; + guiEditor->undoManager()->pushStepSequencer(scene, lfoid, undoStorageCopy); +} + } // namespace Widgets } // namespace Surge diff --git a/src/surge-xt/gui/widgets/LFOAndStepDisplay.h b/src/surge-xt/gui/widgets/LFOAndStepDisplay.h index 557a8b70510..a92b2eb1d5a 100644 --- a/src/surge-xt/gui/widgets/LFOAndStepDisplay.h +++ b/src/surge-xt/gui/widgets/LFOAndStepDisplay.h @@ -70,6 +70,11 @@ struct LFOAndStepDisplay : public juce::Component, { lfoid = l; } + int scene{-1}; + void setScene(int l) // from 0 + { + scene = l; + } void populateLFOMS(LFOModulationSource *s); @@ -127,6 +132,23 @@ struct LFOAndStepDisplay : public juce::Component, repaint(); } + struct BeginStepGuard + { + LFOAndStepDisplay *that{nullptr}; + BeginStepGuard(LFOAndStepDisplay *t) : that(t) { that->prepareForEdit(); } + ~BeginStepGuard() { that->postDirtyEdit(); } + }; + void prepareForEdit(); + void postDirtyEdit() + { + jassert(stepDirtyCount == 1); + stepDirtyCount--; + } + void stepSeqDirty(); + + int stepDirtyCount{0}; + StepSequencerStorage undoStorageCopy; + enum DragMode { NONE,