view: split the deferred FX-park drain by kind — restores stay forced, parks go one FX per idle tick behind a 1s coalescing delay

A rapid A→B→A flip now costs no plugin work: the restore cancels the still-pending
park outright. A park that has already written one FX carries a `partial` flag and is
superseded by its inverse rather than cancelled, so a half-parked chain is never stranded.
This commit is contained in:
2026-08-03 15:00:40 -04:00
parent 1159d364c2
commit 6e2128e937
7 changed files with 341 additions and 89 deletions
+6 -4
View File
@@ -26,7 +26,7 @@
#include "shell/capture/track_guid.h" // shared MediaTrack* -> canonical GUID key
#include "shell/panel/panel_window.h" // bankPanelInvalidate — footer toggle repaint
#include "shell/view/view.h" // applyMode + mintManagedLanes (D2 shell)
#include "shell/view/view_fx_park.h" // drainDeferredFxParks — see persistViewState
#include "shell/view/view_fx_park.h" // drainDeferredFxRestores — see persistViewState
#define REAPERAPI_MINIMAL
#define REAPERAPI_WANT_CountSelectedTracks
@@ -148,9 +148,11 @@ void persistViewState(PersistScope scope) {
// BEFORE the model is serialized, and before the Save-As below can write a
// .rpp: a deferred FX restore leaves the chain offline while the model has
// already dropped the snapshot that would replan it. Why that combination is
// unrecoverable on reopen is at drainDeferredFxParks. Runs for BOTH scopes —
// the narrowed save still writes the key the half-applied park contradicts.
drainDeferredFxParks();
// unrecoverable on reopen is at drainDeferredFxRestores. Restores only — a
// pending PARK is safe to save over and converges on reopen. Runs for BOTH
// scopes: the narrowed save still writes the key the half-applied restore
// contradicts.
drainDeferredFxRestores();
if (!g_session->view().membership().empty()) {
ReaProject* proj = EnumProjects(-1, nullptr, 0);