Fix parent visibility: parent visible by own membership OR derived from children

visibleTracks pass 1 now evaluates every node (parent or leaf) by its own
membership, so an untagged folder carrying its own FX/media shows in Arrange
(its default) as well as any mode derived from its children. Adds case-matrix
tests; updates the nested-Arrange assertion to the corrected rule.
This commit is contained in:
2026-07-23 06:17:34 -04:00
parent 4f1231ea81
commit 8d08279bdb
3 changed files with 134 additions and 15 deletions
+16 -9
View File
@@ -172,24 +172,31 @@ std::set<std::string> ViewModeModel::visibleTracks(const FolderTree& tree,
const std::string& modeId) const {
std::set<std::string> visible;
// Pass 1: every leaf that belongs to the mode is visible.
// Pass 1: every node — leaf OR parent — that belongs to the mode by its OWN
// membership is visible. For a leaf this is the tagged/show-both/untagged-Arrange
// rule; for a parent it means an untagged folder (which carries its own FX/media
// and defaults to Arrange) shows in Arrange even when none of its children do.
// Parents ALSO become visible in pass 2 by derivation from a visible descendant;
// the two rules are OR'd, so an untagged folder of all-Design leaves shows in both
// Arrange (own default) and Design (derived).
for (const auto& node : tree.nodes) {
if (node.isParent) continue; // parents derived in pass 2
if (leafBelongsToMode(node.guid, modeId))
visible.insert(node.guid);
}
// Pass 2: a parent is visible if any descendant leaf is visible. Walk each
// visible leaf up its parent chain and mark ancestors. Parent chains are read
// from the supplied tree only (no REAPER access). A cycle-guard bounds the walk
// in case a malformed tree links a node to itself.
// Pass 2: a parent is also visible if any descendant is visible. Walk each
// currently-visible node up its parent chain and mark ancestors. Seeding from the
// full pass-1 set means a parent made visible by its own membership propagates its
// visibility up the remaining ancestors too. Parent chains are read from the
// supplied tree only (no REAPER access). A cycle-guard bounds the walk in case a
// malformed tree links a node to itself.
std::map<std::string, std::string> parentOf;
for (const auto& node : tree.nodes) parentOf[node.guid] = node.parentGuid;
// Snapshot the leaf-visible set so we don't re-walk parents we add mid-loop.
// Snapshot the pass-1 visible set so we don't re-walk parents we add mid-loop.
const std::vector<std::string> seeds(visible.begin(), visible.end());
for (const auto& leaf : seeds) {
auto it = parentOf.find(leaf);
for (const auto& node : seeds) {
auto it = parentOf.find(node);
std::size_t guard = 0;
while (it != parentOf.end() && !it->second.empty() && guard++ < parentOf.size()) {
const std::string& parent = it->second;
+8 -4
View File
@@ -150,8 +150,9 @@ private:
// -- Folder tree (INPUT, not stored) ----------------------------------------
//
// The shell builds this from I_FOLDERDEPTH each time and passes it to a visibility
// query. A node is a leaf or a parent; a parent is visible in every mode any of
// its descendant leaves belongs to, and is never parked. The master track is
// query. A node is a leaf or a parent; a parent is visible in a mode if it belongs
// to that mode by its own membership OR any of its descendant leaves does, and is
// never parked. The master track is
// modeled implicitly (always visible, never touched) and is NOT a node here.
struct FolderNode {
std::string guid;
@@ -273,8 +274,11 @@ public:
bool leafBelongsToMode(const std::string& guid, const std::string& modeId) const;
// The set of track GUIDs visible in `modeId`, tree-aware: active leaves,
// show-both leaves, and every parent with at least one descendant leaf in the
// mode. Untagged leaves count as Arrange. Stale GUIDs in the tree are tolerated.
// show-both leaves, and every parent that EITHER belongs to the mode by its own
// membership OR has at least one descendant visible in the mode. Untagged nodes
// (leaf or folder) count as Arrange, so an untagged folder carrying its own
// FX/media shows in Arrange even when none of its children do, and additionally
// shows in a child's mode by derivation. Stale GUIDs in the tree are tolerated.
// The master is not represented (always visible; the shell never touches it).
std::set<std::string> visibleTracks(const FolderTree& tree,
const std::string& modeId) const;