#pragma once #include "core/ui/rect.h" // footer_bar — layout + hit-test for the bank_panel footer's LEFT group: the [Arrange|Design] // mode toggle, its compact count label, and the Tail button, left-to-right at the footer's left. // // Affordance order, left -> right: // [Arrange|Design] toggle . count label . Tail button . ... . Prune (rightmost, warn) // The view/session controls group at the left; Prune stays isolated at the far right, warn- // colored (the only byte-deleting affordance) and owned separately by prune_button — footer_bar // reserves a right margin (rightReserve) so its own affordances never run under it. // // The toggle here is only the overall BOX; the shell hands its width to mode_switch // (computeSegmentRects / hitTestSegment) for per-segment tiling — mode_switch stays the one // owner of segment geometry. #include "core/ui/prune_button.h" // FooterRect — the shared footer strip input type namespace reasampler::ui { // One placed affordance's rect, top-left origin. empty() means "not placed" (footer too narrow // after earlier affordances claimed their space) — shell draws/hit-tests nothing for it. using FooterBarRect = Rect; // The laid-out footer LEFT group. Any box may be empty when the footer is too narrow to fit it // left of the reserved right margin; placement is greedy left-to-right (toggle survives longest, // Tail drops first on a very narrow footer). struct FooterBarLayout { FooterBarRect toggle; FooterBarRect count; FooterBarRect tail; bool operator==(const FooterBarLayout& o) const { return toggle == o.toggle && count == o.count && tail == o.tail; } }; // Which footer LEFT-group affordance a point landed on. Prune is hit-tested separately via // hitTestPruneButton. enum class FooterHit { None, Toggle, Tail }; // Layout inputs, in pixels; defaults are the bank_panel footer metrics. // * rightReserve — pixels reserved at the footer's right for the prune button + version // readout; footer_bar never places an affordance whose right edge would cross into it. struct FooterBarSpec { int toggleWidth = 132; int countWidth = 64; int tailWidth = 132; int gap = 6; int leftPad = 8; int verticalInset = 4; int rightReserve = 168; // clears prune_button (rightInset 84 + width 72) + margin }; // Lays out the footer LEFT group inside `footer` per `spec`: toggle, count label, Tail button, // each `gap` px apart from footer.left + leftPad. Greedy — an affordance places only if it fits // left of (footer.right - rightReserve); once one doesn't fit, the rest are suppressed too. // countWidth <= 0 suppresses the count label without leaving a gap for the Tail button. FooterBarLayout computeFooterBar(const FooterRect& footer, const FooterBarSpec& spec); // The affordance (px, py) lands on, or FooterHit::None for a miss (or a hit on the count label, // a passive readout, never a control). Half-open bounds match computeFooterBar. FooterHit hitTestFooterBar(int px, int py, const FooterBarLayout& layout); // Whether one [Arrange|Design] segment is live. A mode switch is refused while the project // plays or records (shell/view::transportBlocksModeSwitch), so an unreachable segment must READ // dead before the click, not merely refuse on it. The ACTIVE segment stays live regardless: it // fires a reapply, which is never gated, and dimming the mode you are already in would read as // "this mode is unavailable" rather than "you cannot leave it right now". `routable` is whether // the shell resolved a live command id for this segment (the model is N-mode, the UI ships two // seeded ids — a third registered mode has no action to route through, so its segment must read // dead rather than paint live and silently no-op on click). Passed IN so this predicate — and // `core/ui` — stays REAPER-free, exactly as `transportRunning` is. bool modeSegmentEnabled(bool isActiveSegment, bool transportRunning, bool routable); } // namespace reasampler::ui