deck: filter mod moves to FILTER ENV, cell runs centre in their reserves, two-segment toggles become single buttons, deck focuses its overlay

This commit is contained in:
2026-08-03 13:12:28 -04:00
parent 0eb2c67875
commit 450559f155
22 changed files with 885 additions and 504 deletions
+29 -22
View File
@@ -29,10 +29,18 @@ bool ReaSamplerEditor::mouseDownDeck(const FaceLayout& fl, int x, int y) {
const DeckLayout dl = layoutDeck(fl.deckDescs, band.x, band.y, band.width);
const DeckHit hit = hitTestDeck(dl, x, y);
if (hit.kind == DeckHitKind::CaptionRadio) {
overlayEnv_ = nextOverlaySelection(overlayEnv_, hit.id);
invalidate(); // view state only: no parameter write, no reload
return true;
// The deck IS the overlay's click target: anywhere inside an envelope group — panel
// background, knob or button — focuses that envelope, and any other group clears it. Set
// BEFORE the kind switch and it consumes nothing, so every grab and commit below still
// runs. A drag in flight owns the surface, so it refuses the change.
// Repaints HERE rather than leaning on the branches below: a click on a group's bare
// padding, or on an inert knob, changes the focus and reaches no other invalidate.
if (drag_ == DragKind::kNone) {
const OverlayEnv focus = overlayEnvForGroup(hit.group);
if (focus != overlayEnv_) {
overlayEnv_ = focus;
invalidate(); // view state only: no parameter write, no reload
}
}
if (hit.kind == DeckHitKind::CaptionToggle || hit.kind == DeckHitKind::RowToggle) {
switch (static_cast<ParamControl>(hit.id)) {
@@ -65,29 +73,29 @@ bool ReaSamplerEditor::mouseDownDeck(const FaceLayout& fl, int x, int y) {
commitAndReload();
break;
case ParamControl::kLimiterEnable: {
const bool on = (hit.segment == 1);
if (on != params_.limiterEnabled) {
params_.limiterEnabled = on;
// Commits the audible state and the persisted state together, here, because
// this is a control the user A/Bs. The funnel only ARMS the host's latency
// restart — the sync tick delivers it — so nothing on this path calls into
// the host from inside a mouse handler.
processor_->setLimiterEnabled(on);
}
// One button, so its next state is the opposite of the current one — which is
// also why the old "did it actually change" guard is gone: it always does.
const bool on = !params_.limiterEnabled;
params_.limiterEnabled = on;
// Commits the audible state and the persisted state together, here, because
// this is a control the user A/Bs. The funnel only ARMS the host's latency
// restart — the sync tick delivers it — so nothing on this path calls into
// the host from inside a mouse handler.
processor_->setLimiterEnabled(on);
invalidate();
break;
}
default: {
// Parameter-set toggles (play mode / pitch engine / pitch-env + filter enable,
// and the three env-mode toggles).
applyParamControl(hit.id, 0.0, hit.segment);
// and the three env-mode selectors). A single button carries no segment, so
// its next state is derived from the parameter set rather than read off the
// click — deck_values owns that derivation.
const int segment =
hit.segment >= 0
? hit.segment
: nextToggleSegment(static_cast<ParamControl>(hit.id), params_.play);
applyParamControl(hit.id, 0.0, segment);
commitAndReload();
// Flipping an EG's Staged|Spline toggle makes THAT envelope's overlay active,
// so the contour (or the staged shape you just returned to) is what's drawn.
// overlayEnvForModeToggle answers kNone for every other toggle this default
// case handles, which is why the assignment is conditional.
const OverlayEnv modeEnv = overlayEnvForModeToggle(hit.id);
if (modeEnv != OverlayEnv::kNone) overlayEnv_ = modeEnv;
break;
}
}
@@ -215,7 +223,6 @@ HoverTarget ReaSamplerEditor::hoverDeck(const FaceLayout& fl, int x,
// The meter reports its own state continuously; a hover on it would only mean "the clip
// cap is clearable", which the cap's presence already says.
if (dh.kind == DeckHitKind::Column) return {};
if (dh.kind == DeckHitKind::CaptionRadio) return {HoverKind::kEnvRadio, dh.id};
if (dh.kind == DeckHitKind::Knob && dh.inner &&
curveParamFor(static_cast<ParamControl>(dh.id)) != ParamControl::kCount) {
// Indexed by the OUTER cell id so the paint side can find the cell it belongs to.