Loop: an explicit enable, four named marks with grabbable caps, and the crossfade painted where it is actually heard
hasLoop becomes user-owned with the gestures as shortcuts onto it; no format change. START uses overlay/trace, not accent/primary, which is the waveform's own fill.
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
//
|
||||
// Covers: the chrome band's two rows (toolbar over strip row, tiling the band exactly); the
|
||||
// toolbar's fixed right-anchored run in order (Hold, bake, preview, velocity cell,
|
||||
// Mono|Stereo, Browse) with the title taking the remainder; the velocity and Hold knobs
|
||||
// Loop Off|On, Mono|Stereo, Browse) with the title taking the remainder and still holding its
|
||||
// text at the editor's floor; the velocity and Hold knobs
|
||||
// centred in their cells above their labels; the piano strip owning its whole row at every
|
||||
// width; no rect on the
|
||||
// toolbar overlapping any other; degenerate bands yielding no inverted rects; and the preview
|
||||
@@ -58,7 +59,10 @@ static void testToolbarRunIsOrderedRightToLeftWithoutOverlap() {
|
||||
CHECK(r.navBrowse.width == kNavButtonWidth);
|
||||
CHECK(r.chanStereo.right() <= r.navBrowse.x);
|
||||
CHECK(r.chanMono.right() == r.chanStereo.x);
|
||||
CHECK(r.velCell.right() <= r.chanMono.x);
|
||||
CHECK(r.loopOn.right() <= r.chanMono.x); // the enable is immediately left of Mono|Stereo
|
||||
CHECK(r.loopOff.right() == r.loopOn.x); // its two segments abut, like the channel pair
|
||||
CHECK(r.loopOff.y == r.chanMono.y && r.loopOff.height == r.chanMono.height);
|
||||
CHECK(r.velCell.right() <= r.loopOff.x);
|
||||
CHECK(r.preview.right() <= r.velCell.x);
|
||||
CHECK(r.bake.right() <= r.preview.x);
|
||||
CHECK(r.bake.width == kBakeButtonWidth);
|
||||
@@ -70,8 +74,8 @@ static void testToolbarRunIsOrderedRightToLeftWithoutOverlap() {
|
||||
CHECK(r.title.width > 0);
|
||||
|
||||
// Every toolbar rect sits inside the toolbar row.
|
||||
const Rect items[] = {r.title, r.holdCell, r.bake, r.preview, r.velCell, r.chanMono,
|
||||
r.chanStereo, r.navBrowse};
|
||||
const Rect items[] = {r.title, r.holdCell, r.bake, r.preview, r.velCell, r.loopOff,
|
||||
r.loopOn, r.chanMono, r.chanStereo, r.navBrowse};
|
||||
for (const Rect& it : items) {
|
||||
CHECK(it.y >= r.toolbar.y && it.bottom() <= r.toolbar.bottom());
|
||||
}
|
||||
@@ -84,8 +88,8 @@ static void testChromePartsNeverOverlapAtAnyWidth() {
|
||||
// stay inside its own row, clear of every control.
|
||||
CHECK(!overlaps(r.toolbar, r.rootStrip));
|
||||
CHECK(r.rootStrip.y >= r.controls.y && r.rootStrip.bottom() <= r.controls.bottom());
|
||||
const Rect items[] = {r.holdCell, r.bake, r.preview, r.velCell, r.chanMono,
|
||||
r.chanStereo, r.navBrowse};
|
||||
const Rect items[] = {r.holdCell, r.bake, r.preview, r.velCell, r.loopOff, r.loopOn,
|
||||
r.chanMono, r.chanStereo, r.navBrowse};
|
||||
for (const Rect& it : items) {
|
||||
CHECK(!overlaps(it, r.rootStrip));
|
||||
CHECK(!overlaps(it, r.title));
|
||||
@@ -147,6 +151,20 @@ static void testHoldCellIsReservedAndFollowsTheVelocityCellGrammar() {
|
||||
CHECK(wide.holdCell.width == r.holdCell.width);
|
||||
}
|
||||
|
||||
// The enable joins a RIGHT-ANCHORED run, so it is charged to the title slot and not to the
|
||||
// window. kEditorMinWidth itself is guarded by test_deck_groups' derived-floor assertion — this
|
||||
// is the other half of that contract: the title must still hold its text AT that floor, because
|
||||
// the agreed remedy if it cannot is to narrow the enable's segments, never to move the floor.
|
||||
static void testTheControlRunLeavesTheTitleReadableAtTheEditorFloor() {
|
||||
const ChromeRects r = chromeRects(chromeBand(), kKnob);
|
||||
// "ReaSampler 9000" (15 chars) plus a bracketed 20-char capture name, at the toolbar font's
|
||||
// generous ~7 px/char estimate: 38 * 7.
|
||||
constexpr int kTitleTextFloorPx = 266;
|
||||
CHECK(r.title.width >= kTitleTextFloorPx);
|
||||
// Nothing in the run reaches into the title's slot.
|
||||
CHECK(r.title.right() <= r.holdCell.x);
|
||||
}
|
||||
|
||||
static void testDegenerateBandYieldsNoInvertedRects() {
|
||||
const ChromeRects empty = chromeRects(Rect{}, kKnob);
|
||||
CHECK(empty.toolbar.empty() && empty.controls.empty());
|
||||
@@ -157,8 +175,8 @@ static void testDegenerateBandYieldsNoInvertedRects() {
|
||||
kKnob);
|
||||
const Rect items[] = {tiny.title, tiny.holdCell, tiny.holdKnob, tiny.holdLabel,
|
||||
tiny.bake, tiny.preview, tiny.velCell, tiny.velKnob, tiny.velLabel,
|
||||
tiny.chanMono, tiny.chanStereo, tiny.navBrowse,
|
||||
tiny.rootStrip};
|
||||
tiny.loopOff, tiny.loopOn, tiny.chanMono, tiny.chanStereo,
|
||||
tiny.navBrowse, tiny.rootStrip};
|
||||
for (const Rect& it : items) CHECK(it.right() >= it.x && it.bottom() >= it.y);
|
||||
}
|
||||
|
||||
@@ -206,6 +224,7 @@ int main() {
|
||||
testStripOwnsItsWholeRowAndGrowsWithTheWindow();
|
||||
testVelocityKnobIsCentredInItsCellAboveTheLabel();
|
||||
testHoldCellIsReservedAndFollowsTheVelocityCellGrammar();
|
||||
testTheControlRunLeavesTheTitleReadableAtTheEditorFloor();
|
||||
testDegenerateBandYieldsNoInvertedRects();
|
||||
testPreviewGlyphSitsInsideTheButtonAndPointsRight();
|
||||
testPreviewGlyphDegradesRatherThanOverflowing();
|
||||
|
||||
Reference in New Issue
Block a user