Merge: redraw arrange when inserted item lands on already-split lane
This commit is contained in:
+18
-4
@@ -595,11 +595,25 @@ bool mintManagedLanes(ViewModeModel& model, ReaProject* proj) {
|
|||||||
const bool changed = applyMintPlan(model, plan, handleByGuid);
|
const bool changed = applyMintPlan(model, plan, handleByGuid);
|
||||||
|
|
||||||
if (!changed) {
|
if (!changed) {
|
||||||
// The plan was non-empty but every write was already satisfied (idempotent
|
// The plan was non-empty but every REAPER write was already satisfied. Close the
|
||||||
// re-run: lanes exist, items already assigned, ownership already recorded). Close
|
// block with no description so REAPER discards the empty undo point rather than
|
||||||
// the block with no description so REAPER discards the empty undo point rather
|
// flooding history with a no-change entry every detection tick.
|
||||||
// than flooding history with a no-change entry every detection tick.
|
|
||||||
Undo_EndBlock2(proj, "", 0);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1459,6 +1459,50 @@ static void testLaneMintingFolderOwnItemsSpanBothModes() {
|
|||||||
CHECK(hasAssign(plan, "{d}", "{F}", kDesignModeId));
|
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
|
// 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.
|
// 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.
|
// Even with own items that would otherwise span modes, the decision skips it entirely.
|
||||||
@@ -1631,6 +1675,7 @@ int main() {
|
|||||||
testLaneMintingFolderDerivedVisibleSplitsOwnMedia();
|
testLaneMintingFolderDerivedVisibleSplitsOwnMedia();
|
||||||
testLaneMintingLazySingleLaneStillConfines();
|
testLaneMintingLazySingleLaneStillConfines();
|
||||||
testLaneMintingFolderOwnItemsSpanBothModes();
|
testLaneMintingFolderOwnItemsSpanBothModes();
|
||||||
|
testLaneMintingNewItemOnAlreadySplitFolderYieldsPlan();
|
||||||
testLaneMintingShowBothNotForceSplit();
|
testLaneMintingShowBothNotForceSplit();
|
||||||
testLaneMintingSingleModeLeafVisibleOnceNoSplit();
|
testLaneMintingSingleModeLeafVisibleOnceNoSplit();
|
||||||
testLaneMintingEmptyFolderNotSplit();
|
testLaneMintingEmptyFolderNotSplit();
|
||||||
|
|||||||
Reference in New Issue
Block a user