Reflow the deck into two categorical rows plus a double-height MASTER bus deck
Row membership is now the group's own property, not a wrap outcome. FILTER's Band|Notch moves to its caption slack, which is what makes the sound row fit. MASTER gains the limiter toggle, the output meter and the GR lamp.
This commit is contained in:
@@ -3,16 +3,10 @@
|
||||
// action_bar/param_slider; the knob primitive itself (value<->needle-angle, drag) is
|
||||
// param_slider's — a knob cell here is just a rect the shell composes it into.
|
||||
//
|
||||
// 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 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 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
|
||||
// that does not fit the remaining width starts a new row (whole groups only, never
|
||||
// split); the first group of a row always places even if wider than the row.
|
||||
// A group is a fenced box: caption row (caption left, toggles and a corner radio
|
||||
// right-anchored) over a knob row of equal-width cells, optionally followed by one row
|
||||
// toggle. Row membership is a PROPERTY OF THE GROUP (DeckRow), never a wrap outcome — see
|
||||
// the justification law at layoutDeck.
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -39,6 +33,7 @@ inline constexpr int kDeckToggleGap = 4; // caption text -> toggle / cells
|
||||
inline constexpr int kDeckGroupGap = 12; // gap between groups on a row
|
||||
inline constexpr int kDeckRowGap = 8; // gap between wrapped deck rows
|
||||
inline constexpr int kDeckRadioSize = 12; // the caption-row corner radio square
|
||||
inline constexpr int kDeckColumnGap = 8; // the spanning deck's cell column -> its readout column
|
||||
// The knob cell's INNER dial: a concentric sub-disc that edits a second, related value while
|
||||
// the outer ring keeps editing the cell's own. Geometry only — WHICH cells carry one is
|
||||
// deck_groups' call, so a cell without an inner value simply resolves an inner hit as a knob.
|
||||
@@ -46,6 +41,15 @@ inline constexpr int kDeckInnerDialSize = 20;
|
||||
// One group box: padding + caption + gap + cell row + padding.
|
||||
inline constexpr int kDeckGroupH =
|
||||
kDeckGroupPadY + kDeckCaptionH + kDeckCaptionGap + kDeckCellH + kDeckGroupPadY;
|
||||
// The spanning deck's box: it stands across both categorical rows AND the seam between them,
|
||||
// which is what lets its two cell slots land on the two rows' own knob baselines.
|
||||
inline constexpr int kDeckSpanningH = 2 * kDeckGroupH + kDeckRowGap;
|
||||
|
||||
// The deck's two categorical rows, plus the row-spanning bus deck. Sound is what the voice
|
||||
// IS, Contour is how it moves over time, Spanning is what happens after the mixer. Declared
|
||||
// here rather than with the group inventory because the LAYOUT is what reads it; which group
|
||||
// sits in which row is deck_groups' deckRowFor.
|
||||
enum class DeckRow { Sound, Contour, Spanning };
|
||||
|
||||
// --- The deck's width budget at the editor's floor ------------------------------------
|
||||
// DECLARATIONS of budget, not measurements: nothing here is computed from a descriptor, and a
|
||||
@@ -67,15 +71,29 @@ struct DeckToggleDesc {
|
||||
};
|
||||
|
||||
// A single-square corner radio (an exclusive selector across groups, so the group itself
|
||||
// carries no state). id -1 = absent.
|
||||
// carries no state). id -1 = absent. `passive` reuses the same slot for a READOUT lamp: the
|
||||
// hit-test skips it entirely, so the shell cannot accidentally grow a gesture on it.
|
||||
struct DeckRadioDesc {
|
||||
int id = -1;
|
||||
bool passive = false;
|
||||
};
|
||||
|
||||
// A full-height readout column beside a spanning group's cell slots (the output meter). Only
|
||||
// a Spanning group may carry one — a categorical row's groups have no height to span.
|
||||
// id -1 = absent.
|
||||
struct DeckColumnDesc {
|
||||
int id = -1;
|
||||
int width = 0;
|
||||
};
|
||||
|
||||
// One fenced group, in deck order. `cellIds` are the knob cells left-to-right; an id of -1
|
||||
// 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).
|
||||
//
|
||||
// A SPANNING group reads `cellIds` down instead of across: one FIXED kDeckCellW slot per
|
||||
// declared id, at successive row baselines, reserves included. The run-division law above is
|
||||
// horizontal only — applied vertically it would stretch a lone knob over the whole box.
|
||||
struct DeckGroupDesc {
|
||||
int id = 0; // shell group id (opaque here)
|
||||
int captionWidth = 60;
|
||||
@@ -87,6 +105,8 @@ struct DeckGroupDesc {
|
||||
DeckToggleDesc captionToggle2;
|
||||
std::vector<int> cellIds; // knob cells; -1 reserves width only, no cell (see above)
|
||||
DeckToggleDesc rowToggle; // in the knob row after the cells; id -1 = none
|
||||
DeckRow row = DeckRow::Sound;
|
||||
DeckColumnDesc column; // Spanning groups only; id -1 = none
|
||||
};
|
||||
|
||||
// --- Laid-out geometry ---------------------------------------------------------------
|
||||
@@ -100,6 +120,12 @@ struct DeckToggleLayout {
|
||||
struct DeckRadioLayout {
|
||||
int id = -1;
|
||||
Rect box;
|
||||
bool passive = false; // a readout lamp, not a selector — see DeckRadioDesc
|
||||
};
|
||||
|
||||
struct DeckColumnLayout {
|
||||
int id = -1;
|
||||
Rect box;
|
||||
};
|
||||
|
||||
struct DeckCellLayout {
|
||||
@@ -119,34 +145,45 @@ struct DeckGroupLayout {
|
||||
DeckToggleLayout captionToggle2;
|
||||
std::vector<DeckCellLayout> cells;
|
||||
DeckToggleLayout rowToggle; // id -1 when absent
|
||||
DeckColumnLayout column; // id -1 when absent (Spanning groups only)
|
||||
};
|
||||
|
||||
struct DeckLayout {
|
||||
std::vector<DeckGroupLayout> groups;
|
||||
int rowCount = 0;
|
||||
int height = 0; // rowCount * kDeckGroupH + (rowCount-1) * kDeckRowGap; 0 for no groups
|
||||
int rowCount = 0; // POPULATED categorical rows (0..2). A spanning deck is in neither.
|
||||
int height = 0; // the tallest thing laid out; 0 for no groups
|
||||
};
|
||||
|
||||
// Width of one group box: the wider of its caption row (caption + gap + toggle) and its
|
||||
// knob row (cells + gap + row toggle), plus horizontal padding.
|
||||
// Width of one group box: the wider of its caption row (caption + gap + toggles + radio) and
|
||||
// its knob row, plus horizontal padding. A Spanning group's knob row is one cell wide plus
|
||||
// its readout column, because its cells stack.
|
||||
int deckGroupWidth(const DeckGroupDesc& g);
|
||||
|
||||
// Number of deck rows the groups occupy at `availWidth` under the greedy whole-group wrap.
|
||||
// 0 for an empty list.
|
||||
int deckRowCount(const std::vector<DeckGroupDesc>& groups, int availWidth);
|
||||
// How many of the two categorical rows carry at least one group (0..2). Independent of width:
|
||||
// row membership is the group's own property.
|
||||
int deckRowCount(const std::vector<DeckGroupDesc>& groups);
|
||||
|
||||
// Total deck height at `availWidth` (rows * kDeckGroupH + inter-row gaps). The shell
|
||||
// Total deck height: the categorical rows, or the spanning deck when it is taller. The shell
|
||||
// bottom-anchors a band of exactly this height.
|
||||
int deckHeight(const std::vector<DeckGroupDesc>& groups, int availWidth);
|
||||
int deckHeight(const std::vector<DeckGroupDesc>& groups);
|
||||
|
||||
// Lays the groups out from (left, top) within `availWidth`, wrapping per deckRowCount's
|
||||
// rule. Every rect is absolute.
|
||||
// Lays the groups out from (left, top) within `availWidth`. Every rect is absolute, and
|
||||
// `groups` comes back in DECK order — the same position as the descriptor it was built from,
|
||||
// whichever row that descriptor landed in.
|
||||
//
|
||||
// Spanning groups are right-anchored at `left + availWidth` and take no part in either row's
|
||||
// justification; the ROW BLOCK is what remains to their left. Inside the block each row is
|
||||
// justified SPACE-BETWEEN: groups keep their natural widths and the slack becomes gutters,
|
||||
// divided equally with the integer residue going to the leftmost ones. Decks are never
|
||||
// stretched. Below the width the block needs, every gutter sits at kDeckGroupGap and the row
|
||||
// overflows right rather than wrapping — the shell clamps the window to a floor that fits
|
||||
// (sample_bands' kEditorMinWidth), so that degrade is unreachable in the editor.
|
||||
DeckLayout layoutDeck(const std::vector<DeckGroupDesc>& groups, int left, int top,
|
||||
int availWidth);
|
||||
|
||||
// --- Hit-test --------------------------------------------------------------------------
|
||||
|
||||
enum class DeckHitKind { None, Knob, CaptionToggle, RowToggle, CaptionRadio };
|
||||
enum class DeckHitKind { None, Knob, CaptionToggle, RowToggle, CaptionRadio, Column };
|
||||
|
||||
struct DeckHit {
|
||||
DeckHitKind kind = DeckHitKind::None;
|
||||
@@ -157,8 +194,9 @@ 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. Everything else — fence, padding, outside — misses.
|
||||
// a grab on the concentric inner dial), a caption-toggle segment, a row-toggle segment, an
|
||||
// interactive caption-row corner radio, or a spanning group's readout column. Everything
|
||||
// else — fence, padding, a PASSIVE radio, outside — misses.
|
||||
DeckHit hitTestDeck(const DeckLayout& layout, int x, int y);
|
||||
|
||||
// The knob FACE a point lands on. id -1 is a miss.
|
||||
|
||||
Reference in New Issue
Block a user