Strip FX writes from the Design View mode switch; split view.cpp into park/lanes/reconcile/tracks and add the record-scoped undo repair pass

This commit is contained in:
2026-08-06 17:19:43 -04:00
parent d455b9a2fa
commit c4038a9e9c
22 changed files with 1172 additions and 1146 deletions
+31 -42
View File
@@ -1,10 +1,9 @@
#pragma once
// REAPER-facing shell of Design View (D2): reads the live folder tree, runs
// ViewModeModel's pure planner, and applies the resulting flag / per-FX /
// lane writes. The .cpp is the sole REAPER-facing TU here (CLAUDE.md contract:
// only main.cpp defines the API pointers). See src/shell/view/CLAUDE.md for
// the enforced invariants (never touch master/mute, solo cached per mode and
// never lost, snapshot-based restore).
// REAPER-facing shell of Design View: reads the live folder tree, runs
// ViewModeModel's pure planner, and applies the resulting flag / lane / parent
// writes under ONE undo block. See src/shell/view/CLAUDE.md for the enforced
// invariants (never touch master/mute, solo cached per mode and never lost,
// record-based restore).
#include <string>
@@ -25,51 +24,41 @@ bool transportBlocksModeSwitch(ReaProject* proj);
// Does one applyMode leave an undo point behind?
//
// A REAPPLY that wrote nothing must not: the load-tick reapply over a project
// saved fully parked re-plans a park for every inactive leaf and finds every
// flag and every FX already where it wants them, so a point there would mean
// opening a project costs an undo step. `Undo_EndBlock2(proj, "", 0)` is the
// discard form (mintManagedLanes' no-op path uses the same idiom).
// A REAPPLY that wrote nothing must not: the load-tick reapply over a project saved
// fully parked re-plans a park for every inactive leaf and finds every flag already
// where it wants them, so a point there would mean opening a project costs an undo
// step. `Undo_EndBlock2(proj, "", 0)` is the discard form (mintManagedLanes' no-op
// path and the reconcile pass use the same idiom).
//
// A real switch mints regardless, fail-safe: it is an explicitly fired action
// and has to stay undoable even in the degenerate case where the plan found
// nothing to write. Note what applyMode CANNOT see when it decides — the
// `view_state` ext-state write lands in persistViewState, after this block has
// closed (src/shell/view/CLAUDE.md's Gotchas).
// A real switch mints regardless, fail-safe: it is an explicitly fired action and has
// to stay undoable even in the degenerate case where the plan found nothing to write.
// Note what applyMode CANNOT see when it decides — the `view_state` ext-state write
// lands in persistViewState, after this block has closed (src/shell/view/CLAUDE.md's
// Gotchas).
inline bool applyMintsUndoPoint(bool realSwitch, bool wroteAnything) {
return realSwitch || wroteAnything;
}
// True while an applyMode call is on the stack. applyMode fails closed when re-entered
// (its FX writes are assumed to pump the message loop, so a timer tick or an action can
// land inside one), and this lets a caller with no fallback test BEFORE it spends a
// True while an applyMode call is on the stack. applyMode fails closed when
// re-entered, and this lets a caller with no fallback test BEFORE it spends a
// one-shot signal on a call that would be refused — see main.cpp's load glue.
bool modeApplyInProgress();
// Snapshots each about-to-park track's flags into `model`, caches/clears the
// outgoing mode's solo state and replays the incoming mode's, runs planToggle,
// applies park/restore writes — flags AND per-FX offline, synchronously — plus
// parent visibility flags, then sets the active mode. ONE switch is ONE undo
// point; everything it moved rolls back in a single Ctrl-Z. Returns false (no
// mutation) if `targetModeId` isn't registered, if this is a real switch
// (target != active) while the transport is running, if it is re-entered
// Captures each about-to-park track's prior flag values into `model`, caches/clears
// the outgoing mode's solo state and replays the incoming mode's, runs planToggle,
// applies the park/restore flag writes plus managed-lane and parent-visibility
// writes, then sets the active mode. ONE switch is ONE undo point; everything it
// moved rolls back in a single Ctrl-Z.
//
// FX are NOT written here. Offlining a parked chain is the reclaimer's job, off this
// path: TrackFX_SetOffline was 1897ms of a measured 1942ms switch, against 0.02ms for
// all the flag work below. Do not reintroduce a synchronous FX write.
//
// Returns false (no mutation) if `targetModeId` isn't registered, if this is a real
// switch (target != active) while the transport is running, if it is re-entered
// (modeApplyInProgress), or if the project does not validate. A reapply (target ==
// active) is never gated and never touches solo. `proj` == nullptr means the
// current project, resolved ONCE and used for every call this apply makes.
// active) is never gated and never touches solo. `proj` == nullptr means the current
// project, resolved ONCE and used for every call this apply makes.
bool applyMode(ViewModeModel& model, const std::string& targetModeId, ReaProject* proj);
// Splits any track visible in more than one mode while carrying its own media
// into fixed lanes (one managed lane per involved mode), assigns items, and
// records ownership in `model`. Never touches manual lanes. Runs planLaneMinting
// (model + tree aware); wraps the mutation in one Undo block when non-empty.
// Returns true if any lane was minted (repaint hint). `proj` == nullptr means
// the current project.
bool mintManagedLanes(ViewModeModel& model, ReaProject* proj);
// Recovers managed-lane ownership from durable P_LANENAME on project open —
// self-healing, without minting/reassigning anything and without touching
// membership. A lane without the managed prefix is left untouched (manual).
// `proj` == nullptr means the current project.
void reconcileManagedLanes(ViewModeModel& model, ReaProject* proj);
} // namespace reasampler