Q-W2v: split VST god-modules — editor 8 face-axis TUs (+pure layout hoist), processor 3 TUs, component_state_io codec split (extension drops the voice engine), zone_params.h, core/wire putLE; formats frozen, 61/61 green
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "core/instrument/ui/browser_scroll.h"
|
||||
|
||||
#include "core/instrument/ui/editor_geometry.h" // kPad / kTitleHeight / kNavButtonWidth (Q-W2v hoist)
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
@@ -155,4 +157,25 @@ std::vector<int> filterNameIndices(const std::vector<std::string>& names,
|
||||
return out;
|
||||
}
|
||||
|
||||
// The Browse-modal regions (hoisted from the editor shell, Q-W2v/T2-06 — body verbatim;
|
||||
// the band metrics come from editor_geometry, the search height from searchBoxRect).
|
||||
BrowseModal computeBrowseModal(int w, int h) {
|
||||
constexpr int kBrowseFooterH = 30;
|
||||
BrowseModal m;
|
||||
const int titleH = (std::min)(kTitleHeight, h);
|
||||
m.title = Rect::ltrb(0, 0, w, titleH);
|
||||
m.back = Rect::ltrb(w - kPad - kNavButtonWidth, 2, w - kPad, (std::max)(2, titleH - 2));
|
||||
// Search box below the title, spanning the width (searchBoxRect lays it out from 0).
|
||||
const Rect sb = searchBoxRect(w);
|
||||
m.search = Rect::ltrb(kPad, titleH, w - kPad, titleH + sb.height);
|
||||
const int footerTop = (std::max)(m.search.bottom(), h - kBrowseFooterH);
|
||||
m.content = Rect::ltrb(0, m.search.bottom(), w, footerTop);
|
||||
// Footer: Cancel (left) + Load (right).
|
||||
const int fTop = footerTop + 3;
|
||||
const int fBot = (std::max)(fTop, h - 3);
|
||||
m.cancel = Rect::ltrb(kPad, fTop, kPad + 90, fBot);
|
||||
m.confirm = Rect::ltrb(w - kPad - 90, fTop, w - kPad, fBot);
|
||||
return m;
|
||||
}
|
||||
|
||||
} // namespace reasampler::instrument::ui
|
||||
|
||||
@@ -104,4 +104,21 @@ bool nameMatchesQuery(const std::string& name, const std::string& query);
|
||||
std::vector<int> filterNameIndices(const std::vector<std::string>& names,
|
||||
const std::string& query);
|
||||
|
||||
// --- The Browse-modal (S-VIEW-5) top-level regions (Q-W2v hoist, T2-06) -------
|
||||
//
|
||||
// A title band with a Back button, the search box, the browser sub-area (tabs + card
|
||||
// grid — layoutBrowser's origin), and a footer with Cancel / Load-confirm. The picker
|
||||
// covers the full window (F3: full-window overlay). Draw + hit-test both derive from
|
||||
// this single layout so they never drift. Homed here (not editor_geometry) because the
|
||||
// search-box height feeds it — browser_scroll already owns the search/scroll geometry.
|
||||
struct BrowseModal {
|
||||
Rect title;
|
||||
Rect back; // the "Back" title-band button
|
||||
Rect search; // the type-to-filter box (absolute)
|
||||
Rect content; // the browser sub-area (tabs + grid) — layoutBrowser's origin
|
||||
Rect cancel; // footer Cancel
|
||||
Rect confirm; // footer Load (confirm)
|
||||
};
|
||||
BrowseModal computeBrowseModal(int w, int h);
|
||||
|
||||
} // namespace reasampler::instrument::ui
|
||||
|
||||
@@ -159,4 +159,150 @@ bool addZoneHitTest(const KeymapEditorLayout& layout, int x, int y) {
|
||||
return contains(layout.addZoneButton, x, y);
|
||||
}
|
||||
|
||||
// --- r11 Sample / Zone face layout (Q-W2v hoist, T2-06) ----------------------
|
||||
// Bodies moved verbatim from the reasampler_editor shell (behavior-identical); the
|
||||
// only signature change is clusterRects' `knobSize` parameter (formerly knob_deck's
|
||||
// kDeckKnobSize read directly — passed in so this module stays knob_deck-free).
|
||||
|
||||
namespace {
|
||||
|
||||
// Fixed band metrics (formerly the editor shell's anon-ns constants).
|
||||
constexpr int kHeroMinHeight = 150; // the elastic hero's floor (r11)
|
||||
constexpr int kClusterHeight = 52; // root strip + preview + vel knob + curve btn + channel toggle
|
||||
constexpr int kStripBandHeight = 40; // the keyboard-strip band height (root strip + zone strip)
|
||||
|
||||
// The r11 cluster's fixed right-anchored run (left -> right: Preview button, the radial
|
||||
// preview-velocity knob cell, the mini curve-preview button, Mono|Stereo).
|
||||
constexpr int kPreviewBtnW = 64;
|
||||
constexpr int kVelCellW = 48; // the Vel knob cell (deck cell grammar)
|
||||
constexpr int kCurveBtnSize = 28; // the square curve-preview button
|
||||
|
||||
// The S7 mono/stereo toggle segments.
|
||||
constexpr int kChanSegW = 52;
|
||||
constexpr int kChanSegH = 18;
|
||||
|
||||
} // namespace
|
||||
|
||||
// r11 band order: title (fixed) -> hero (ELASTIC: absorbs all height left after the fixed
|
||||
// bands, floor kHeroMinHeight) -> cluster (fixed) -> deck (fixed height `deckH`, bottom-
|
||||
// anchored). When the window is too short for the floor (below the checkSizeConstraint
|
||||
// minimum — a defensive case), the hero keeps its floor and the lower bands clip past the
|
||||
// window bottom gracefully.
|
||||
SampleBands computeSampleBands(int w, int h, int deckH) {
|
||||
SampleBands b;
|
||||
const int titleH = (std::min)(kTitleHeight, h);
|
||||
b.title = Rect::ltrb(0, 0, w, titleH);
|
||||
// Two nav buttons right-anchored in the title band (Browse then Zone).
|
||||
const int navTop = 2;
|
||||
const int navBot = (std::max)(navTop, titleH - 2);
|
||||
const Rect zone = Rect::ltrb(w - kPad - kNavButtonWidth, navTop, w - kPad, navBot);
|
||||
const Rect browse = Rect::ltrb(zone.x - 4 - kNavButtonWidth, navTop, zone.x - 4, navBot);
|
||||
b.navBrowse = browse;
|
||||
b.navZone = zone;
|
||||
|
||||
int deckTop = h - kPad - deckH;
|
||||
int clusterTop = deckTop - kClusterHeight - 4;
|
||||
int heroBottom = clusterTop - 4;
|
||||
if (heroBottom - titleH < kHeroMinHeight) {
|
||||
heroBottom = titleH + kHeroMinHeight; // hero floor wins; lower bands clip below
|
||||
clusterTop = heroBottom + 4;
|
||||
deckTop = clusterTop + kClusterHeight + 4;
|
||||
}
|
||||
b.hero = Rect::ltrb(kPad, titleH, w - kPad, heroBottom);
|
||||
b.cluster = Rect::ltrb(0, clusterTop, w, clusterTop + kClusterHeight);
|
||||
b.deck = Rect::ltrb(kPad, deckTop, w - kPad, deckTop + deckH);
|
||||
return b;
|
||||
}
|
||||
|
||||
// Draw + hit-test both derive from this ONE formula.
|
||||
ClusterRects clusterRects(const Rect& cluster, const Rect& chanMono, int knobSize) {
|
||||
ClusterRects r;
|
||||
const int stripTop = cluster.y + (cluster.height - kStripBandHeight) / 2;
|
||||
const int stripBot = stripTop + kStripBandHeight;
|
||||
const int curveTop = cluster.y + (cluster.height - kCurveBtnSize) / 2;
|
||||
r.curveBtn = Rect::ltrb(chanMono.x - kPad - kCurveBtnSize, curveTop,
|
||||
chanMono.x - kPad, curveTop + kCurveBtnSize);
|
||||
r.velCell = Rect::ltrb(r.curveBtn.x - kPad - kVelCellW, stripTop,
|
||||
r.curveBtn.x - kPad, stripBot);
|
||||
const int knobLeft = r.velCell.x + (kVelCellW - knobSize) / 2;
|
||||
r.velKnob = Rect::ltrb(knobLeft, r.velCell.y, knobLeft + knobSize,
|
||||
r.velCell.y + knobSize);
|
||||
r.velLabel = Rect::ltrb(r.velCell.x, r.velKnob.bottom(), r.velCell.right(), r.velCell.bottom());
|
||||
r.preview = Rect::ltrb(r.velCell.x - kPad - kPreviewBtnW, stripTop,
|
||||
r.velCell.x - kPad, stripBot);
|
||||
r.rootStrip = Rect::ltrb(cluster.x + kPad, stripTop, r.preview.x - kPad, stripBot);
|
||||
return r;
|
||||
}
|
||||
|
||||
ChannelToggleRects channelToggleRects(const Rect& area) {
|
||||
const int top = area.y + (area.height - kChanSegH) / 2;
|
||||
const int right = area.right() - kPad;
|
||||
const Rect stereo = Rect::ltrb(right - kChanSegW, top, right, top + kChanSegH);
|
||||
const Rect mono = Rect::ltrb(stereo.x - kChanSegW, top, stereo.x, top + kChanSegH);
|
||||
return {mono, stereo};
|
||||
}
|
||||
|
||||
Rect zoneContentArea(int w, int h) {
|
||||
const int titleH = (std::min)(kTitleHeight, h);
|
||||
return Rect::ltrb(0, titleH, w, h);
|
||||
}
|
||||
|
||||
Rect zoneBackRect(int w, int h) {
|
||||
return Rect::ltrb(w - kPad - kNavButtonWidth, 2, w - kPad,
|
||||
(std::max)(2, (std::min)(kTitleHeight, h) - 2));
|
||||
}
|
||||
|
||||
Rect zoneAddRect(const Rect& content) {
|
||||
return Rect::ltrb(content.x + kPad, content.y + 4, content.x + kPad + 96,
|
||||
content.y + 4 + 20);
|
||||
}
|
||||
|
||||
Rect zoneDeleteRect(const Rect& addR) {
|
||||
return Rect::ltrb(addR.right() + 8, addR.y, addR.right() + 8 + 64, addR.bottom());
|
||||
}
|
||||
|
||||
// Zone content sits below the "+ Add Zone" affordance (top+4, height 20) with a 12px
|
||||
// gap, padded kPad horizontally. All call sites use this formula.
|
||||
Rect zonesStripArea(const Rect& content) {
|
||||
const int stripTop = content.y + 4 + 20 + 12; // addR.bottom() + 12
|
||||
return Rect::ltrb(content.x + kPad, stripTop, content.right() - kPad,
|
||||
stripTop + kStripBandHeight);
|
||||
}
|
||||
|
||||
// Anchored off zonesStripArea.bottom() so the legend top tracks the strip bottom
|
||||
// without re-inlining the strip arithmetic here.
|
||||
Rect noteEntryFieldsArea(const Rect& content) {
|
||||
const int stripBottom = zonesStripArea(content).bottom();
|
||||
const int top = stripBottom + 8; // legendTop (== zonesStripArea.bottom() + 8)
|
||||
return Rect::ltrb(content.x + 8 + 128, top, content.right() - 8, top + 18);
|
||||
}
|
||||
|
||||
Rect noteEntryFieldRect(const Rect& fields, int f) {
|
||||
if (f < 0 || f > 2 || fields.width <= 0) return Rect{};
|
||||
const int segW = fields.width / 3;
|
||||
const int left = fields.x + f * segW + (f > 0 ? 4 : 0); // small inter-field gap
|
||||
const int right = (f == 2) ? fields.right() : fields.x + (f + 1) * segW;
|
||||
return Rect::ltrb(left, fields.y, right, fields.bottom());
|
||||
}
|
||||
|
||||
Rect zonesControlPanel(const Rect& content) {
|
||||
const Rect strip = zonesStripArea(content);
|
||||
const int panelTop = strip.bottom() + 8 + 18 + 8; // strip + the 18px legend row + gap
|
||||
return Rect::ltrb(content.x + kPad, panelTop, content.right() - kPad,
|
||||
content.bottom() - 4);
|
||||
}
|
||||
|
||||
// FB2 (R11-F2 parity): the deck lays out from the panel top (top-anchored), with a
|
||||
// column at the panel's right reserved for the mini curve-preview button so no deck row
|
||||
// starts inside it.
|
||||
Rect zonesDeckArea(const Rect& content) {
|
||||
const Rect panel = zonesControlPanel(content);
|
||||
return Rect::ltrb(panel.x, panel.y, panel.right() - kCurveBtnSize - kPad, panel.bottom());
|
||||
}
|
||||
|
||||
Rect zonesCurveButton(const Rect& content) {
|
||||
const Rect panel = zonesControlPanel(content);
|
||||
return Rect::ltrb(panel.right() - kCurveBtnSize, panel.y, panel.right(), panel.y + kCurveBtnSize);
|
||||
}
|
||||
|
||||
} // namespace reasampler::instrument::ui
|
||||
|
||||
@@ -139,4 +139,86 @@ ZoneHit zoneHitTest(const KeymapEditorLayout& layout, int zoneCount, int x, int
|
||||
// True if (x, y) lands on the "Add Zone" button. Pure.
|
||||
bool addZoneHitTest(const KeymapEditorLayout& layout, int x, int y);
|
||||
|
||||
// --- r11 Sample / Zone face layout (Q-W2v hoist, T2-06) ----------------------
|
||||
//
|
||||
// The capture-first editor's band/cluster/zone-surface layout math, hoisted out of the
|
||||
// reasampler_editor shell where it had accreted untestable (the §2 scope gap). Draw and
|
||||
// hit-test both derive every rect from these ONE formulas so they can never drift; the
|
||||
// shell only draws + routes. The Browse-modal layout lives in browser_scroll (its search
|
||||
// box height feeds it — dependency-clean placement beside its scroll/search siblings).
|
||||
|
||||
// Shared band metrics (the shell's remaining direct uses: horizontal padding + the
|
||||
// title-band height; everything else is internal to the layout functions below).
|
||||
inline constexpr int kPad = 8;
|
||||
inline constexpr int kTitleHeight = 26;
|
||||
inline constexpr int kNavButtonWidth = 62; // Browse / Zone / Back title-band buttons
|
||||
|
||||
// The r11 Sample-face bands (top->bottom): a TITLE band (name + Browse/Zone nav), the
|
||||
// FULL-WIDTH ELASTIC HERO (absorbs all height left after the fixed bands, floor
|
||||
// kHeroMinHeight), the ROOT + PREVIEW CLUSTER, and the bottom-anchored KNOB DECK
|
||||
// (height `deckH` from the pure knob_deck wrap). When the window is too short for the
|
||||
// hero floor (below the checkSizeConstraint minimum — defensive), the hero keeps its
|
||||
// floor and the lower bands clip past the window bottom gracefully.
|
||||
struct SampleBands {
|
||||
Rect title; // top: name + Browse/Zone nav buttons
|
||||
Rect navBrowse; // the "Browse" title-band button
|
||||
Rect navZone; // the "Zone" title-band button
|
||||
Rect hero; // the FULL-WIDTH ELASTIC hero waveform + S-VIEW-3 envelope overlay
|
||||
Rect cluster; // root strip + preview + vel knob + curve button + channel toggle
|
||||
Rect deck; // the bottom-anchored knob deck (height from the pure knob_deck wrap)
|
||||
};
|
||||
SampleBands computeSampleBands(int w, int h, int deckH);
|
||||
|
||||
// The r11 cluster sub-rects: the root strip keeps the left side at REMAINDER width; the
|
||||
// right side is the fixed-width right-anchored run (Preview 64 · Vel knob cell 48 · curve
|
||||
// preview button 28 · Mono|Stereo). `knobSize` is the deck knob square (knob_deck's
|
||||
// kDeckKnobSize — passed in so this module does not depend on knob_deck).
|
||||
struct ClusterRects {
|
||||
Rect rootStrip; // remainder-width fenced root strip
|
||||
Rect preview; // the preview-trigger button
|
||||
Rect velCell; // the radial preview-velocity knob cell (knob + label band)
|
||||
Rect velKnob; // the knob square at the cell's top
|
||||
Rect velLabel; // the label band beneath it
|
||||
Rect curveBtn; // the mini curve-preview button (opens the popup)
|
||||
};
|
||||
ClusterRects clusterRects(const Rect& cluster, const Rect& chanMono, int knobSize);
|
||||
|
||||
// The S7 mono/stereo toggle: a two-segment control right-anchored in `area`, vertically
|
||||
// centered. Returns {mono-segment, stereo-segment}, side by side.
|
||||
struct ChannelToggleRects {
|
||||
Rect mono;
|
||||
Rect stereo;
|
||||
};
|
||||
ChannelToggleRects channelToggleRects(const Rect& area);
|
||||
|
||||
// The Zone-view (S-VIEW-8) content area: the whole window below the title band.
|
||||
Rect zoneContentArea(int w, int h);
|
||||
|
||||
// The Zone/Browse "Back" title-band button (right-anchored — the same slot the Sample
|
||||
// face's Zone nav button occupies).
|
||||
Rect zoneBackRect(int w, int h);
|
||||
|
||||
// The "+ Add Zone" affordance at the top of the Zone content, and the "Delete" button
|
||||
// beside it (Delete only draws/hits when a zone is selected).
|
||||
Rect zoneAddRect(const Rect& content);
|
||||
Rect zoneDeleteRect(const Rect& addR);
|
||||
|
||||
// The Zone-view keyboard strip rect: below the "+ Add Zone" affordance with a 12px gap,
|
||||
// padded kPad horizontally.
|
||||
Rect zonesStripArea(const Rect& content);
|
||||
|
||||
// The S12 numeric-entry field ROW area inside the Zones legend (a band to the right of
|
||||
// the sample label), and the rect of field `f` (0=low, 1=high, 2=root) within it —
|
||||
// three equal segments left-to-right. An out-of-range index yields an empty rect.
|
||||
Rect noteEntryFieldsArea(const Rect& content);
|
||||
Rect noteEntryFieldRect(const Rect& fields, int f);
|
||||
|
||||
// The per-zone parameter panel below the strip + the one-line legend, running to the
|
||||
// content bottom; the FB2 knob-deck area within it (a column at the right reserved for
|
||||
// the mini curve-preview button); and that button's rect (the cluster's 28px square,
|
||||
// right-anchored at the panel top).
|
||||
Rect zonesControlPanel(const Rect& content);
|
||||
Rect zonesDeckArea(const Rect& content);
|
||||
Rect zonesCurveButton(const Rect& content);
|
||||
|
||||
} // namespace reasampler::instrument::ui
|
||||
|
||||
Reference in New Issue
Block a user