Reconcile orphaned Design View snapshots on track delete

Add ViewModeModel::reconcile(liveGuids) to prune snapshots of deleted
tracks; wire it into applyMode before planning. Keep membership so
undo-delete (same GUID) preserves the Design tag.
This commit is contained in:
2026-07-23 13:27:42 -04:00
parent dcbe5d7bdc
commit f9af111c7a
4 changed files with 141 additions and 0 deletions
+20
View File
@@ -267,6 +267,26 @@ public:
const TrackSnapshot* snapshot(const std::string& guid) const;
const std::map<std::string, TrackSnapshot>& snapshots() const { return snapshots_; }
// Prunes orphaned per-track state: drops every snapshot whose GUID is NOT in
// `liveGuids` (the set of GUIDs the shell currently enumerates from the project).
// Returns the number of snapshots removed. The shell calls this before planning a
// toggle; because reapply-on-load also routes through the shell's applyMode, this
// reconciles on project open too.
//
// Why snapshots and NOT membership: a parked track's snapshot is dead weight once
// the track is deleted — it can never be restored, and if REAPER reuses that GUID
// for a different track a stale snapshot would drive an INCORRECT restore. So it
// must be pruned. Membership is deliberately KEPT: REAPER's undo of a track delete
// restores the SAME GUID, so dropping the Design tag on delete would silently lose
// it on undo-delete. Keeping membership means an undone delete brings the track
// back correctly tagged and it re-snapshots + re-parks cleanly on the next toggle.
// A genuinely-deleted-and-never-restored track leaves only a tiny dormant
// membership entry — acceptable, and far better than losing tags on undo. Folder
// RESTRUCTURE (moving tracks without deleting) is already self-healing: the tree is
// rebuilt from I_FOLDERDEPTH every toggle, so a restructure leaves every GUID live
// and reconcile is a no-op over it. This handles DELETION specifically.
std::size_t reconcile(const std::set<std::string>& liveGuids);
// Does `guid` belong to `modeId`? A leaf belongs if it is tagged into modeId,
// is show-both (belongs everywhere), or is untagged and modeId is Arrange (the
// default). Parent derivation is NOT applied here — this is the LEAF rule; use