S10: capture-first ReaSampler 9000 editor — browser, single-capture setup, zones toggle

Reverse S4 auto-select (empty=silence+empty state), add peak-thumbnail capture
browser + bank filter + keyboard-strip drag machine, v3 component state (selection
+ zones), and the S-NAME-1 filename/display rename (UID locked). MSVC min/max
macro collisions fixed post-implementation; 26/26 tests green.
This commit is contained in:
2026-07-26 20:36:36 -04:00
parent 991c190bb8
commit 7151e19432
16 changed files with 1906 additions and 325 deletions
+92
View File
@@ -0,0 +1,92 @@
// capture_browser.h — PURE layout + hit-test for the S10 capture-first editor's default
// face: a scannable grid of capture CARDS with a bank-FILTER tab strip above it. NO VST3,
// NO REAPER, NO SWELL/LICE types at the boundary. The mirror of editor_geometry /
// embed_strip / mode_switch: the fiddly card-grid + tab arithmetic lives here so it is
// unit-tested outside the DAW, while the editor shell draws each card's peak thumbnail +
// name + root/key badge and routes clicks into these functions.
//
// The browser replaces the old text item-list (the named anti-pattern). It lays out N
// cards in a fixed-cell grid that wraps across the browser width, and a horizontal tab
// strip of bank filters (one tab per bank_book bank + an "All" tab) above the grid. This
// module knows only COUNTS and RECTS — it draws nothing and holds no sample data; the
// shell owns the SampleChoice list, the peak envelopes, and the filter state, and asks this
// module only "where does card i draw" / "what did the user click".
//
// Scroll is NOT here (S12 layers it over this module). The browser lays out every card
// top-down; the shell clips at the browser's bottom until S12 adds a scroll offset. Keeping
// scroll out keeps this module the stable card/tab geometry S12 builds on.
//
// It reuses the same Rect + contains() as editor_geometry (one shared geometry idiom).
#pragma once
#include "editor_geometry.h" // Rect, contains — one shared geometry idiom
namespace reasampler::vst {
// Fixed browser metrics, exposed so the shell and tests agree. The card is sized to show a
// peak thumbnail with a name + badge line under it — scannable by eye, not a dense list.
inline constexpr int kBrowserTabHeight = 26; // the bank-filter tab strip band height
inline constexpr int kBrowserCardWidth = 132; // one card cell width (incl. gutter)
inline constexpr int kBrowserCardHeight = 84; // one card cell height (incl. gutter)
inline constexpr int kBrowserCardGutter = 8; // inset between the cell edge and the card
inline constexpr int kBrowserThumbHeight = 44; // the peak-thumbnail band inside a card
// The browser's regions, derived from the (w x h) area the shell allots it. Both clamp to
// the area so a degenerate (tiny/zero) size never yields an inverted rect.
struct BrowserLayout {
Rect tabStrip; // top: the bank-filter tabs
Rect grid; // below the tabs: where the capture cards tile
int columns = 1; // cards per row in `grid` (>= 1); derived from grid.width()
};
// Divide a (w x h) browser area into its regions and compute the column count. Pure: same
// inputs -> same layout. The tab strip takes a fixed height at the top (clamped so it never
// exceeds the area); the grid takes the rest. columns = max(1, grid.width()/cardWidth) so a
// browser narrower than one card still lays out a single column. A zero/negative size
// yields empty rects + columns==1.
BrowserLayout layoutBrowser(int w, int h);
// The cell rect of capture card `index` (0-based) in the grid, laid out left-to-right then
// top-to-bottom across `columns`. This is the full CELL (card + gutter); cardContentRect
// insets it to the drawable card. Rows past the visible grid are still computed (the shell
// clips at paint time). A negative index yields an empty rect. Pure.
Rect cardCellRect(const BrowserLayout& layout, int index);
// The drawable card rect inside a cell: the cell inset by kBrowserCardGutter on all sides.
// The shell fills this (background + border) and draws the thumbnail/name/badge inside it. Pure.
Rect cardContentRect(const BrowserLayout& layout, int index);
// The peak-thumbnail sub-rect at the top of a card's content: full card width, the top
// kBrowserThumbHeight (clamped to the card height). The shell draws the envelope here; the
// name + badge go in the remaining strip below. Pure.
Rect cardThumbnailRect(const BrowserLayout& layout, int index);
// The name/badge sub-rect below the thumbnail: the card content minus the thumbnail band.
// The shell draws the display name + root/key badge here. Pure.
Rect cardLabelRect(const BrowserLayout& layout, int index);
// The card a click at (x, y) lands on, given `cardCount` cards, or -1 for a click outside
// every card (in a gutter, past the last card, or on the tab strip). Only the card CONTENT
// rect counts as a hit — a click in the inter-card gutter is a miss. Pure.
int cardHitTest(const BrowserLayout& layout, int cardCount, int x, int y);
// --- Bank-filter tabs --------------------------------------------------------
//
// The tab strip divides tabStrip into `tabCount` equal segments (mirror of mode_switch):
// one tab per bank_book bank plus a leading "All" tab the shell prepends, so tabCount ==
// bankCount + 1 in practice. This module only divides the strip + hit-tests; the shell
// supplies the labels and tracks which tab is active. A tab click narrows the card list to
// that bank (the shell filters its SampleChoice list before laying out cards).
// The rect of tab `index` (0-based) when the strip is divided into `tabCount` equal
// segments. The last tab absorbs any width remainder so the tabs tile the whole strip with
// no gap (mirror of mode_switch's segment split). A negative index or tabCount<=0 yields an
// empty rect. Pure.
Rect filterTabRect(const BrowserLayout& layout, int tabCount, int index);
// The tab a click at (x, y) lands on, given `tabCount` tabs, or -1 for a click outside the
// tab strip. Pure.
int filterTabHitTest(const BrowserLayout& layout, int tabCount, int x, int y);
} // namespace reasampler::vst