fix(instrument-ui): distinguish Disabled from Off, add hover cue to mode toggles, close review minors

Adds a hairline outline for Disabled chrome buttons, resolves Hover on the four
mode-selector single-buttons, re-measures the control-surface doc's 1.2 layout
table post-reflow, and adds a structural test pinning every enable/mode toggle
to a derived segment.
This commit is contained in:
2026-08-03 13:43:38 -04:00
parent 450559f155
commit 73c69f689c
8 changed files with 108 additions and 33 deletions
+3 -2
View File
@@ -422,8 +422,8 @@ static void testEnvModeCeilingsArePinnedForPitchEnvAndAmp() {
// Every group's width, in BOTH play modes, against the measured layout table
// (instrument-control-surface.md §1.2). Mode-independence is the second half of the claim: the
// reserve slots hold the two mode-dependent groups at 312 either way, which is what makes the
// contour row's 876 a constant rather than a Gate-only fact.
// reserve slots hold the two mode-dependent groups at 372 (FILTER ENV) and 312 (AMP ENVELOPE)
// either way, which is what makes the contour row's 936 a constant rather than a Gate-only fact.
static void testEveryGroupWidthMatchesTheMeasuredLayout() {
const struct { int id; int width; } want[] = {
{kGroupPitch, 192}, {kGroupPitchEnv, 252}, {kGroupFilter, 372},
@@ -477,6 +477,7 @@ static void testTheGateFaceIsPixelIdenticalApartFromTheTwoFilterGroups() {
{kGroupPitchEnv, 0, 252, 4}, // unchanged
{kGroupFilterEnv, 298, 372, 6}, // was x=328 w=312 with 5 cells
{kGroupAmpEnv, 716, 312, 5}, // unchanged
{kGroupMaster, 1040, 142, 1}, // unchanged — the claim above actually pins it
};
for (const auto& w : want) {
const DeckGroupLayout& l = lay(w.id);
+26
View File
@@ -6,6 +6,7 @@
#include "../src/core/instrument/ui/deck_values.h"
#include "../src/core/instrument/engine/master_gain.h"
#include "../src/core/instrument/ui/deck_groups.h"
#include <cmath>
#include <cstdio>
@@ -450,9 +451,34 @@ static void testASingleButtonsDerivedSegmentFlipsTheFieldItNames() {
}
}
// The cross-check the hand-maintained list above cannot catch: a control RE-STYLED to a
// single button (kEnable/kMode) with no nextToggleSegment entry silently commits segment -1,
// which setDeckParam reads as "off" — a latch, not a toggle. Swept over every group
// sampleDeckGroups actually ships, in both play modes, rather than a fixed id list, so a
// future re-style is caught the moment it lands here with no entry above. kLimiterEnable is
// the one shipped kEnable that is excluded: it lives on `InstrumentParams::limiterEnabled`,
// outside `PlaySeconds`, and commits through its own handler (editor_input_deck.cpp) rather
// than through nextToggleSegment/setDeckParam at all.
static void testEveryShippedSingleButtonToggleHasADerivedSegment() {
const PlaySeconds p;
for (PlayMode mode : {PlayMode::Gate, PlayMode::Trigger}) {
for (const DeckGroupDesc& g : sampleDeckGroups(mode)) {
for (const DeckToggleDesc* t : {&g.captionToggle, &g.captionToggle2, &g.rowToggle}) {
if (t->id < 0) continue;
if (t->style != DeckToggleStyle::kEnable && t->style != DeckToggleStyle::kMode)
continue;
const DeckParam id = static_cast<DeckParam>(t->id);
if (id == DeckParam::kLimiterEnable) continue;
CHECK(nextToggleSegment(id, p) != -1);
}
}
}
}
int main() {
testTheTwoCeilingNamesAreOneNumber();
testASingleButtonsDerivedSegmentFlipsTheFieldItNames();
testEveryShippedSingleButtonToggleHasADerivedSegment();
testNormRoundTripsThroughEveryValueDomain();
testRateKnobEndsAreTheStretchersOwnBounds();
testRateAndPitchBindTheirOwnFields();