// capture_browser.cpp — see capture_browser.h. Pure math; no host types. #include "capture_browser.h" #include namespace reasampler::vst { namespace { // The left edge of tab i in a strip of the given x-origin and width divided into `count` // equal segments (mirror of mode_switch::segmentEdge). Every boundary derives from the same // formula, so consecutive tabs share an exact edge and the last tab reaches x+width exactly. int tabEdge(int x, int width, int i, int count) { return x + (i * width) / count; } } // namespace BrowserLayout layoutBrowser(int w, int h) { const int cw = std::max(0, w); const int ch = std::max(0, h); BrowserLayout out; const int tabH = std::min(kBrowserTabHeight, ch); out.tabStrip = Rect{0, 0, cw, tabH}; out.grid = Rect{0, tabH, cw, ch}; const int gridW = std::max(0, out.grid.width()); out.columns = std::max(1, gridW / kBrowserCardWidth); return out; } Rect cardCellRect(const BrowserLayout& layout, int index) { if (index < 0) return Rect{}; const int cols = std::max(1, layout.columns); const int col = index % cols; const int row = index / cols; const int left = layout.grid.left + col * kBrowserCardWidth; const int top = layout.grid.top + row * kBrowserCardHeight; return Rect{left, top, left + kBrowserCardWidth, top + kBrowserCardHeight}; } Rect cardContentRect(const BrowserLayout& layout, int index) { if (index < 0) return Rect{}; const Rect cell = cardCellRect(layout, index); return Rect{cell.left + kBrowserCardGutter, cell.top + kBrowserCardGutter, cell.right - kBrowserCardGutter, cell.bottom - kBrowserCardGutter}; } Rect cardThumbnailRect(const BrowserLayout& layout, int index) { if (index < 0) return Rect{}; const Rect content = cardContentRect(layout, index); const int thumbH = std::min(kBrowserThumbHeight, std::max(0, content.height())); return Rect{content.left, content.top, content.right, content.top + thumbH}; } Rect cardLabelRect(const BrowserLayout& layout, int index) { if (index < 0) return Rect{}; const Rect content = cardContentRect(layout, index); const Rect thumb = cardThumbnailRect(layout, index); return Rect{content.left, thumb.bottom, content.right, content.bottom}; } int cardHitTest(const BrowserLayout& layout, int cardCount, int x, int y) { if (cardCount <= 0) return -1; if (!contains(layout.grid, x, y)) return -1; const int cols = std::max(1, layout.columns); const int col = (x - layout.grid.left) / kBrowserCardWidth; const int row = (y - layout.grid.top) / kBrowserCardHeight; if (col < 0 || col >= cols) return -1; // past the last column (right dead-zone) const int index = row * cols + col; if (index < 0 || index >= cardCount) return -1; // Only a hit inside the card CONTENT counts — a click in the inter-card gutter misses. if (!contains(cardContentRect(layout, index), x, y)) return -1; return index; } Rect filterTabRect(const BrowserLayout& layout, int tabCount, int index) { if (tabCount <= 0 || index < 0 || index >= tabCount) return Rect{}; const Rect& strip = layout.tabStrip; const int left = tabEdge(strip.left, std::max(0, strip.width()), index, tabCount); const int right = tabEdge(strip.left, std::max(0, strip.width()), index + 1, tabCount); return Rect{left, strip.top, right, strip.bottom}; } int filterTabHitTest(const BrowserLayout& layout, int tabCount, int x, int y) { if (tabCount <= 0) return -1; if (!contains(layout.tabStrip, x, y)) return -1; for (int i = 0; i < tabCount; ++i) { if (contains(filterTabRect(layout, tabCount, i), x, y)) return i; } return -1; } } // namespace reasampler::vst