#pragma once // overflow_menu — the REAPER-free layout math behind the bank_panel TOP toolbar's "⋯ / More" // overflow-menu button (Phase L, L5, refinement 1). The rare capture variants (Batch Items / // Batch Razor / Capture RT) move OFF the always-visible top bar into a popup opened by a small // square button pinned to the FAR RIGHT of the top toolbar band. This module owns two things, // both unit-tested outside the DAW: // * WHERE the More button sits in the top toolbar band (right-anchored, vertically inset); // * the horizontal RESERVE the action_bar must leave for it, so the frequent buttons never // run under the menu button (the shell shrinks the action_bar's usable width by this). // The popup itself (TrackPopupMenu) + the command dispatch is shell — a transient OS menu, not // panel chrome (brief §1: "a REAPER/host popup menu is acceptable"). Only the button // geometry + hit-test live here. // // PURE MODULE: NO REAPER types, NO SWELL, NO LICE, NO vendor/ includes. Standard library only. // Mirror of prune_button / mode_switch. The bar rect type it consumes mirrors action_bar's // ActionBarRect shape but is named distinctly to avoid coupling the two modules. namespace reasampler { // The toolbar band the button is drawn into, top-left origin (SWELL/LICE convention). The // shell derives this from topToolbarRect(). A distinct type from action_bar::ActionBarRect so // this module stands alone (same shape; deliberate — the two modules are not coupled). struct MenuBarRect { int x = 0; int y = 0; int width = 0; int height = 0; bool operator==(const MenuBarRect& o) const { return x == o.x && y == o.y && width == o.width && height == o.height; } }; // The More button's pixel rectangle within the band, top-left origin. A zero-area rect // (width <= 0 or height <= 0) means "no button" — the band is degenerate or too narrow to // place the button clear of its left inset; the caller must not draw or hit-test it. The // three variants stay reachable via their bindable commands, so a suppressed button is // graceful, not a lost affordance. struct MenuButtonRect { int x = 0; int y = 0; int width = 0; int height = 0; bool empty() const { return width <= 0 || height <= 0; } bool operator==(const MenuButtonRect& o) const { return x == o.x && y == o.y && width == o.width && height == o.height; } }; // Layout inputs for the More button, in pixels. Defaults match the bank_panel top-toolbar // metrics; the shell passes its own so draw and hit-test share one source of truth. // * buttonWidth — the button's fixed width (a compact square-ish glyph button). // * rightInset — gap from the band's right edge to the button's right edge. // * verticalInset — top/bottom gap inside the band (shorter than the band so it reads as a // raised control, matching the action_bar buttons' verticalInset). // * minLeftInset — the button's left edge must stay at least this far from the band left // edge; if it would encroach past this, computeMenuButton yields an empty // rect (button suppressed). struct MenuButtonSpec { int buttonWidth = 28; int rightInset = 6; int verticalInset = 3; int minLeftInset = 40; }; // The horizontal reserve (px) the action_bar must leave at the band's right so its buttons // never run under the More button: the button width + both insets (right gap + a matching // left breathing gap equal to rightInset). The shell subtracts this from the action_bar rect's // width before laying out slots. Returns 0 for a degenerate band (nothing to reserve). int menuButtonReserve(const MenuBarRect& bar, const MenuButtonSpec& spec); // Computes the More button's rect within `bar` per `spec`. Right-anchored: the button's right // edge is bar.x + bar.width - rightInset, its width is buttonWidth, vertically centred by // verticalInset. Returns an EMPTY rect when: the band is degenerate (width/height <= 0), the // buttonWidth is non-positive, OR the resulting left edge would fall closer to the band left // than minLeftInset. A thin band clamps the button height to the band's own rather than going // negative (mirror of computePruneButton). MenuButtonRect computeMenuButton(const MenuBarRect& bar, const MenuButtonSpec& spec); // True iff the point (px, py) (SWELL/LICE top-left client coords) falls inside `button`. // Half-open bounds [x, x+width) x [y, y+height) — matches computeMenuButton so draw and // hit-test agree on the same pixels. An empty button never claims a point (always false). bool hitTestMenuButton(int px, int py, const MenuButtonRect& button); } // namespace reasampler