#pragma once // mode_switch — the REAPER-free layout math behind the bank_panel's Design-View // mode switch (Phase D, Wave 4 — D5). A segmented control `[ Arrange | Design ]` // (N-mode general, one segment per registered mode) drawn in a fixed-height header // strip at the top of the docked panel. The panel shell (bank_panel.cpp) owns the // SWELL window, LICE drawing, and the live ViewModeModel read + mode activation — // all REAPER-bound, DAW-verified. What is NOT DAW-bound — how N segments tile a // header rectangle, and which segment a click lands in — lives here so it is // unit-tested outside the DAW (CLAUDE.md §load-bearing split). Mirror of bank_grid. // // PURE MODULE: NO REAPER types, NO SWELL, NO vendor/ includes. Standard library // only. Builds and unit-tests without REAPER. #include namespace reasampler { // The header strip the switch is drawn into, top-left origin (SWELL/LICE // convention). (x, y) is the top-left corner; width/height are the strip extents. // The panel reserves this at the top of its client area and offsets the grid below. struct HeaderRect { int x = 0; int y = 0; int width = 0; int height = 0; bool operator==(const HeaderRect& o) const { return x == o.x && y == o.y && width == o.width && height == o.height; } }; // One segment's pixel rectangle within the header, top-left origin. These are the // draw bounds for one mode's button; the panel draws the mode's display name inside // it and lights it when it is the active mode. struct SegmentRect { int x = 0; int y = 0; int width = 0; int height = 0; bool operator==(const SegmentRect& o) const { return x == o.x && y == o.y && width == o.width && height == o.height; } }; // Divides `header` into `segmentCount` equal segments left-to-right, in the caller's // order (the panel passes modes in ordinal order). Returns exactly segmentCount // rects. The division tiles the header EXACTLY: each segment's left edge is // header.x + (i * width) / segmentCount, so integer rounding is absorbed at the // boundaries — segments abut with no gap and no overlap, and the last segment // reaches header.x + header.width precisely (individual widths may differ by one // pixel when width does not divide evenly). Each segment inherits the header's full // y/height. segmentCount <= 0 or a non-positive header width returns empty. std::vector computeSegmentRects(const HeaderRect& header, int segmentCount); // Hit-tests a point (SWELL/LICE top-left client coords) against the segmented // control laid out in `header` with `segmentCount` segments. Returns the index of // the segment containing the point, or -1 for a miss: a point outside the header // bounds entirely (including below it, where the grid lives), or when segmentCount // <= 0. Half-open bounds [x, x+width) x [y, y+height) match computeSegmentRects, so // adjacent segments never both claim a pixel and the point maps to the same segment // the panel drew there. int hitTestSegment(int px, int py, const HeaderRect& header, int segmentCount); } // namespace reasampler