b2f6b3f1d0
Every opener ends the span first, so a tag fired mid-drain no longer hijacks the switch's point. One stated criterion governs both lane sites; no-op actions discard.
76 lines
4.1 KiB
C++
76 lines
4.1 KiB
C++
#pragma once
|
|
// REAPER-facing shell of Design View: reads the live folder tree, runs
|
|
// ViewModeModel's pure planner, and applies the resulting flag / lane / parent
|
|
// writes — under ONE undo block on a real switch, under none at all on a reapply.
|
|
// See src/shell/view/CLAUDE.md for the enforced invariants (never touch
|
|
// master/mute, solo cached per mode and never lost, record-based restore).
|
|
|
|
#include <string>
|
|
|
|
#include "core/view/view_mode_model.h"
|
|
|
|
// Forward-declared to keep this header SDK-free; the .cpp includes the real SDK header.
|
|
class ReaProject;
|
|
|
|
namespace reasampler {
|
|
|
|
// True while `proj`'s transport is playing or recording — the condition under which
|
|
// applyMode refuses a real mode switch. Exposed so the panel can paint the footer's
|
|
// [Arrange|Design] segments disabled BEFORE the click rather than only refusing on
|
|
// it. `proj` is passed through to GetPlayStateEx as-is; the SDK documents proj=0
|
|
// meaning "current project" for GetTrack but is silent on GetPlayStateEx, so
|
|
// `nullptr` meaning the current project here is inferred by analogy, not confirmed.
|
|
bool transportBlocksModeSwitch(ReaProject* proj);
|
|
|
|
// True while an applyMode call is on the stack. applyMode fails closed when
|
|
// re-entered, and this lets a caller with no fallback test BEFORE it spends a
|
|
// one-shot signal on a call that would be refused — see main.cpp's load glue.
|
|
bool modeApplyInProgress();
|
|
|
|
// What one applyMode did.
|
|
//
|
|
// `wroteProjectState` counts REAPER writes ONLY — a driven flag, a lane parameter,
|
|
// a parent's visibility. Model moves (a snapshot stored, a record's flag half spent,
|
|
// the active mode set) are deliberately excluded: they reach disk through
|
|
// persistViewState, which every caller runs OUTSIDE its undo block, so no point can
|
|
// carry them and counting them would only mint an empty one. It is the answer a
|
|
// caller holding its own block needs for the discard decision (design_view_actions'
|
|
// ActionUndoBlock); the solo pair cannot affect it, since only a real switch drives
|
|
// solo and a real switch mints regardless.
|
|
struct ModeApplyResult {
|
|
bool applied = false;
|
|
bool wroteProjectState = false;
|
|
};
|
|
|
|
// Captures each about-to-park track's prior flag values into `model`, caches/clears
|
|
// the outgoing mode's solo state and replays the incoming mode's, runs planToggle,
|
|
// applies the park/restore flag writes plus managed-lane and parent-visibility
|
|
// writes, then sets the active mode. ONE switch is ONE undo point; everything it
|
|
// moved rolls back in a single Ctrl-Z. A REAPPLY leaves no point at all — it opens no
|
|
// block, because none of what it writes mints one on its own.
|
|
//
|
|
// FX are NOT written here. Offlining a parked chain is the reclaimer's job, off this
|
|
// path: TrackFX_SetOffline was 1897ms of a measured 1942ms switch, against 0.02ms for
|
|
// all the flag work below. Do not reintroduce a synchronous FX write.
|
|
//
|
|
// A REAL switch therefore RETURNS WITH ITS UNDO BLOCK STILL OPEN (fx_reclaim's
|
|
// UndoSpan): the reclaimer closes it once the parking has drained, which is what keeps
|
|
// the switch and its FX writes ONE point rather than two. A reapply opens none, so a
|
|
// caller that wants its own act undoable opens its own (design_view_actions does) —
|
|
// and, because REAPER's blocks are a depth counter, ENDS THE SPAN BEFORE IT OPENS
|
|
// THAT BLOCK. A reapply never closes a span, precisely because it can be running
|
|
// inside such a caller's block.
|
|
//
|
|
// `applied` is false (no mutation) if `targetModeId` isn't registered, if this is a
|
|
// real switch (target != active) while the transport is running, if it is re-entered
|
|
// (modeApplyInProgress), or if the project does not validate. A reapply (target ==
|
|
// active) is never gated and never touches solo. `proj` == nullptr means the current
|
|
// project, resolved ONCE and used for every call this apply makes.
|
|
//
|
|
// Either way — applied or refused — this drops the FX reclaimer's derived queue, which
|
|
// is why callers may safely discard the return after mutating the model.
|
|
ModeApplyResult applyMode(ViewModeModel& model, const std::string& targetModeId,
|
|
ReaProject* proj);
|
|
|
|
} // namespace reasampler
|