fix: close review findings on the velocity-curve deck and bipolar curves

Cancels the curve-node drag whenever the popup closes so Esc mid-drag can't alias the amp curve; generalizes CurveTarget routing to one switch; fixes stale/overstated comments; clamps a pre-v12 depth fold; adds deck-inertness and filter-fold test coverage.
This commit is contained in:
2026-07-31 19:46:37 -04:00
parent 9d38f87a2d
commit cfb53aade3
22 changed files with 223 additions and 99 deletions
+30
View File
@@ -238,6 +238,36 @@ bool overlayEnvInert(OverlayEnv env, bool pitchEnvEnabled, bool filterEnabled) {
return false; // unreachable for a valid enumerator; silences a warning.
}
bool deckKnobInert(DeckParam id, bool pitchEnvEnabled, bool filterEnabled) {
switch (id) {
case DeckParam::kPitchEnvAttack:
case DeckParam::kPitchEnvHold:
case DeckParam::kPitchEnvDecay:
case DeckParam::kPitchEnvDepth:
return !pitchEnvEnabled;
case DeckParam::kFilterMorph:
case DeckParam::kFilterCutoff:
case DeckParam::kFilterQ:
case DeckParam::kFilterDrive:
case DeckParam::kFilterModAmt:
case DeckParam::kFilterKeyTrack:
// The filter's velocity curve sits in the VELOCITY group but is a filter parameter:
// it goes inert with every other one, so no surface can reach a param the knobs can't.
case DeckParam::kFilterVelCurve:
case DeckParam::kFilterEnvAttack:
case DeckParam::kFilterEnvHold:
case DeckParam::kFilterEnvDecay:
case DeckParam::kFilterEnvSustain:
case DeckParam::kFilterEnvRelease:
case DeckParam::kFilterTrigAttack:
case DeckParam::kFilterTrigHold:
case DeckParam::kFilterTrigDecay:
return !filterEnabled;
default:
return false;
}
}
bool liveCommitFor(LiveDragKind kind, int paramId) {
switch (kind) {
case LiveDragKind::kDeckKnob:
+11 -2
View File
@@ -96,8 +96,10 @@ enum DeckGroupId {
};
// Which velocity curve a deck cell edits, or kNone when the control is an ordinary knob. THE
// one place the three curve cells are named, so paint (draw a curve thumbnail, not a dial),
// hit-test (open a popup, not start a drag) and the popup's own title all read from it.
// one place a control id resolves to a curve target — paint (draw a curve thumbnail, not a
// dial) and hit-test (open a popup, not start a drag) both read this predicate rather than
// re-deriving which ids are curve cells. What each resolved target then shows (which stored
// curve, which title) is a separate switch — see the shell's curveFor/curveTitle.
enum class CurveTarget { kNone, kAmp, kPitch, kFilter };
CurveTarget curveTargetFor(int controlId);
@@ -163,6 +165,13 @@ OverlayEnv nextOverlaySelection(OverlayEnv current, int radioId);
// a param a knob couldn't (envelope_edit.h). Amp has no enable toggle and is never inert.
bool overlayEnvInert(OverlayEnv env, bool pitchEnvEnabled, bool filterEnabled);
// Whether a deck knob cell is drawn-but-dead: the pitch envelope's four knobs while it is
// disabled, and the filter group's tone/modulation knobs (plus its VELOCITY cell, a filter
// parameter that just sits in that group) while the filter is disabled. Every other id is
// always live. Mirrors overlayEnvInert's group-toggle-gates-its-knobs shape for the deck's own
// mouse-down/paint (the shell's deckKnobDisabled is a thin int-id wrapper over this).
bool deckKnobInert(DeckParam id, bool pitchEnvEnabled, bool filterEnabled);
// The deck's BIPOLAR knob law: 0.5 of the knob's travel is zero depth, the ends are -1 and
// +1. Exact inverses, and exact at the centre detent (0.5 -> 0 -> 0.5), so a knob parked at
// centre can never persist a hair of modulation. Out-of-range norm clamps to the endpoints.
+1 -1
View File
@@ -20,7 +20,7 @@ inline constexpr int kEditorMinWidth = 840;
inline constexpr int kEditorMinHeight = 620;
// Chrome band: the toolbar row (title + nav) stacked over the control row (piano strip,
// preview, velocity knob, curve button, channel toggle). sample_chrome partitions it.
// preview, velocity knob, channel toggle). sample_chrome partitions it.
inline constexpr int kTitleHeight = 26;
inline constexpr int kChromeRowHeight = 52;