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:
+159
-90
@@ -4,11 +4,13 @@
|
||||
// * group width — caption row vs knob row max + padding; row-toggle and caption-toggle widths.
|
||||
// * layout — caption toggle right-anchored IN the caption row; cells abutting left-to-right
|
||||
// inside the box; knob square centered; label band beneath; row toggle after the cells.
|
||||
// * reserves — a -1 id holds the group's width and hands its pixels to the cells present.
|
||||
// * reserves — a -1 id holds the group's width and pays for it in the two end margins, with
|
||||
// the run of present cells centred at their natural width.
|
||||
// * rows — membership comes from the group's own DeckRow, never from a wrap outcome;
|
||||
// space-between justification inside the row block; the right-anchored spanning deck.
|
||||
// * hit-test — knob cell hit (whole cell), toggle segment 0/1 boundaries, fence padding
|
||||
// misses, outside-deck misses.
|
||||
// * hit-test — knob cell hit (whole cell), toggle segment 0/1 boundaries, the single-button
|
||||
// styles' no-segment answer, the group id every hit carries, fence padding misses,
|
||||
// outside-deck misses.
|
||||
// * knob-FACE hit-test — the reset resolve against the drawn circles: inner disc, outer ring,
|
||||
// both exclusive boundaries, and the points where it deliberately disagrees with the cell.
|
||||
|
||||
@@ -30,25 +32,30 @@ static int g_fail = 0;
|
||||
// toggle + row toggle), MASTER (1 cell, no toggle).
|
||||
static std::vector<DeckGroupDesc> shellLikeDeck() {
|
||||
std::vector<DeckGroupDesc> g;
|
||||
g.push_back({0, 78, {}, {100, 44}, {}, {1, 2, 3, 4, 5}, {}});
|
||||
g.push_back({1, 38, {}, {101, 48}, {}, {6}, {}});
|
||||
g.push_back({2, 58, {}, {102, 32}, {}, {7, 8, 9}, {}});
|
||||
g.push_back({3, 38, {}, {103, 40}, {}, {10}, {104, 44}});
|
||||
g.push_back({0, 78, {}, {100, 88}, {}, {1, 2, 3, 4, 5}, {}});
|
||||
g.push_back({1, 38, {}, {101, 96}, {}, {6}, {}});
|
||||
g.push_back({2, 58, {}, {102, 64}, {}, {7, 8, 9}, {}});
|
||||
g.push_back({3, 38, {}, {103, 80}, {}, {10}, {104, 88}});
|
||||
g.push_back({4, 46, {}, {}, {}, {11}, {}});
|
||||
return g;
|
||||
}
|
||||
|
||||
static void testGroupWidth() {
|
||||
// Knob row dominates: 5 cells (240) > caption row (78 + 4 + 88 = 170) -> 240 + 2*6.
|
||||
DeckGroupDesc amp{0, 78, {}, {100, 44}, {}, {1, 2, 3, 4, 5}, {}};
|
||||
// Knob row dominates: 5 cells (300) > caption row (78 + 4 + 88 = 170) -> 300 + 2*6.
|
||||
DeckGroupDesc amp{0, 78, {}, {100, 88}, {}, {1, 2, 3, 4, 5}, {}};
|
||||
CHECK(deckGroupWidth(amp) == 5 * kDeckCellW + 2 * kDeckGroupPadX);
|
||||
// Caption row dominates: 38 + 4 + 96 = 138 > 48 -> 138 + 12.
|
||||
DeckGroupDesc pitch{1, 38, {}, {101, 48}, {}, {6}, {}};
|
||||
CHECK(deckGroupWidth(pitch) == 38 + kDeckToggleGap + 2 * 48 + 2 * kDeckGroupPadX);
|
||||
// Row toggle counts into the knob row: 48 + 4 + 88 = 140 > caption 38+4+80=122.
|
||||
DeckGroupDesc voice{3, 38, {}, {103, 40}, {}, {10}, {104, 44}};
|
||||
// Caption row dominates: 38 + 4 + 96 = 138 > 60 -> 138 + 12.
|
||||
DeckGroupDesc pitch{1, 38, {}, {101, 96}, {}, {6}, {}};
|
||||
CHECK(deckGroupWidth(pitch) == 38 + kDeckToggleGap + 96 + 2 * kDeckGroupPadX);
|
||||
// Row toggle counts into the knob row: 60 + 4 + 88 = 152 > caption 38+4+80=122.
|
||||
DeckGroupDesc voice{3, 38, {}, {103, 80}, {}, {10}, {104, 88}};
|
||||
CHECK(deckGroupWidth(voice) ==
|
||||
kDeckCellW + kDeckToggleGap + 2 * 44 + 2 * kDeckGroupPadX);
|
||||
kDeckCellW + kDeckToggleGap + 88 + 2 * kDeckGroupPadX);
|
||||
// A toggle's `width` is the WHOLE control either way, so a single button and a segmented
|
||||
// one of the same declared width cost the group exactly the same.
|
||||
DeckGroupDesc single = voice;
|
||||
single.captionToggle.style = DeckToggleStyle::kEnable;
|
||||
CHECK(deckGroupWidth(single) == deckGroupWidth(voice));
|
||||
// No toggles: max(caption, cells) + padding.
|
||||
DeckGroupDesc master{4, 46, {}, {}, {}, {11}, {}};
|
||||
CHECK(deckGroupWidth(master) == kDeckCellW + 2 * kDeckGroupPadX);
|
||||
@@ -64,11 +71,11 @@ static void testGroupWidth() {
|
||||
// widths: two Sound groups, two Contour groups, one Spanning group carrying a column.
|
||||
static std::vector<DeckGroupDesc> tworowDeck() {
|
||||
std::vector<DeckGroupDesc> g;
|
||||
g.push_back({0, 78, {}, {100, 44}, {}, {1, 2, 3, 4, 5}, {}, DeckRow::Sound, {}});
|
||||
g.push_back({1, 38, {}, {101, 48}, {}, {6, 7}, {}, DeckRow::Sound, {}});
|
||||
g.push_back({2, 58, {}, {102, 32}, {}, {8, 9, 10}, {}, DeckRow::Contour, {}});
|
||||
g.push_back({3, 38, {}, {103, 40}, {}, {11}, {}, DeckRow::Contour, {}});
|
||||
g.push_back({4, 46, {200, true}, {104, 32}, {}, {12, -1}, {},
|
||||
g.push_back({0, 78, {}, {100, 88}, {}, {1, 2, 3, 4, 5}, {}, DeckRow::Sound, {}});
|
||||
g.push_back({1, 38, {}, {101, 96}, {}, {6, 7}, {}, DeckRow::Sound, {}});
|
||||
g.push_back({2, 58, {}, {102, 64}, {}, {8, 9, 10}, {}, DeckRow::Contour, {}});
|
||||
g.push_back({3, 38, {}, {103, 80}, {}, {11}, {}, DeckRow::Contour, {}});
|
||||
g.push_back({4, 46, {200, true}, {104, 64}, {}, {12, -1}, {},
|
||||
DeckRow::Spanning, {300, 62}});
|
||||
return g;
|
||||
}
|
||||
@@ -291,41 +298,101 @@ static void testHitTest() {
|
||||
h = hitTestDeck(dl, voice.rowToggle.seg1.x + 1, voice.rowToggle.seg1.y + 1);
|
||||
CHECK(h.kind == DeckHitKind::RowToggle && h.id == 104 && h.segment == 1);
|
||||
|
||||
// A reserve (id -1) yields no cell of its own. This fixture's reserve divides its present
|
||||
// cells evenly (5 slots / 3 present -> 240/3, no residue), so every point of the knob row
|
||||
// lands on a real control: no dead rect survives for a grab to fall into. That does NOT
|
||||
// generalize to an indivisible reserve — a residue leaves a few uncovered margin pixels by
|
||||
// design (testIndivisibleResidueSplitsSymmetricallyAcrossBothEnds, below).
|
||||
// A reserve (id -1) yields no cell of its own, and the cells present cover their CENTRED
|
||||
// run contiguously — no dead rect between them for a grab to fall into. What the reserve
|
||||
// buys is margin at the two ends, which is a deliberate miss and pinned as one below.
|
||||
std::vector<DeckGroupDesc> trig;
|
||||
trig.push_back({0, 78, {}, {100, 44}, {}, {20, 21, 22, -1, -1}, {}});
|
||||
trig.push_back({0, 78, {}, {100, 88}, {}, {20, 21, 22, -1, -1}, {}});
|
||||
const DeckLayout tl = layoutDeck(trig, 0, 0, 824);
|
||||
const DeckGroupLayout& tg = tl.groups[0];
|
||||
CHECK(tg.cells.size() == 3);
|
||||
for (const DeckCellLayout& c : tg.cells) CHECK(c.id >= 0);
|
||||
// Bound the sweep against the RESERVED run (5 slots, not the 3 present cells) rather than
|
||||
// the cells' own extent — the cells are what's under test, so deriving the bound from them
|
||||
// could never catch a layout that under-covers the run they were reserved out of.
|
||||
const int runStart = tg.box.x + kDeckGroupPadX;
|
||||
const int runEnd = runStart + static_cast<int>(trig[0].cellIds.size()) * kDeckCellW;
|
||||
const int rowY = tg.cells.back().cell.y + 5;
|
||||
for (int px = runStart; px < runEnd; ++px) {
|
||||
for (int px = tg.cells.front().cell.x; px < tg.cells.back().cell.right(); ++px) {
|
||||
const DeckHit rowHit = hitTestDeck(tl, px, rowY);
|
||||
CHECK(rowHit.kind == DeckHitKind::Knob && rowHit.id >= 0);
|
||||
}
|
||||
// The reserve's own pixels answer no control — but they still name the group, which is
|
||||
// what makes the deck panel's background a target for the overlay focus.
|
||||
const DeckHit margin = hitTestDeck(tl, tg.box.x + kDeckGroupPadX + 1, rowY);
|
||||
CHECK(margin.kind == DeckHitKind::None && margin.group == 0);
|
||||
|
||||
// The fence padding inside the box misses; outside the deck misses.
|
||||
// The fence padding inside the box misses as a control and names its group; outside the
|
||||
// deck misses entirely, group included.
|
||||
h = hitTestDeck(dl, amp.box.x + 1, amp.box.bottom() - 1);
|
||||
CHECK(h.kind == DeckHitKind::None);
|
||||
CHECK(h.kind == DeckHitKind::None && h.id == -1 && h.group == 0);
|
||||
h = hitTestDeck(dl, -50, -50);
|
||||
CHECK(h.kind == DeckHitKind::None);
|
||||
CHECK(h.kind == DeckHitKind::None && h.group == -1);
|
||||
|
||||
// Every hit kind carries the group it landed in, so the shell never has to re-scan the
|
||||
// layout to find out which deck a click belongs to.
|
||||
CHECK(hitTestDeck(dl, c0.cell.x + 1, c0.cell.y + 1).group == 0);
|
||||
CHECK(hitTestDeck(dl, amp.captionToggle.seg1.x, amp.captionToggle.seg1.y + 1).group == 0);
|
||||
CHECK(hitTestDeck(dl, voice.rowToggle.seg1.x + 1, voice.rowToggle.seg1.y + 1).group == 3);
|
||||
}
|
||||
|
||||
// A reserve holds the group's WIDTH and hands its pixels to the cells that are present. The
|
||||
// three properties together are what stops a narrower face reading as a hole: the group is
|
||||
// exactly as wide as the full-face one, the cells are uniform and abutting, and what they do
|
||||
// not cover is smaller than one pixel per cell.
|
||||
static void testReservedCellWidthGoesToTheCellsPresent() {
|
||||
const DeckGroupDesc full{0, 78, {}, {100, 44}, {}, {20, 21, 22, 23, 24}, {}};
|
||||
// A single-button toggle takes the WHOLE declared width in seg0, leaves seg1 empty, and — the
|
||||
// property the commit seam rests on — answers with NO segment, so a caller cannot mistake it
|
||||
// for the left half of a two-segment control.
|
||||
static void testSingleButtonToggleTakesTheWholeSlotAndCarriesNoSegment() {
|
||||
for (DeckToggleStyle style : {DeckToggleStyle::kEnable, DeckToggleStyle::kMode}) {
|
||||
DeckGroupDesc g{0, 40, {}, {200, 52, style}, {}, {1, 2, 3}, {}};
|
||||
std::vector<DeckGroupDesc> gs{g};
|
||||
const DeckLayout dl = layoutDeck(gs, 0, 0, 400);
|
||||
const DeckToggleLayout& t = dl.groups[0].captionToggle;
|
||||
CHECK(t.id == 200);
|
||||
CHECK(t.style == style);
|
||||
CHECK(t.seg0.width == 52);
|
||||
CHECK(t.seg1.empty());
|
||||
// Right-anchored in the caption row exactly as a segmented toggle is.
|
||||
CHECK(t.seg0.right() == dl.groups[0].box.right() - kDeckGroupPadX);
|
||||
CHECK(t.seg0.height == kDeckToggleH);
|
||||
|
||||
// Both ends of the button answer the same hit, with segment -1.
|
||||
for (int px : {t.seg0.x, t.seg0.x + 26, t.seg0.right() - 1}) {
|
||||
const DeckHit h = hitTestDeck(dl, px, t.seg0.y + 1);
|
||||
CHECK(h.kind == DeckHitKind::CaptionToggle);
|
||||
CHECK(h.id == 200 && h.segment == -1 && h.group == 0);
|
||||
}
|
||||
// Where the right half of a segmented toggle would have been is now the same button,
|
||||
// not segment 1 — the regression this style exists to make impossible.
|
||||
CHECK(hitTestDeck(dl, t.seg0.right() - 1, t.seg0.y + 1).segment != 1);
|
||||
}
|
||||
}
|
||||
|
||||
// The caption toggles sit in the caption row and the knob circles in the cell row, so no
|
||||
// button rect can overlap a dial. hitTestKnobFace runs NO toggle-precedence pass, and this is
|
||||
// the property that lets it get away with that — re-checked here because the single-button
|
||||
// styles made every one of those rects wider.
|
||||
static void testNoToggleRectOverlapsAKnobCircle() {
|
||||
DeckGroupDesc g{0, 40, {}, {200, 96, DeckToggleStyle::kEnable},
|
||||
{201, 96, DeckToggleStyle::kMode}, {1, 2, 3}, {202, 96}};
|
||||
std::vector<DeckGroupDesc> gs{g};
|
||||
const DeckLayout dl = layoutDeck(gs, 0, 0, 600);
|
||||
const DeckGroupLayout& lay = dl.groups[0];
|
||||
const DeckToggleLayout* toggles[] = {&lay.captionToggle, &lay.captionToggle2,
|
||||
&lay.rowToggle};
|
||||
for (const DeckToggleLayout* t : toggles) {
|
||||
for (const Rect& seg : {t->seg0, t->seg1}) {
|
||||
if (seg.empty()) continue;
|
||||
for (const DeckCellLayout& c : lay.cells) {
|
||||
// Sweep the segment's own pixels: none of them may land on a drawn dial.
|
||||
for (int px = seg.x; px < seg.right(); ++px) {
|
||||
for (int py = seg.y; py < seg.bottom(); ++py) {
|
||||
CHECK(!inKnobFace(c.knob, px, py));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A reserve holds the group's WIDTH and gives its pixels to the two END MARGINS, never to the
|
||||
// cells: every cell keeps kDeckCellW whatever face the group is showing, and the run of them is
|
||||
// centred. That is the whole spacing law — a reduced face is the same knobs at the same pitch,
|
||||
// sitting in the middle of a box that did not move.
|
||||
static void testAReserveCentresTheRunAndNeverWidensACell() {
|
||||
const DeckGroupDesc full{0, 78, {}, {100, 88}, {}, {20, 21, 22, 23, 24}, {}};
|
||||
// Three, four, and a lone cell against the same five-slot reserve.
|
||||
const std::vector<std::vector<int>> faces = {
|
||||
{20, 21, 22, -1, -1}, {20, 21, 22, 23, -1}, {20, -1, -1, -1, -1}};
|
||||
@@ -340,30 +407,42 @@ static void testReservedCellWidthGoesToTheCellsPresent() {
|
||||
const int present = static_cast<int>(lay.cells.size());
|
||||
CHECK(present == 5 - static_cast<int>(std::count(ids.begin(), ids.end(), -1)));
|
||||
|
||||
const int run = 5 * kDeckCellW;
|
||||
for (int i = 0; i < present; ++i) {
|
||||
const DeckCellLayout& c = lay.cells[static_cast<std::size_t>(i)];
|
||||
CHECK(c.cell.width == lay.cells[0].cell.width); // uniform
|
||||
CHECK(c.knob.width == kDeckKnobSize); // the dial itself is fixed
|
||||
// Centred as exactly as integers allow: a cell whose spare width is odd cannot
|
||||
// split it evenly, and the layout's integer division gives the odd pixel to the
|
||||
// RIGHT margin. Pinned as a directional identity rather than a tolerance, so a
|
||||
// future off-by-one on the other side would still fail here.
|
||||
const int leftGap = c.knob.x - c.cell.x;
|
||||
const int rightGap = c.cell.right() - c.knob.right();
|
||||
CHECK(rightGap - leftGap == (c.cell.width - kDeckKnobSize) % 2);
|
||||
CHECK(c.cell.width == kDeckCellW); // natural pitch, never the divided run
|
||||
CHECK(c.knob.width == kDeckKnobSize);
|
||||
// The dial sits centred in its cell — 60 and 40 are both even, so exactly so.
|
||||
CHECK(c.knob.x - c.cell.x == c.cell.right() - c.knob.right());
|
||||
if (i > 0) CHECK(c.cell.x == lay.cells[static_cast<std::size_t>(i - 1)].cell.right());
|
||||
}
|
||||
// Uncovered run is the indivisible residue only, split evenly at the two ends.
|
||||
const int covered = lay.cells.back().cell.right() - lay.cells[0].cell.x;
|
||||
CHECK(run - covered < present);
|
||||
const int leadPad = lay.cells[0].cell.x - (lay.box.x + kDeckGroupPadX);
|
||||
CHECK(leadPad == (run - covered) / 2);
|
||||
// What the run does not cover is the reserve, split evenly at the two ends. The
|
||||
// reserve is a whole number of 60px cells, so the split is exact — never off by one.
|
||||
const int leadPad = lay.cells.front().cell.x - (lay.box.x + kDeckGroupPadX);
|
||||
const int trailPad = (lay.box.right() - kDeckGroupPadX) - lay.cells.back().cell.right();
|
||||
CHECK(leadPad == trailPad);
|
||||
CHECK(leadPad + trailPad == (5 - present) * kDeckCellW);
|
||||
}
|
||||
|
||||
// Only the reserve COUNT matters, not where a -1 sits: with the run centred, three faces
|
||||
// that reserve two slots in three different places lay out identically.
|
||||
const std::vector<std::vector<int>> sameCount = {
|
||||
{20, 21, 22, -1, -1}, {-1, 20, 21, -1, 22}, {-1, -1, 20, 21, 22}};
|
||||
std::vector<Rect> firstRun;
|
||||
for (const std::vector<int>& ids : sameCount) {
|
||||
DeckGroupDesc d = full;
|
||||
d.cellIds = ids;
|
||||
std::vector<DeckGroupDesc> g{d};
|
||||
const DeckLayout dl = layoutDeck(g, 0, 0, 824);
|
||||
std::vector<Rect> cells;
|
||||
for (const DeckCellLayout& c : dl.groups[0].cells) cells.push_back(c.cell);
|
||||
CHECK(cells.size() == 3);
|
||||
if (firstRun.empty()) firstRun = cells;
|
||||
else CHECK(cells == firstRun);
|
||||
}
|
||||
|
||||
// A reserve does not move the row toggle: it anchors past the whole run, so the FILTER
|
||||
// group's law switch cannot drift when a neighbouring face changes shape.
|
||||
DeckGroupDesc withToggle{1, 40, {}, {}, {}, {20, 21, 22, 23, 24}, {104, 44}};
|
||||
DeckGroupDesc withToggle{1, 40, {}, {}, {}, {20, 21, 22, 23, 24}, {104, 88}};
|
||||
std::vector<DeckGroupDesc> a{withToggle};
|
||||
withToggle.cellIds = {20, 21, -1, -1, -1};
|
||||
std::vector<DeckGroupDesc> b{withToggle};
|
||||
@@ -371,35 +450,23 @@ static void testReservedCellWidthGoesToTheCellsPresent() {
|
||||
layoutDeck(b, 0, 0, 824).groups[0].rowToggle.seg0);
|
||||
}
|
||||
|
||||
// The three faces above all divide their run evenly, so none of them actually exercises
|
||||
// "residue in symmetric end margins". An 8-slot reserve with 7 present (480/7 = 68 r4) does:
|
||||
// residue 4 is the smallest case that can tell a symmetric split (2/2) apart from a
|
||||
// trailing-only one (0/4) — a residue of 1 can't, since leadPad = residue/2 rounds to 0 either
|
||||
// way, which is exactly why this seam's earlier test passed without pinning the rule it was
|
||||
// named for.
|
||||
static void testIndivisibleResidueSplitsSymmetricallyAcrossBothEnds() {
|
||||
const DeckGroupDesc g{0, 78, {}, {100, 44}, {}, {20, 21, 22, 23, 24, 25, 26, -1}, {}};
|
||||
std::vector<DeckGroupDesc> gs{g};
|
||||
const DeckLayout dl = layoutDeck(gs, 0, 0, 824);
|
||||
const DeckGroupLayout& lay = dl.groups[0];
|
||||
CHECK(lay.cells.size() == 7);
|
||||
|
||||
const int run = 8 * kDeckCellW;
|
||||
const int present = 7;
|
||||
const int cellW = run / present; // 76: the same integer division the layout uses
|
||||
const int expectedResidue = run - cellW * present; // 4
|
||||
CHECK(expectedResidue == 4);
|
||||
|
||||
const int covered = lay.cells.back().cell.right() - lay.cells.front().cell.x;
|
||||
CHECK(run - covered == expectedResidue);
|
||||
const int leadPad = lay.cells.front().cell.x - (lay.box.x + kDeckGroupPadX);
|
||||
const int trailPad = (lay.box.right() - kDeckGroupPadX) - lay.cells.back().cell.right();
|
||||
// Hard literals, not just the formula: this is the case that actually distinguishes
|
||||
// symmetric (2/2) from trailing-only (0/4) — see the comment above.
|
||||
CHECK(leadPad == 2);
|
||||
CHECK(trailPad == 2);
|
||||
CHECK(leadPad == expectedResidue / 2);
|
||||
CHECK(trailPad == expectedResidue - leadPad); // both ends share it, not one absorbing it
|
||||
// A face with NO reserve is untouched by the centring — the offset is zero by construction, so
|
||||
// the run starts flush against the group's inner padding exactly as it always did. This is what
|
||||
// makes "the Gate deck face is pixel-identical" a structural claim rather than an observation.
|
||||
static void testAFaceWithNoReserveStartsFlushAgainstThePadding() {
|
||||
for (int slots = 1; slots <= 8; ++slots) {
|
||||
DeckGroupDesc g{0, 78, {}, {100, 88}, {}, {}, {}};
|
||||
for (int i = 0; i < slots; ++i) g.cellIds.push_back(20 + i);
|
||||
std::vector<DeckGroupDesc> gs{g};
|
||||
const DeckLayout dl = layoutDeck(gs, 0, 0, 824);
|
||||
const DeckGroupLayout& lay = dl.groups[0];
|
||||
CHECK(static_cast<int>(lay.cells.size()) == slots);
|
||||
CHECK(lay.cells.front().cell.x == lay.box.x + kDeckGroupPadX);
|
||||
// The run covers the whole reserve exactly — no lead margin to absorb, none to leave.
|
||||
// (Not "flush right": a caption-row-bound group's box is wider than its knob row.)
|
||||
CHECK(lay.cells.back().cell.right() - lay.cells.front().cell.x == slots * kDeckCellW);
|
||||
for (const DeckCellLayout& c : lay.cells) CHECK(c.cell.width == kDeckCellW);
|
||||
}
|
||||
}
|
||||
|
||||
// The corner radio widens the caption row, takes the far corner, and pushes the caption
|
||||
@@ -449,7 +516,7 @@ static void testInnerDialHit() {
|
||||
// the caption text stops before the LEFTMOST one), and takes captionToggle's own slot when
|
||||
// captionToggle is absent — the shipped FILTER ENV group's exact shape (deck_groups.cpp).
|
||||
static void testCaptionToggle2() {
|
||||
const DeckGroupDesc both{9, 40, {}, {300, 30}, {301, 20}, {1, 2, 3}, {}};
|
||||
const DeckGroupDesc both{9, 40, {}, {300, 60}, {301, 40}, {1, 2, 3}, {}};
|
||||
std::vector<DeckGroupDesc> g{both};
|
||||
const DeckLayout dl = layoutDeck(g, 0, 0, 800);
|
||||
const DeckGroupLayout& lay = dl.groups[0];
|
||||
@@ -469,7 +536,7 @@ static void testCaptionToggle2() {
|
||||
|
||||
// FILTER ENV's real shape: captionToggle absent, captionToggle2 present with a radio — it
|
||||
// takes the first (rightmost) slot rather than leaving a gap where captionToggle would sit.
|
||||
const DeckGroupDesc filterEnvLike{10, 66, {200}, {}, {302, 23}, {1, 2, 3, 4, 5}, {}};
|
||||
const DeckGroupDesc filterEnvLike{10, 66, {200}, {}, {302, 46}, {1, 2, 3, 4, 5}, {}};
|
||||
std::vector<DeckGroupDesc> g2{filterEnvLike};
|
||||
const DeckLayout dl2 = layoutDeck(g2, 0, 0, 800);
|
||||
const DeckGroupLayout& fe = dl2.groups[0];
|
||||
@@ -558,8 +625,10 @@ int main() {
|
||||
testSpanningOnlyDeckKeepsItsHeight();
|
||||
testGroupInnerGeometry();
|
||||
testHitTest();
|
||||
testReservedCellWidthGoesToTheCellsPresent();
|
||||
testIndivisibleResidueSplitsSymmetricallyAcrossBothEnds();
|
||||
testSingleButtonToggleTakesTheWholeSlotAndCarriesNoSegment();
|
||||
testNoToggleRectOverlapsAKnobCircle();
|
||||
testAReserveCentresTheRunAndNeverWidensACell();
|
||||
testAFaceWithNoReserveStartsFlushAgainstThePadding();
|
||||
testCaptionRadioGeometryAndHit();
|
||||
testInnerDialHit();
|
||||
testKnobFaceResolvesInnerRingOuterRingAndMisses();
|
||||
|
||||
Reference in New Issue
Block a user