fix: close review findings on spline EGs — engine, codec, and popup/overlay UI grammar

Live pitch depth, Gate/Spline enable-rule agreement, inert kTrigLength, NaN wire guards, hard-flag-tail corruption no longer wipes the record, RT/cold spline tie-break, retired alt-click, marker-shadow fix, plus new test coverage.
This commit is contained in:
2026-07-31 22:27:41 -04:00
parent e44bd42dd9
commit 1c774226d3
22 changed files with 414 additions and 88 deletions
+7 -3
View File
@@ -36,9 +36,8 @@ std::vector<DeckGroupDesc> sampleDeckGroups(PlayMode playMode) {
penv.captionWidth = 58;
penv.captionRadio = {id(DeckParam::kPitchEnvSelect)};
penv.captionToggle = {id(DeckParam::kPitchEnvEnable), 32};
// The mode toggle rides the caption slack rather than the knob row: every env group's
// knob row is wider than its caption row, so this costs no group width — and the deck
// has six pixels of headroom on its first row at the editor's floor width.
// The mode toggle rides the caption slack rather than the knob row — costs no group
// width; see this module's CLAUDE.md bullet (knob_deck) for the headroom this relies on.
penv.captionToggle2 = {id(DeckParam::kPitchEnvMode), kEnvModeSegW};
penv.cellIds = {id(DeckParam::kPitchEnvAttack),
id(DeckParam::kPitchEnvHold),
@@ -289,6 +288,11 @@ bool deckKnobInert(DeckParam id, const DeckEnableState& state) {
case DeckParam::kTrigHold:
case DeckParam::kTrigDecay:
return state.ampSpline;
// The Trigger %-length knob is inert whenever ANY spline is active (not just the amp's):
// a drawn contour always covers the full sample length (splineActive, play_params.h), so
// the engine ignores lengthFraction in that case regardless of which EG is drawn.
case DeckParam::kTrigLength:
return state.ampSpline || state.pitchSpline || state.filterSpline;
case DeckParam::kPitchEnvAttack:
case DeckParam::kPitchEnvHold:
case DeckParam::kPitchEnvDecay:
+2 -3
View File
@@ -65,9 +65,8 @@ struct DeckGroupDesc {
DeckRadioDesc captionRadio; // the caption row's far corner; id -1 = none
DeckToggleDesc captionToggle; // caption row, left of the radio; id -1 = none
// A second caption toggle, placed immediately left of the first (or in its place when the
// first is absent). Exists because a group whose knob row is wider than its caption row has
// caption slack a toggle can occupy for free — a rowToggle would widen the GROUP, and the
// deck has six pixels of headroom on its first row at the editor's floor width.
// first is absent) — why this exists rather than a rowToggle is recorded once, at this
// module's CLAUDE.md bullet.
DeckToggleDesc captionToggle2;
std::vector<int> cellIds; // knob cells; -1 = blank reserve
DeckToggleDesc rowToggle; // in the knob row after the cells; id -1 = none