fix(view): park untagged leaves in non-Arrange modes; refresh TCP/MCP on apply

planToggle now enumerates all leaves in the FolderTree instead of only tagged
membership entries, so untagged (Arrange-member) tracks park in Design and
restore in Arrange. applyMode calls TrackList_AdjustWindows + UpdateArrange so
changes render immediately. Correct the stale "tagged-only" invariant comments.
This commit is contained in:
2026-07-23 04:59:50 -04:00
parent 60add4afc7
commit 4bf394b89c
5 changed files with 130 additions and 22 deletions
+88
View File
@@ -383,6 +383,92 @@ static void testPlanToggleParkHasEmptyFxOffline() {
CHECK(plan.park[0].flags.size() == 4);
}
// -- 7b. Untagged leaves are managed by the mode system ----------------------
//
// The core semantic fix: an untagged leaf is an Arrange member. planToggle must
// enumerate EVERY leaf in the tree (not just membership_.all()), so an untagged
// leaf — absent from the membership index — parks in every non-Arrange mode and
// restores in Arrange, identically to a tagged leaf. Parents and show-both leaves
// remain never-parked. Tagged-leaf behavior is unchanged.
static void testUntaggedLeavesManagedByModeSystem() {
ViewModeModel vm;
// A tree of leaves NONE of which are in the membership index (all untagged),
// plus a parent folder and a show-both leaf to prove they stay untouched.
FolderTree tree;
tree.nodes.push_back(FolderNode{"{P}", "", /*isParent=*/true});
tree.nodes.push_back(FolderNode{"{U1}", "{P}", false}); // untagged leaf
tree.nodes.push_back(FolderNode{"{U2}", "{P}", false}); // untagged leaf
tree.nodes.push_back(FolderNode{"{SB}", "", false}); // show-both leaf
vm.membership().setShowBoth("{SB}", true);
// (iii) Enumeration covers leaves absent from the membership index: {U1}/{U2}
// are NOT in membership_.all(), yet the planner reaches them.
CHECK(vm.membership().query("{U1}") == nullptr);
CHECK(vm.membership().query("{U2}") == nullptr);
// (i) Toggling to Design (non-Arrange): every untagged leaf is parked.
auto toDesign = vm.planToggle(tree, kDesignModeId);
CHECK(parkTargets(toDesign, "{U1}"));
CHECK(parkTargets(toDesign, "{U2}"));
// (iv) The parent and the show-both leaf are NEVER parked, in either mode.
CHECK(!parkTargets(toDesign, "{P}"));
CHECK(!parkTargets(toDesign, "{SB}"));
// (ii) Toggling to Arrange: the untagged leaves are Arrange members ⇒ active and
// NOT parked. (No snapshot stored yet ⇒ no restore op either; just not parked.)
auto toArrange = vm.planToggle(tree, kArrangeModeId);
CHECK(!parkTargets(toArrange, "{U1}"));
CHECK(!parkTargets(toArrange, "{U2}"));
CHECK(restoreFor(toArrange, "{U1}") == nullptr);
CHECK(!parkTargets(toArrange, "{P}"));
CHECK(!parkTargets(toArrange, "{SB}"));
// (vi) Restore-from-snapshot fidelity for a previously-parked UNTAGGED leaf:
// an untagged leaf parked while in Design carries a snapshot; toggling back to
// Arrange restores it from that snapshot verbatim, never a hardcoded default.
TrackSnapshot snap;
snap.showInTcp = 1; snap.showInMixer = 1; snap.mainSend = 0; snap.fxEnable = 1;
snap.fxOffline = {0, 1};
vm.storeSnapshot("{U1}", snap); // as the shell would, before parking it in Design
auto backToArrange = vm.planToggle(tree, kArrangeModeId);
const TrackPlan* r = restoreFor(backToArrange, "{U1}");
CHECK(r != nullptr);
if (r) {
CHECK(flagValue(*r, Flag::ShowInTcp) == 1);
CHECK(flagValue(*r, Flag::ShowInMixer) == 1);
CHECK(flagValue(*r, Flag::MainSend) == 0); // captured 0 comes back 0
CHECK(flagValue(*r, Flag::FxEnable) == 1);
CHECK(r->fxOffline.size() == 2);
CHECK(r->fxOffline[0].offline == false);
CHECK(r->fxOffline[1].offline == true);
}
}
// (v) Tagged-leaf park/restore behavior is unchanged after the untagged fix: a leaf
// tagged Design parks in Arrange and is active (not parked) in Design, and a mix of
// tagged + untagged leaves each land on the correct side of the toggle.
static void testTaggedLeafBehaviorUnchangedWithUntagged() {
ViewModeModel vm;
FolderTree tree;
tree.nodes.push_back(FolderNode{"{DES}", "", false}); // tagged into Design
tree.nodes.push_back(FolderNode{"{UNT}", "", false}); // untagged ⇒ Arrange
vm.membership().tag("{DES}", kDesignModeId);
// In Design: {DES} active (not parked); {UNT} inactive ⇒ parked.
auto design = vm.planToggle(tree, kDesignModeId);
CHECK(!parkTargets(design, "{DES}"));
CHECK(parkTargets(design, "{UNT}"));
// In Arrange: {DES} inactive ⇒ parked; {UNT} active (not parked).
auto arrange = vm.planToggle(tree, kArrangeModeId);
CHECK(parkTargets(arrange, "{DES}"));
CHECK(!parkTargets(arrange, "{UNT}"));
}
// -- 8. nextModeId cycle (D4 toggle helper) ----------------------------------
static void testNextModeIdCycles() {
@@ -417,6 +503,8 @@ int main() {
testEmptyModelRoundTrip();
testMalformedJson();
testPlanToggleParkHasEmptyFxOffline();
testUntaggedLeavesManagedByModeSystem();
testTaggedLeafBehaviorUnchangedWithUntagged();
testNextModeIdCycles();
if (g_fail == 0) std::printf("All tests passed.\n");