// 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