fix(view): redraw arrange when inserted item lands on already-split lane
An item inserted onto an already-split folder in Design mode sat on the correct lane but stayed invisible until a manual toggle: mintManagedLanes returned on the idempotent no-op path before its UpdateArrange call. Force the redraw there so the item appears immediately.
This commit is contained in:
+18
-4
@@ -595,11 +595,25 @@ bool mintManagedLanes(ViewModeModel& model, ReaProject* proj) {
|
||||
const bool changed = applyMintPlan(model, plan, handleByGuid);
|
||||
|
||||
if (!changed) {
|
||||
// The plan was non-empty but every write was already satisfied (idempotent
|
||||
// re-run: lanes exist, items already assigned, ownership already recorded). Close
|
||||
// the block with no description so REAPER discards the empty undo point rather
|
||||
// than flooding history with a no-change entry every detection tick.
|
||||
// The plan was non-empty but every REAPER write was already satisfied. Close the
|
||||
// block with no description so REAPER discards the empty undo point rather than
|
||||
// flooding history with a no-change entry every detection tick.
|
||||
Undo_EndBlock2(proj, "", 0);
|
||||
|
||||
// BUT the arrange still needs a redraw. On the detect-tick caller (bankPanelRefresh)
|
||||
// mintManagedLanes runs only when this tick just tagged new content, and a NON-EMPTY
|
||||
// plan means that content sits on a managed-split track. The idempotent no-op path is
|
||||
// reached when a freshly-inserted item ALREADY landed on the active mode's playing
|
||||
// lane (REAPER places a new item on the playing lane; the active mode's lane IS the
|
||||
// playing lane, so assignItemToLane sees I_FIXEDLANE unchanged and writes nothing).
|
||||
// The item is correctly placed and confined, but the arrange was never told to
|
||||
// repaint it onto the lane — so it stayed invisible until a manual mode toggle forced
|
||||
// applyMode's refresh. Force the redraw here so the item appears immediately without a
|
||||
// toggle. UpdateArrange() only repaints (no I_FREEMODE transition happened on this
|
||||
// path, so UpdateTimeline is not owed); it is NOT a project mutation, so it stays
|
||||
// outside the undo block and adds no history entry. On the action caller (doMoveItems)
|
||||
// this is a harmless repaint immediately before its own reapplyActiveMode() refresh.
|
||||
UpdateArrange();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1459,6 +1459,50 @@ static void testLaneMintingFolderOwnItemsSpanBothModes() {
|
||||
CHECK(hasAssign(plan, "{d}", "{F}", kDesignModeId));
|
||||
}
|
||||
|
||||
// -- Fix (pd2): item inserted onto an ALREADY-split folder still yields a non-empty plan --
|
||||
//
|
||||
// Regression guard for the "inserted item invisible until a manual toggle" bug. When a new
|
||||
// item lands (via insert/capture) on a folder that is ALREADY lane-split and derived-visible
|
||||
// in >1 mode, and REAPER placed it on the active mode's currently-playing lane, the shell's
|
||||
// assignItemToLane sees I_FIXEDLANE unchanged and writes nothing — applyMintPlan reports
|
||||
// changed==false. The shell must STILL treat this tick as "content landed on a managed track"
|
||||
// and refresh the arrange (so the item draws immediately, no toggle). The pure signal the
|
||||
// shell keys on is: planLaneMinting returns a NON-EMPTY plan carrying an assign for the new
|
||||
// item. This test locks that signal; if planLaneMinting ever went empty here, the shell would
|
||||
// have nothing to refresh on and the bug would return.
|
||||
static void testLaneMintingNewItemOnAlreadySplitFolderYieldsPlan() {
|
||||
ViewModeModel vm;
|
||||
vm.membership().tag("{LD}", kDesignModeId); // Design leaf ⇒ folder derived-visible Design
|
||||
// {LA} untagged ⇒ Arrange ⇒ folder ALSO derived-visible in Arrange (dual-visible).
|
||||
vm.membership().tag("{own}", kDesignModeId); // the pre-existing own Design item
|
||||
vm.membership().tag("{new}", kDesignModeId); // the JUST-INSERTED item (auto-tagged Design)
|
||||
|
||||
FolderTree tree;
|
||||
tree.nodes.push_back(FolderNode{"{F}", "", /*isParent=*/true});
|
||||
tree.nodes.push_back(FolderNode{"{LD}", "{F}", false});
|
||||
tree.nodes.push_back(FolderNode{"{LA}", "{F}", false});
|
||||
|
||||
// The folder is already split for Design (its lane exists + is owned). This mirrors the
|
||||
// live "already auto-split" track the bug reproduces on.
|
||||
CHECK(vm.lanes().setManaged("{F}", laneNameForMode(kDesignModeId), kDesignModeId));
|
||||
|
||||
// {F} now carries its original own item PLUS the freshly-inserted one, both Design.
|
||||
std::vector<LaneTrack> tracks{
|
||||
LaneTrack{"{F}", {
|
||||
LaneItem{"{own}", kDesignModeId, false},
|
||||
LaneItem{"{new}", kDesignModeId, false},
|
||||
}},
|
||||
};
|
||||
const LaneMintPlan plan = planLaneMinting(vm, tree, tracks);
|
||||
|
||||
// The plan is NON-EMPTY (folder is dual-visible ⇒ splits) and carries an assign for the
|
||||
// new item onto the Design lane. In the shell this is the exact branch that must force a
|
||||
// redraw even when the assign is an idempotent no-op (item already on the playing lane).
|
||||
CHECK(!plan.empty());
|
||||
CHECK(hasAssign(plan, "{new}", "{F}", kDesignModeId));
|
||||
CHECK(hasAssign(plan, "{own}", "{F}", kDesignModeId));
|
||||
}
|
||||
|
||||
// SHOW-BOTH escape hatch: a show-both track carrying its own items is visible in every
|
||||
// mode ON PURPOSE and must NOT be force-split — its content stays cross-mode-visible.
|
||||
// Even with own items that would otherwise span modes, the decision skips it entirely.
|
||||
@@ -1631,6 +1675,7 @@ int main() {
|
||||
testLaneMintingFolderDerivedVisibleSplitsOwnMedia();
|
||||
testLaneMintingLazySingleLaneStillConfines();
|
||||
testLaneMintingFolderOwnItemsSpanBothModes();
|
||||
testLaneMintingNewItemOnAlreadySplitFolderYieldsPlan();
|
||||
testLaneMintingShowBothNotForceSplit();
|
||||
testLaneMintingSingleModeLeafVisibleOnceNoSplit();
|
||||
testLaneMintingEmptyFolderNotSplit();
|
||||
|
||||
Reference in New Issue
Block a user