Ψ-W1-T2 review remediation: solo restore drops on visible-in-target, not parked; N-mode segments read dead when unroutable

Fixes a hidden-parent solo replay that could silence the mix. Also closes the N-mode segment silent no-op, amends the invariant comment, trims view.cpp under 600 lines, hedges two SDK inferences, drops a dead null-check.
This commit is contained in:
2026-08-01 20:13:10 -04:00
parent 9c234c2e6b
commit f2cdf676f3
17 changed files with 137 additions and 73 deletions
+18 -17
View File
@@ -44,6 +44,20 @@ TailSetting currentTail() {
return g_panel.session ? g_panel.session->tail() : TailSetting{};
}
// The registered activate action behind a footer mode segment, or 0 if the mode has none.
// Routing the segment through the SAME action the Actions list fires is what gives a
// panel-initiated switch the persist + repaint it used to skip; a mode with no such
// action (the model is N-mode, the UI ships two) resolves to 0 here, which
// modeSegmentEnabled reads as unroutable so the segment paints dead rather than live-
// but-inert. Non-anonymous: panel_render.cpp resolves the same id to compute that bool.
int modeActivateCommandId(const std::string& modeId) {
ActionBarRow row{};
if (modeId == kArrangeModeId) row.suffix = "VIEW_ACTIVATE_ARRANGE";
else if (modeId == kDesignModeId) row.suffix = "VIEW_ACTIVATE_DESIGN";
else return 0;
return resolveBarCommandId(row);
}
namespace {
// Commits the current tail setting to ext state and marks the active project dirty so the
@@ -76,18 +90,6 @@ bool handleToolbarClick(int x, int y, const ActionBarRect& bar,
return true;
}
// The registered activate action behind a footer mode segment. Routing the segment
// through the SAME action the Actions list fires is what gives a panel-initiated switch
// the persist + repaint it used to skip; a mode with no such action (the model is
// N-mode, the UI ships two) is unreachable from the footer rather than routed around it.
int modeActivateCommandId(const std::string& modeId) {
ActionBarRow row{};
if (modeId == kArrangeModeId) row.suffix = "VIEW_ACTIVATE_ARRANGE";
else if (modeId == kDesignModeId) row.suffix = "VIEW_ACTIVATE_DESIGN";
else return 0;
return resolveBarCommandId(row);
}
// True iff `tr` has I_FREEMODE==2 (fixed lanes enabled). Value verified in view.cpp;
// reproduced locally so this file stays self-contained.
constexpr int kFreeModeFixedLanes = 2;
@@ -316,11 +318,10 @@ void handleClick(int x, int y) {
if (seg < static_cast<int>(modes.size())) {
const std::string& id = modes[static_cast<std::size_t>(seg)].id;
const bool isActive = id == view.activeModeId();
// Disabled while the transport runs: claimed but inert, the same shape a
// disabled toolbar row takes — a dead segment reads inert, never absent,
// and never falls through to the grid.
if (modeSegmentEnabled(isActive, g_panel.modeSwitchBlocked)) {
const int cmd = modeActivateCommandId(id);
const int cmd = modeActivateCommandId(id);
// Disabled/dead rationale: core/ui/footer_bar.h. Claimed but inert, the
// same shape a disabled toolbar row takes — never falls through to the grid.
if (modeSegmentEnabled(isActive, g_panel.modeSwitchBlocked, cmd != 0)) {
if (cmd != 0 && Main_OnCommand) Main_OnCommand(cmd, 0);
}
}
+3 -3
View File
@@ -112,9 +112,9 @@ void drawFooter(LICE_IBitmap* bmp, int w, int h) {
const SegmentRect& s = segs[static_cast<std::size_t>(i)];
const Mode& mode = modes[static_cast<std::size_t>(i)];
const bool active = mode.id == activeId;
// A switch is refused while the transport runs, so an unreachable segment
// draws dead rather than inviting a click that would silently do nothing.
const bool live = modeSegmentEnabled(active, g_panel.modeSwitchBlocked);
// Disabled/dead rationale: core/ui/footer_bar.h.
const bool live = modeSegmentEnabled(active, g_panel.modeSwitchBlocked,
modeActivateCommandId(mode.id) != 0);
const InteractionState state =
active ? InteractionState::Active
: (live ? hoverState(g_panel.hovered, HoverKind::ModeSegment, i)
+7 -4
View File
@@ -292,10 +292,10 @@ struct PanelState {
unsigned int hoverSinceTick = 0;
bool tooltipShown = false;
// Polled on the OnTimer tick (REAPER exposes no transport-change callback): a mode
// switch is refused while the transport runs, so the footer's [Arrange|Design]
// segments paint disabled. Cached rather than read per paint AND per click so the
// pixel the user saw and the click they made cannot disagree within a tick.
// Polled on the OnTimer tick (REAPER exposes no transport-change callback). Feeds
// modeSegmentEnabled (core/ui/footer_bar.h). Cached rather than read per paint AND
// per click so the pixel the user saw and the click they made cannot disagree
// within a tick.
bool modeSwitchBlocked = false;
BankPanelFullHeight fullHeight = BankPanelFullHeight::Split;
@@ -485,6 +485,9 @@ void startAudition(int idx);
// panel_input.cpp — click/wheel/key routing, accelerator, new-content detection,
// and the session-tail read/mutate helpers.
TailSetting currentTail();
// The registered activate action behind a footer mode segment, or 0 if unroutable.
// Shared with panel_render.cpp so paint and click resolve the same id.
int modeActivateCommandId(const std::string& modeId);
void handleClick(int x, int y);
bool handleWheel(int x, int y, int delta);
void registerAccel();