instrument: a deck group's reserved cell width goes to the cells present
A Trigger face dropping Sustain and Release now gets wider cells instead of 144 px of dead slots. Group widths, row packing and Gate are untouched.
This commit is contained in:
@@ -88,8 +88,9 @@ std::vector<DeckGroupDesc> sampleDeckGroups(PlayMode playMode) {
|
||||
amp.captionToggle2 = {id(DeckParam::kAmpEnvMode), kEnvModeSegW};
|
||||
if (trigger) {
|
||||
// The play span first, then the AHD that shapes it, time-ordered left-to-right so
|
||||
// the row reads like the drawn envelope. One blank keeps the group's width — and
|
||||
// therefore its neighbours' placement — identical across a mode flip.
|
||||
// the row reads like the drawn envelope. One reserve (-1) keeps the group's width —
|
||||
// and therefore its neighbours' placement — identical across a mode flip; its
|
||||
// pixels go to the four cells that remain (knob_deck.h).
|
||||
amp.cellIds = {id(DeckParam::kTrigLength), id(DeckParam::kTrigAttack),
|
||||
id(DeckParam::kTrigHold), id(DeckParam::kTrigDecay), -1};
|
||||
} else {
|
||||
|
||||
@@ -111,8 +111,8 @@ CurveTarget curveTargetFor(int controlId);
|
||||
|
||||
// The deck's groups, left to right, in SIGNAL-FLOW order: pitch -> filter -> amp, then the
|
||||
// two instance-wide groups. `playMode` picks the AMP and FILTER ENV groups' faces — AHDSR in
|
||||
// Gate, AHD in Trigger — via knob_deck's blank-cell reservation (knob_deck.h) so a mode flip
|
||||
// never reflows the neighbouring groups.
|
||||
// Gate, AHD in Trigger — via knob_deck's cell-width reservation (knob_deck.h) so a mode flip
|
||||
// never reflows the neighbouring groups and never leaves a hole in the narrower face.
|
||||
std::vector<DeckGroupDesc> sampleDeckGroups(PlayMode playMode);
|
||||
|
||||
// The curve-exponent control a stage knob's INNER DIAL edits, or kCount when the knob shapes
|
||||
|
||||
@@ -65,14 +65,23 @@ DeckGroupLayout layoutGroup(const DeckGroupDesc& g, const Rect& box) {
|
||||
placeToggle(g.captionToggle, out.captionToggle);
|
||||
placeToggle(g.captionToggle2, out.captionToggle2);
|
||||
|
||||
// Knob row: fixed cells left-to-right, then the optional row toggle.
|
||||
// Knob row: the cells present divide the whole reserved run (one kDeckCellW per declared
|
||||
// id, reserves included). Integer division puts an indivisible residue in symmetric end
|
||||
// margins rather than in one odd-width cell — keyboard_strip's uniformity-wins rule.
|
||||
const int cellTop = captionTop + kDeckCaptionH + kDeckCaptionGap;
|
||||
int x = innerLeft;
|
||||
const int runWidth = static_cast<int>(g.cellIds.size()) * kDeckCellW;
|
||||
int presentCells = 0;
|
||||
for (int id : g.cellIds) {
|
||||
if (id >= 0) ++presentCells;
|
||||
}
|
||||
const int cellW = presentCells > 0 ? runWidth / presentCells : 0;
|
||||
int x = innerLeft + (runWidth - presentCells * cellW) / 2;
|
||||
for (int id : g.cellIds) {
|
||||
if (id < 0) continue;
|
||||
DeckCellLayout c;
|
||||
c.id = id;
|
||||
c.cell = Rect::ltrb(x, cellTop, x + kDeckCellW, cellTop + kDeckCellH);
|
||||
const int knobLeft = x + (kDeckCellW - kDeckKnobSize) / 2;
|
||||
c.cell = Rect::ltrb(x, cellTop, x + cellW, cellTop + kDeckCellH);
|
||||
const int knobLeft = x + (cellW - kDeckKnobSize) / 2;
|
||||
const int knobTop = cellTop + 4;
|
||||
c.knob = Rect::ltrb(knobLeft, knobTop, knobLeft + kDeckKnobSize, knobTop + kDeckKnobSize);
|
||||
const int innerLeftPx = knobLeft + (kDeckKnobSize - kDeckInnerDialSize) / 2;
|
||||
@@ -82,13 +91,16 @@ DeckGroupLayout layoutGroup(const DeckGroupDesc& g, const Rect& box) {
|
||||
const int labelTop = knobTop + kDeckKnobSize + 4;
|
||||
c.label = Rect::ltrb(c.cell.x, labelTop, c.cell.right(), labelTop + kDeckCellLabelH);
|
||||
out.cells.push_back(c);
|
||||
x += kDeckCellW;
|
||||
x += cellW;
|
||||
}
|
||||
if (g.rowToggle.id >= 0) {
|
||||
if (!g.cellIds.empty()) x += kDeckToggleGap;
|
||||
// Anchored past the whole reserved run, not past the last cell, so a residue margin
|
||||
// cannot shift it.
|
||||
int tx = innerLeft + runWidth;
|
||||
if (!g.cellIds.empty()) tx += kDeckToggleGap;
|
||||
const int segW = g.rowToggle.segWidth;
|
||||
const int togTop = cellTop + (kDeckCellH - kDeckToggleH) / 2;
|
||||
const Rect seg0 = Rect::ltrb(x, togTop, x + segW, togTop + kDeckToggleH);
|
||||
const Rect seg0 = Rect::ltrb(tx, togTop, tx + segW, togTop + kDeckToggleH);
|
||||
const Rect seg1 = Rect::ltrb(seg0.right(), togTop, seg0.right() + segW, togTop + kDeckToggleH);
|
||||
out.rowToggle = DeckToggleLayout{g.rowToggle.id, seg0, seg1};
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
//
|
||||
// The deck is a horizontal run of fenced groups, left->right, each a bordered box with a
|
||||
// caption row (caption left, the group's compact mode toggle right-anchored) over a knob
|
||||
// row of fixed cells (knob centered, label band beneath). A group may also place one
|
||||
// row of equal-width cells (knob centered, label band beneath). A group may also place one
|
||||
// two-segment toggle in the knob row after its cells. Groups that must keep stable
|
||||
// geometry across a mode flip reserve blank cells (id -1) so a mode flip never reflows
|
||||
// geometry across a mode flip reserve cell width (id -1) so a mode flip never reflows
|
||||
// neighbouring groups.
|
||||
//
|
||||
// Wrap is deterministic: groups place left-to-right with kDeckGroupGap between; a group
|
||||
@@ -57,7 +57,8 @@ struct DeckRadioDesc {
|
||||
};
|
||||
|
||||
// One fenced group, in deck order. `cellIds` are the knob cells left-to-right; an id of -1
|
||||
// is a reserved blank cell (geometry held, never hit). `captionWidth` is the px the shell
|
||||
// reserves one cell's WIDTH without a cell, and the cells present divide the whole run —
|
||||
// see this module's CLAUDE.md bullet for what that buys. `captionWidth` is the px the shell
|
||||
// reserves for the caption text (this module does not measure text).
|
||||
struct DeckGroupDesc {
|
||||
int id = 0; // shell group id (opaque here)
|
||||
@@ -87,7 +88,7 @@ struct DeckRadioLayout {
|
||||
|
||||
struct DeckCellLayout {
|
||||
int id = -1;
|
||||
Rect cell; // the full 48x58 cell
|
||||
Rect cell; // the whole cell; width is the group's reserved run divided by its cell count
|
||||
Rect knob; // the centered kDeckKnobSize square (the knob circle inscribes it)
|
||||
Rect inner; // the concentric kDeckInnerDialSize square inside `knob`
|
||||
Rect label; // the 12px label band beneath the knob
|
||||
@@ -141,7 +142,7 @@ struct DeckHit {
|
||||
// The deck element a point lands on: a knob cell (the whole cell, not just the knob
|
||||
// circle — the shell anchors the vertical drag wherever the grab lands, with `inner` marking
|
||||
// a grab on the concentric inner dial), a caption-toggle segment, a row-toggle segment, or
|
||||
// the caption-row corner radio. Blank cells (id -1) and everything else miss.
|
||||
// the caption-row corner radio. Everything else — fence, padding, outside — misses.
|
||||
DeckHit hitTestDeck(const DeckLayout& layout, int x, int y);
|
||||
|
||||
} // namespace reasampler::instrument::ui
|
||||
|
||||
Reference in New Issue
Block a user