Cut shell/panel comment bloat ~47% (comments only, zero code change)

This commit is contained in:
2026-07-29 20:48:56 -04:00
parent 1f24c4b095
commit cc36dd59c7
15 changed files with 627 additions and 1320 deletions
+56 -167
View File
@@ -1,18 +1,11 @@
// panel_layout.cpp — the geometry-glue seam of the docked bank panel (Q-W2 split of
// bank_panel.cpp; the T4-01 NEW seam). Owns the toolbar/footer/menu rects, the
// toolbar row/cluster builders, the vertical-split geometry + region rects, and the
// L7 slot-order display bridge (regionDisplay/focusedDisplay). Every rect is derived
// from the client size + fullHeight state, and BOTH paint (panel_render) and
// hit-testing (panel_input / panel_drag) call these so they never drift.
// panel_layout.cpp — geometry-glue seam of the docked bank panel: toolbar/footer/menu
// rects, toolbar row/cluster builders, vertical-split geometry + region rects, and
// the slot-order display bridge. Every rect is derived from client size + fullHeight
// state, and both paint and hit-testing call these so they never drift. Also home of
// the public split-state seam (panel_layout.h).
//
// Also home of the public split-state seam (panel_layout.h): the B3-owned
// BankPanelFullHeight toggles the render derives the region rects from.
//
// Compiled into the reaper_reasampler MODULE. Includes reaper_plugin_functions.h
// WITHOUT REAPERAPI_IMPLEMENT — main.cpp owns the API pointers; here they are
// extern (CLAUDE.md §contract). DAW-verified, not unit tested (the PURE tiling /
// hit-test math lives in action_bar / footer_bar / prune_button / overflow_menu /
// tab_strip / mode_switch / bank_grid / card_drag, unit-tested outside the DAW).
// main.cpp owns the API pointers; here they are extern. DAW-verified, not unit tested
// (the pure tiling/hit-test math lives in action_bar / footer_bar / etc.).
#include <string>
#include <vector>
@@ -20,12 +13,11 @@
#include "shell/panel/panel_state.h"
#include "shell/panel/panel_layout.h"
#include "shell/persist/session.h" // ReaSamplerSession — mode/view reads
#include "core/view/view_mode_model.h" // ViewModeModel — modes()/activeModeId()
#include "shell/persist/session.h"
#include "core/view/view_mode_model.h"
// Action-trigger buttons (M11): resolve each button's command id at runtime from the
// composed named-command string and read its current key binding for the tooltip.
// All main-section (SectionFromUniqueID(0)).
// Resolves each button's command id at runtime from the composed named-command
// string, and reads its current key binding for the tooltip. Main-section only.
#define REAPERAPI_MINIMAL
#define REAPERAPI_WANT_NamedCommandLookup
#define REAPERAPI_WANT_kbd_getTextFromCmd
@@ -34,20 +26,14 @@
namespace reasampler::panel {
// --- Mode toggle (D5; relocated to the footer at L4) --------------------------
int modeCount() {
if (!g_panel.session) return 0;
return static_cast<int>(g_panel.session->view().modes().size());
}
// --- Top toolbar band (L4; L5 overflow-menu reserve) --------------------------
//
// The TOP toolbar (capture + placement) occupies the very top of the client. Degenerate
// (height 0) when the client is too short to host it above the split body. The WHOLE band
// (topToolbarRect) is what the far-right More button anchors into; the action_bar's frequent
// buttons tile into the band MINUS the menu reserve (topToolbarActionRect), so they never run
// under the menu button (L5 refinement 1).
// topToolbarRect is what the far-right More button anchors into; the action_bar
// buttons tile into the band minus the menu reserve (topToolbarActionRect) so
// they never run under it.
namespace {
@@ -60,25 +46,21 @@ ActionBarRect topToolbarRect(int w) {
return s;
}
// The band the More button occupies (the whole top toolbar band as a MenuBarRect).
MenuBarRect topMenuBarRect(int w) {
const ActionBarRect bar = topToolbarRect(w);
return MenuBarRect{bar.x, bar.y, bar.width, bar.height};
}
// The More button's rect (right-anchored in the top band). Empty when the band is too narrow
// to place it clear of its left inset — the three variants stay reachable via their bindable
// commands (graceful suppression).
} // namespace
// Empty when the band is too narrow to place the button clear of its left
// inset — suppressed gracefully; still reachable via its bindable command.
MenuButtonRect topMenuButtonRect(int w) {
return computeMenuButton(topMenuBarRect(w), kMenuBtnSpec);
}
// The rect the TOP toolbar's action_bar tiles into: the whole band MINUS the reserve for the
// far-right More button, so the frequent buttons never overlap it. When the More button is
// suppressed (band too narrow) the reserve is still subtracted (the reserve is 0 only for a
// degenerate band), which keeps draw and hit-test consistent whether or not the button shows.
// Reserve is still subtracted even when the button is suppressed (0 only for a
// degenerate band), keeping draw and hit-test consistent either way.
ActionBarRect topToolbarActionRect(int w) {
ActionBarRect bar = topToolbarRect(w);
const int reserve = menuButtonReserve(topMenuBarRect(w), kMenuBtnSpec);
@@ -87,8 +69,6 @@ ActionBarRect topToolbarActionRect(int w) {
return bar;
}
// --- Footer (L4) --------------------------------------------------------------
RECT panelFooter(int w, int h) {
RECT rc{};
rc.left = 0;
@@ -100,8 +80,8 @@ RECT panelFooter(int w, int h) {
return rc;
}
// The footer LEFT-group layout (mode toggle + count + Tail button), derived from the client
// size. SINGLE source of truth for draw and hit-test. All-empty when the footer is degenerate.
// Left-group layout (mode toggle + count + Tail button). Single source of truth
// for draw and hit-test.
FooterBarLayout footerBarLayoutFor(int w, int h) {
const RECT f = panelFooter(w, h);
if (f.top >= f.bottom) return FooterBarLayout{};
@@ -109,22 +89,18 @@ FooterBarLayout footerBarLayoutFor(int w, int h) {
return computeFooterBar(footer, FooterBarSpec{});
}
// The prune button's rect within the footer, derived from the client size. SINGLE source
// of truth for both draw and hit-test (they never drift). Empty when the footer is degenerate
// or too narrow to place the button clear of the footer-left group / version readout — the
// action stays reachable via its bindable command, so a suppressed button is graceful. Kept
// set apart at the RIGHT (footer_bar reserves the matching space at its right so the two
// groups never overlap). See prune_button.h §Placement contract.
// Empty when the footer is degenerate or too narrow to clear the left group /
// version readout — reachable via its bindable command regardless. Set apart at
// the right (footer_bar reserves matching space so the groups never overlap).
ButtonRect pruneButtonRectFor(int w, int h) {
const RECT f = panelFooter(w, h);
if (f.top >= f.bottom) return ButtonRect{}; // degenerate footer -> no button
if (f.top >= f.bottom) return ButtonRect{};
const FooterRect footer{f.left, f.top, f.right - f.left, f.bottom - f.top};
return computePruneButton(footer, PruneButtonSpec{});
}
// True iff client-relative (x, y) falls inside the (non-degenerate) footer strip. Used by the
// scroll-wheel (Manual tail fine-adjust) so a wheel notch over the footer is claimed. The
// Tail-cycle CLICK no longer uses this — it now hits the Tail button rect (footer_bar).
// Used by the scroll-wheel (Manual tail fine-adjust); the Tail-cycle click hits
// the Tail button rect (footer_bar) instead.
bool pointInFooter(int x, int y) {
if (!g_panel.hwnd) return false;
RECT cr{};
@@ -133,37 +109,11 @@ bool pointInFooter(int x, int y) {
return f.top < f.bottom && x >= f.left && x < f.right && y >= f.top && y < f.bottom;
}
// === Task-grouped toolbars (Phase L, L2 + L4) =================================
//
// L4 re-homes the button inventory around frequency and intent (DS-3 layout, not a re-skin)
// across TWO toolbars, BOTH drawn through the pure action_bar module:
// * the TOP toolbar (Capture + Placement) sits at the very top where the eye lands — the
// two acts the tool exists for (L4 §1);
// * the BOTTOM toolbar (the Design-View verbs: Tagging then Switching) sits above the
// footer, in the space capture/placement vacated (L4 §2).
// Each button is drawn with its action name (Font::Label) and live key binding on a Micro
// sub-row (the L2 contract). action_bar owns the cluster tiling, the label/binding sub-rects,
// the whole-trailing-button overflow, and the hit-test; only the kit draw + SDK binding query
// + the NamedCommandLookup/Main_OnCommand dispatch live here.
//
// Each button resolves its command id at RUNTIME from the composed named-command string
// (NamedCommandLookup on "_" + channelCommandId(suffix)), so it is channel-correct on stable
// and beta and adds NO second registration. A cmd of 0 (action not registered on this channel)
// draws Disabled and no-ops on click. L4 is layout-only: the SAME existing actions fire via the
// SAME contract — no re-wiring, no command-id changes, and capture never auto-inserts.
// The TOP toolbar inventory (L6 refinement): the FREQUENT acts only — Capture (item / track)
// then Re-capture (Maintenance, set between the two capture verbs and the placement verbs) then
// Placement (insert / insert-conform). The FOUR RARE variants (Batch Items / Batch Razor /
// Capture RT / Cancel RT) are ALL in the far-right "⋯" overflow menu (overflowMenuRows) —
// same registered actions, same command-id contract, just a different home. Capture scopes come
// from captureActionTable() (render_settings, pure); the rest are the registered M11/M10/M8
// commands. Built once per draw/click. Each row carries its full (prefix-stripped) action name
// for the hover tooltip.
// Frequent acts only: Capture (item/track), Re-capture (between capture and
// placement), Placement (insert/insert-conform). The four rare variants live in
// the overflow menu (overflowMenuRows) — same actions, different home.
std::vector<ActionBarRow> topBarRows() {
std::vector<ActionBarRow> rows;
// Capture cluster — the primary gesture, leftmost. Face is a terse "Capture Item/Track";
// the tooltip carries the full descriptionPhrase the action was registered with.
for (const CaptureActionDef& def : captureActionTable()) {
std::string label = def.commandSuffix;
if (label == "CAPTURE_ITEM") label = "Capture Item";
@@ -171,12 +121,8 @@ std::vector<ActionBarRow> topBarRows() {
rows.push_back({def.commandSuffix, label, def.descriptionPhrase,
ActionCluster::Capture, true});
}
// Maintenance cluster — Re-capture from source (M10), placed BETWEEN the capture group and
// the placement group so its position reads "refine the last capture before placing it".
// Cancel RT lives in the overflow menu (both realtime verbs share that home — L6).
rows.push_back({"RECAPTURE_FROM_SOURCE", "Re-capture",
"re-capture from source", ActionCluster::Maintenance, true});
// Placement cluster — the second act (still a distinct on-demand act; no auto-insert).
rows.push_back({"INSERT_SELECTED", "Insert",
"insert selected sample at edit cursor", ActionCluster::Placement, true});
rows.push_back({"INSERT_SELECTED_CONFORM", "Insert Conform",
@@ -185,12 +131,8 @@ std::vector<ActionBarRow> topBarRows() {
return rows;
}
// The TOP-toolbar OVERFLOW menu inventory (L6): four items pulled off the visible bar into the
// far-right "⋯" menu button's popup — the three rare batch/realtime capture variants plus
// Cancel RT (both realtime verbs share the menu home). Each fires the SAME existing registered
// command id via the SAME NamedCommandLookup/Main_OnCommand contract — no action changes. The
// fullName is the popup entry text (the terse shortLabel is unused for menu items; the popup has
// room for the full name). Batch entries first, then the two realtime verbs.
// The four rare batch/realtime capture variants pulled off the visible bar, plus
// Cancel RT. fullName is the popup entry text (shortLabel is unused for menu items).
std::vector<ActionBarRow> overflowMenuRows() {
return {
{"CAPTURE_BATCH_ITEMS", "Batch Items",
@@ -206,8 +148,6 @@ std::vector<ActionBarRow> overflowMenuRows() {
namespace {
// The active mode id the opposite-mode gate + footer toggle both read (ONE source of truth for
// "which mode is active"). Empty when no session (every button then falls to fail-open live).
std::string activeModeIdOrEmpty() {
if (!g_panel.session) return {};
return g_panel.session->view().activeModeId();
@@ -215,26 +155,16 @@ std::string activeModeIdOrEmpty() {
} // namespace
// The BOTTOM toolbar inventory (L5 refinement 3): FOUR Item/Track x Arrange/Design tag buttons
// then a set-apart Show Both. The suffixes are the ACTUAL registered command-id strings from
// design_view_actions.cpp (VIEW_MOVE_ITEMS_ARRANGE / VIEW_MOVE_ITEMS_DESIGN for the item moves;
// VIEW_TAG_ARRANGE / VIEW_TAG_DESIGN for the track tags; VIEW_SHOW_BOTH) — grepped, not
// paraphrased. "…: Arrange" routes through the untag/arrange path (Arrange = absence of a tag).
// The Toggle + both Activate buttons are REMOVED (L5 refinement 4 / settled inventory): the
// footer [Arrange|Design] toggle owns mode switching.
//
// OPPOSITE-MODE ENABLEMENT (L5): a tag button is LIVE only for the OPPOSITE of the active mode
// (you tag into the mode you are not in). The pure mode_enable::tagButtonEnabled decides it from
// the active mode id; Show Both is unconditional (not a tag target). enabled=false rows draw
// Disabled and no-op on click. The Item/Track axis is display-only here — both the Item and the
// Track button for a target share the target's enablement.
// Four Item/Track x Arrange/Design tag buttons then a set-apart Show Both. A tag
// button is live only for the OPPOSITE of the active mode (tag into the mode
// you're not in) — decided by the pure mode_enable::tagButtonEnabled; disabled
// rows draw Disabled and no-op on click. Show Both is unconditional.
std::vector<ActionBarRow> bottomBarRows() {
const std::string active = activeModeIdOrEmpty();
const bool arrangeLive = tagButtonEnabled(active, TagTarget::Arrange);
const bool designLive = tagButtonEnabled(active, TagTarget::Design);
std::vector<ActionBarRow> rows;
// Tagging cluster — the four Item/Track x Arrange/Design tag buttons.
rows.push_back({"VIEW_MOVE_ITEMS_ARRANGE", "Item: Arrange",
"move selected items -> Arrange", ActionCluster::Tagging, arrangeLive});
rows.push_back({"VIEW_MOVE_ITEMS_DESIGN", "Item: Design",
@@ -243,18 +173,14 @@ std::vector<ActionBarRow> bottomBarRows() {
"tag selected tracks -> Arrange", ActionCluster::Tagging, arrangeLive});
rows.push_back({"VIEW_TAG_DESIGN", "Track: Design",
"tag selected tracks -> Design", ActionCluster::Tagging, designLive});
// Switching cluster — Show Both, set apart (the only survivor of the old switching group).
rows.push_back({"VIEW_SHOW_BOTH", "Show Both",
"show both for selected tracks", ActionCluster::Switching, true});
return rows;
}
// The cluster button-count specs for a given row set, in the row list's cluster order (so the
// pure action_bar's flat index lines up with the row list). Handles all five cluster kinds;
// empty clusters contribute a 0-count spec (action_bar skips them, emitting no gap). The spec
// order follows each toolbar's fixed layout order (top: Capture, Maintenance, Placement —
// Re-capture sits between the two capture verbs and the placement verbs; bottom: Tagging,
// Switching). The bottom bar's Maintenance count is 0, so the order change is transparent there.
// Cluster button-count specs in the row list's cluster order, so action_bar's
// flat index lines up with the row list. Empty clusters contribute a 0-count
// spec (action_bar skips them, no gap).
std::vector<ClusterSpec> actionBarClusters(const std::vector<ActionBarRow>& rows) {
int nCap = 0, nPlace = 0, nMaint = 0, nTag = 0, nSwitch = 0;
for (const ActionBarRow& r : rows) {
@@ -275,8 +201,8 @@ std::vector<ClusterSpec> actionBarClusters(const std::vector<ActionBarRow>& rows
};
}
// The BOTTOM toolbar band: a fixed-height band directly above the footer (below the split
// body). Degenerate (height 0) when the client is too short to host it above the footer.
// Fixed-height band directly above the footer; degenerate (height 0) when too
// short to host it above the footer.
ActionBarRect bottomToolbarRect(int w, int h) {
ActionBarRect s;
const RECT footer = panelFooter(w, h);
@@ -285,12 +211,10 @@ ActionBarRect bottomToolbarRect(int w, int h) {
s.width = w;
s.height = kBottomToolbarHeight;
s.y = footerTop - kBottomToolbarHeight;
// Keep the bar below the top toolbar; if the client is too short, collapse it.
if (s.y < kTopToolbarHeight) { s.y = footerTop; s.height = 0; }
return s;
}
// Resolves a row's composed named command to its runtime command id (0 if not registered).
// The named-command lookup string is "_" + the channel-qualified id (REAPER's convention).
int resolveBarCommandId(const ActionBarRow& row) {
if (!NamedCommandLookup) return 0;
@@ -300,8 +224,7 @@ int resolveBarCommandId(const ActionBarRow& row) {
namespace {
// The current key binding string for a command in the MAIN section, or "" (unbound / not
// registered). Queried via kbd_getTextFromCmd (SectionFromUniqueID(0)).
// "" when unbound or not registered.
std::string barBindingText(int cmd) {
if (cmd != 0 && kbd_getTextFromCmd && SectionFromUniqueID) {
const char* t = kbd_getTextFromCmd(cmd, SectionFromUniqueID(0));
@@ -318,16 +241,9 @@ int toolbarHit(int x, int y, const ActionBarRect& bar, const std::vector<ActionB
return hitTestActionBar(x, y, bar, actionBarClusters(rows), kBarSpec);
}
// --- Tooltip (L5 refinement 2) ------------------------------------------------
//
// A custom hover-delay tooltip: the full, prefix-stripped action name of the hovered toolbar
// button. Resolves the hovered element to its (anchor rect, text); returns false when the current
// hover has no tooltip (grid / chrome / the More button — the More button's own popup is its
// affordance). The tooltip DRAW lives in panel_render (drawTooltip); timing
// (kTooltipDelayMs) is applied by the caller.
// The full (prefix-stripped) tooltip text for the currently hovered toolbar button, plus its
// anchor rect. Returns false when the hover is not a tooltip-bearing toolbar button.
// Resolves the hovered element to (anchor rect, text); false when the hover has
// no tooltip (grid / chrome / More button — its own popup is its affordance).
// Draw lives in panel_render; timing is applied by the caller.
bool currentTooltip(int w, int h, std::string& textOut, int& ax, int& ay, int& aw, int& ah) {
const Hover& hv = g_panel.hovered;
std::vector<ActionBarRow> rows;
@@ -343,8 +259,8 @@ bool currentTooltip(int w, int h, std::string& textOut, int& ax, int& ay, int& a
}
if (hv.index < 0 || hv.index >= static_cast<int>(rows.size())) return false;
// The hovered button's slot rect (the anchor). computeBarSlots is the same layout the draw +
// hit-test use, so the anchor matches the drawn button exactly.
// computeBarSlots is the same layout draw + hit-test use, so the anchor
// matches the drawn button exactly.
const std::vector<ClusterSpec> clusters = actionBarClusters(rows);
const std::vector<ActionBarSlot> slots = computeBarSlots(bar, clusters, kBarSpec);
const ActionBarSlot* slot = nullptr;
@@ -352,11 +268,9 @@ bool currentTooltip(int w, int h, std::string& textOut, int& ax, int& ay, int& a
if (s.index == hv.index) { slot = &s; break; }
if (!slot) return false;
// The full name is stored already prefix-free, but strip defensively in case a source ever
// carries the "ReaSampler:" display prefix (the tooltip must never show it — L5 refinement 2).
// L6: the keybinding sub-row was removed from the button face, so the tooltip now carries
// both the name AND the binding (when bound) — e.g. "capture selected item — F5". When the
// action is unbound the tooltip shows only the name (no "(unbound)" noise in the tooltip).
// fullName is stored prefix-free; strip defensively in case a source ever
// carries the display prefix. Tooltip carries name + binding when bound
// (e.g. "capture selected item — F5"), name only when unbound.
const std::string phrase = stripActionPrefix(rows[static_cast<std::size_t>(hv.index)].fullName,
actionDisplayPrefix());
const int cmd = resolveBarCommandId(rows[static_cast<std::size_t>(hv.index)]);
@@ -366,14 +280,9 @@ bool currentTooltip(int w, int h, std::string& textOut, int& ax, int& ay, int& a
return true;
}
// --- Split geometry -----------------------------------------------------------
//
// Every rect below is derived from the client size + fullHeight state, and BOTH paint
// and hit-testing call these so they never drift. All are top-left origin.
// The body band between the TOP toolbar and the BOTTOM toolbar (L4). Its top edge is below the
// top toolbar; its bottom edge is the bottom toolbar's top. When the bottom bar collapses on a
// short client, bottomToolbarRect returns its y at the footer top, so the body still ends there.
// Between the top and bottom toolbars. When the bottom bar collapses on a short
// client, bottomToolbarRect returns its y at the footer top, so the body still
// ends there.
RECT splitBody(int w, int h) {
RECT rc{};
rc.left = 0;
@@ -385,24 +294,20 @@ RECT splitBody(int w, int h) {
return rc;
}
// True when both regions are shown (the split is live). Otherwise one region fills
// the body.
bool poolShown() { return g_panel.fullHeight != BankPanelFullHeight::BanksOnly; }
bool banksShown() { return g_panel.fullHeight != BankPanelFullHeight::PoolOnly; }
// The pool region's rect (whole-region: header band + grid). Empty when hidden.
// Empty when hidden.
RECT poolRegionRect(int w, int h) {
const RECT body = splitBody(w, h);
if (!poolShown()) return RECT{0, 0, 0, 0};
if (!banksShown()) return body; // pool full-height: the whole body
// Split: pool gets the top half (minus the divider).
if (!banksShown()) return body;
RECT rc = body;
rc.bottom = body.top + (body.bottom - body.top - kSplitDividerHeight) / 2;
if (rc.bottom < rc.top) rc.bottom = rc.top;
return rc;
}
// The named-banks region's rect (whole-region: header band + tab strip + grid).
RECT banksRegionRect(int w, int h) {
const RECT body = splitBody(w, h);
if (!banksShown()) return RECT{0, 0, 0, 0};
@@ -414,7 +319,6 @@ RECT banksRegionRect(int w, int h) {
return rc;
}
// A region's header band (the top kRegionHeaderHeight of the region).
RECT regionHeaderRect(const RECT& region) {
RECT rc = region;
rc.bottom = region.top + kRegionHeaderHeight;
@@ -422,7 +326,6 @@ RECT regionHeaderRect(const RECT& region) {
return rc;
}
// The named-banks region's tab strip (below its header band).
TabStripRect banksTabStripRect(const RECT& region) {
const RECT hdr = regionHeaderRect(region);
TabStripRect s;
@@ -445,7 +348,6 @@ RECT regionGridRect(const RECT& region, bool isBanks) {
return rc;
}
// The full-height toggle button rect inside a region header (right-aligned).
RECT fullHtBtnRect(const RECT& region) {
const RECT hdr = regionHeaderRect(region);
RECT rc = hdr;
@@ -456,8 +358,6 @@ RECT fullHtBtnRect(const RECT& region) {
return rc;
}
// The "+" create-bank button rect inside the named-banks region header (left of the
// full-height button).
RECT createBtnRect(const RECT& region) {
RECT ft = fullHtBtnRect(region);
RECT rc = ft;
@@ -466,10 +366,8 @@ RECT createBtnRect(const RECT& region) {
return rc;
}
// Resolves a region's display for the currently-shown bank. Empty (no bank / no width)
// yields an empty display. orderedSampleIds reconciles the bank's SlotMap against live
// membership, so a freshly-migrated or out-of-band-mutated bank always yields a complete
// order (trailing empties are trimmed by the model — maxSlot walks only live occupants).
// orderedSampleIds reconciles the bank's SlotMap against live membership, so a
// freshly-migrated or out-of-band-mutated bank always yields a complete order.
RegionDisplay regionDisplay(const RECT& region, bool isBanks, Region reg) {
RegionDisplay d;
BankBook* b = book();
@@ -490,8 +388,6 @@ RegionDisplay regionDisplay(const RECT& region, bool isBanks, Region reg) {
return d;
}
// The FOCUSED region's display (the slot-order bridge for the region holding the live
// selection). Mirrors columnsForRegion's client read.
RegionDisplay focusedDisplay() {
RECT cr{};
GetClientRect(g_panel.hwnd, &cr);
@@ -501,8 +397,6 @@ RegionDisplay focusedDisplay() {
return regionDisplay(region, isBanks, g_panel.focusedRegion);
}
// Which region (if any) contains client point (x, y); returns false via `out` set to
// Pool by default when the point is in neither region body.
bool regionAt(int x, int y, Region& out) {
RECT cr{};
GetClientRect(g_panel.hwnd, &cr);
@@ -522,8 +416,6 @@ bool regionAt(int x, int y, Region& out) {
return false;
}
// The footer mode-toggle segment (Arrange|Design) under (x, y), or -1. Segments are tiled by
// mode_switch inside footer_bar's toggle box, so both draw and hit-test use the same box.
int footerToggleSegmentHit(int x, int y, int w, int h) {
if (!g_panel.session) return -1;
const FooterBarLayout fb = footerBarLayoutFor(w, h);
@@ -532,7 +424,6 @@ int footerToggleSegmentHit(int x, int y, int w, int h) {
return hitTestSegment(x, y, th, modeCount());
}
// The column count for a region's current grid width (nav needs the layout's wrap).
int columnsForRegion(Region reg) {
RECT cr{};
GetClientRect(g_panel.hwnd, &cr);
@@ -545,8 +436,6 @@ int columnsForRegion(Region reg) {
} // namespace reasampler::panel
// --- Public API (the split-state seam — panel_layout.h) ------------------------
namespace reasampler {
BankPanelFullHeight bankPanelFullHeight() {