Files
reasampler/src/core/instrument/ui/browser_scroll.h
T

96 lines
4.6 KiB
C++

// browser_scroll.h — scroll + type-to-filter geometry layered over capture_browser. Mirror
// of capture_browser/editor_geometry; the shell draws the clipped card window, scrollbar,
// and search field, and routes wheel/drag/keystrokes into these functions.
//
// capture_browser lays out every card top-down and the shell clips at the browser bottom —
// a bank longer than the panel has no way to reach the rest. This module adds scroll (a
// vertical pixel offset with max-offset clamp, visible-row window, scrollbar thumb, and
// thumb-drag<->offset mapping) and search (a case-insensitive name-substring filter that
// composes with capture_browser's bank filter — the shell applies the bank filter first,
// then this search narrows within it).
//
// Holds no card data and draws nothing — knows only the browser layout, counts, and the
// scroll offset the shell owns as transient UI state.
#pragma once
#include <string>
#include <vector>
#include "core/instrument/ui/capture_browser.h" // BrowserLayout, cardCellRect, kBrowserCardHeight, Rect
namespace reasampler::instrument::ui {
// Width of the vertical scrollbar gutter at the grid's right edge. When content fits (no
// scroll needed), scrollThumbRect returns empty and the shell may reclaim the gutter.
inline constexpr int kScrollbarWidth = 10;
// Height of the type-to-filter search box the shell draws above the tab strip.
// capture_browser's tab strip + grid sit below this band.
inline constexpr int kSearchBoxHeight = 22;
// Total pixel height the card grid needs for `cardCount` cards at `layout`'s column
// count: rows (ceil(cardCount / columns)) times the fixed cell height.
int scrollContentHeight(const BrowserLayout& layout, int cardCount);
// Maximum scroll offset: content height minus visible grid height, floored at 0.
int scrollMaxOffset(const BrowserLayout& layout, int cardCount);
// Clamps a proposed scroll offset into [0, scrollMaxOffset].
int clampScrollOffset(const BrowserLayout& layout, int cardCount, int proposedOffset);
// Half-open range of card indices [first, last) at least partially visible at scroll
// `offset` (assumed pre-clamped). The shell draws only these cards.
struct VisibleRange {
int first = 0;
int last = 0;
};
VisibleRange visibleCardRange(const BrowserLayout& layout, int cardCount, int offset);
// Cell rect of card `index` shifted up by the scroll offset (the shell still adds the
// browser sub-area origin).
Rect scrolledCardCellRect(const BrowserLayout& layout, int index, int offset);
// Vertical scrollbar thumb rect within the grid's right-edge gutter, sized proportional
// to the visible fraction and positioned proportional to the scroll offset. Empty when
// the content fits. A minimum thumb height keeps a tiny thumb grabbable on a long bank.
Rect scrollThumbRect(const BrowserLayout& layout, int cardCount, int offset);
// Maps a thumb-drag to a new (clamped) scroll offset: `startOffset` shifted by the pixel
// delta scaled from thumb-track pixels to content pixels. A degenerate track or fitting
// content pins to startOffset.
int thumbDragToOffset(const BrowserLayout& layout, int cardCount, int startOffset, int dyPixels);
// Search-box rect: full-width band of height kSearchBoxHeight at the top of the browser
// area. `w` is the browser sub-area width; the shell adds its origin.
Rect searchBoxRect(int w);
// True iff `name` contains `query` as a case-insensitive ASCII substring. An empty query
// matches everything.
bool nameMatchesQuery(const std::string& name, const std::string& query);
// Narrows a list of display `names` to the indices whose name matches `query`, preserving
// order. An empty query returns every index. Kept name-only (indices, not card structs)
// so this module stays free of the sample_map/bank_book chain — the shell applies the
// bank filter first, then feeds the surviving display names here.
std::vector<int> filterNameIndices(const std::vector<std::string>& names,
const std::string& query);
// --- Browse-modal top-level regions --------------------------------------------
//
// 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. Homed here (not editor_geometry) because the search-box
// height feeds it.
struct BrowseModal {
Rect title;
Rect back;
Rect search;
Rect content; // browser sub-area (tabs + grid) — layoutBrowser's origin
Rect cancel;
Rect confirm;
};
BrowseModal computeBrowseModal(int w, int h);
} // namespace reasampler::instrument::ui