L4: re-home dock-panel buttons into three zones
Top toolbar = capture+placement, bottom toolbar = Design-View verbs, footer = narrow Arrange|Design toggle + count + Tail button + set-apart Prune. New pure footer_bar module; Tail is now a real kit button.
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
// footer_bar — the REAPER-free, LICE-free layout + hit-test math for the bank_panel's L4
|
||||
// footer LEFT group: the narrowed [Arrange|Design] mode toggle, its compact per-mode count
|
||||
// label, and the Tail button, laid out left-to-right at the footer's left. The panel shell
|
||||
// (bank_panel.cpp) owns the SWELL window, LICE drawing, and the click dispatch (cycle tail /
|
||||
// activate a mode); what is NOT DAW-bound — WHERE the toggle box, the count label, and the
|
||||
// Tail button sit, and which one a click lands on — lives here so it is unit-tested outside
|
||||
// the DAW (CLAUDE.md §load-bearing split). Mirror of action_bar / mode_switch / prune_button.
|
||||
//
|
||||
// -- Footer affordance order (L4, left -> right) -------------------------------
|
||||
//
|
||||
// [Arrange|Design] toggle . count label . Tail button . ... . Prune (rightmost, warn)
|
||||
//
|
||||
// The two view/session controls (mode toggle, tail) group at the LEFT as the "how this
|
||||
// panel/capture behaves" cluster; Prune stays isolated at the far RIGHT, warn-colored and
|
||||
// set apart (it is the only byte-deleting affordance). This module lays out the LEFT group
|
||||
// ONLY — the rightmost Prune button remains owned by prune_button (computePruneButton), so
|
||||
// the two never fight over the same pixels. footer_bar reserves a right margin (rightReserve)
|
||||
// so its own affordances never run under the prune button's region.
|
||||
//
|
||||
// The mode toggle is drawn as an N-segment control (2 segments for Arrange|Design; N general).
|
||||
// footer_bar returns only the toggle's BOX (fit to its text width); the shell hands that box's
|
||||
// width to the pure mode_switch (computeSegmentRects / hitTestSegment) for the per-segment
|
||||
// tiling and hit-test, so mode_switch stays the ONE owner of segment geometry. footer_bar
|
||||
// decides the toggle's placement + overall width; mode_switch subdivides it.
|
||||
//
|
||||
// NAME NOTE (brief §name-collision): ButtonRect / FooterRect / SegmentRect / ActionBarRect /
|
||||
// KitBox / KitButtonBox are already owned in this namespace; grep-checked FooterBar* / FooterHit
|
||||
// FREE before minting. FooterRect (prune_button) is the input strip type and is REUSED here
|
||||
// (same concept — the footer strip); the new output/spec/hit types carry the FooterBar* prefix.
|
||||
//
|
||||
// PURE MODULE: NO REAPER types, NO SWELL, NO LICE, NO vendor/ includes. Standard library only.
|
||||
|
||||
#include "prune_button.h" // FooterRect — the footer strip input type (shared, not re-minted)
|
||||
|
||||
namespace reasampler {
|
||||
|
||||
// One placed affordance's pixel rectangle within the footer, top-left origin. A zero-area rect
|
||||
// (empty()) means "not placed" (the footer was too narrow to host it after the ones before it),
|
||||
// so the shell draws/hit-tests nothing for it — graceful degradation, mirroring prune_button.
|
||||
struct FooterBarRect {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
bool empty() const { return width <= 0 || height <= 0; }
|
||||
|
||||
bool operator==(const FooterBarRect& o) const {
|
||||
return x == o.x && y == o.y && width == o.width && height == o.height;
|
||||
}
|
||||
};
|
||||
|
||||
// The laid-out footer LEFT group: the mode toggle box, the count label box, and the Tail
|
||||
// button box, in left-to-right order. Any box may be empty (suppressed) when the footer is
|
||||
// too narrow to fit it left of the reserved right margin — placement is greedy left-to-right,
|
||||
// so an earlier affordance survives while a later one drops (the toggle is most important,
|
||||
// the Tail button drops first on a very narrow footer).
|
||||
struct FooterBarLayout {
|
||||
FooterBarRect toggle; // the [Arrange|Design] segmented control's overall box
|
||||
FooterBarRect count; // the compact per-mode count label (right of the toggle)
|
||||
FooterBarRect tail; // the Tail button (right of the count label)
|
||||
|
||||
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 (or None for a miss / a suppressed
|
||||
// affordance). Prune is NOT here — the shell hit-tests it separately via hitTestPruneButton.
|
||||
enum class FooterHit { None, Toggle, Tail };
|
||||
|
||||
// Layout inputs for the footer LEFT group, in pixels. Defaults are the bank_panel footer
|
||||
// metrics; the shell passes its own so draw and hit-test share ONE source of truth.
|
||||
// * toggleWidth — the [Arrange|Design] toggle's overall width. Sized to fit its two
|
||||
// segment labels comfortably (a NARROW control, per L4 §3 — no longer the
|
||||
// full-width top header). The shell picks this to fit its text; the pure
|
||||
// module treats it as a fixed input.
|
||||
// * countWidth — the compact per-mode count label's width (e.g. "2 tracks"). 0 hides it.
|
||||
// * tailWidth — the Tail button's width (fits "Tail: Manual 8.0s" comfortably).
|
||||
// * gap — horizontal gap between adjacent affordances.
|
||||
// * leftPad — inset from the footer left edge to the toggle's left edge.
|
||||
// * verticalInset — top/bottom gap inside the footer so the controls read as raised, not
|
||||
// full-height fills (matches prune_button's verticalInset).
|
||||
// * rightReserve — pixels reserved at the footer's RIGHT for the prune button + version
|
||||
// readout region; footer_bar never places an affordance whose right edge
|
||||
// would cross into (footer.right - rightReserve). Keeps the LEFT group
|
||||
// clear of the RIGHT prune/version region without those modules coupling.
|
||||
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`, left-to-right: toggle, then the
|
||||
// count label, then the Tail button, each `gap` px apart, starting at footer.left + leftPad,
|
||||
// vertically centred by verticalInset. Greedy: an affordance is placed only if its whole box
|
||||
// fits left of (footer.right - rightReserve); otherwise it (and, since placement is ordered,
|
||||
// it alone or the ones after it) is suppressed (empty box). A degenerate footer (width/height
|
||||
// <= 0) yields an all-empty layout. countWidth <= 0 suppresses the count label (and the gap
|
||||
// that would precede the Tail button collapses so the Tail sits right after the toggle).
|
||||
FooterBarLayout computeFooterBar(const FooterRect& footer, const FooterBarSpec& spec);
|
||||
|
||||
// The footer LEFT-group affordance the point (px, py) (SWELL/LICE top-left client coords) lands
|
||||
// on, or FooterHit::None for a miss (outside every placed box, or on the count label — which is
|
||||
// a passive readout, not a control). Half-open bounds [x, x+width) x [y, y+height) match
|
||||
// computeFooterBar so draw and hit-test agree on the same pixels. An empty (suppressed) box
|
||||
// never claims a point. The shell checks the toggle hit FIRST for a segment sub-hit (via
|
||||
// mode_switch over the toggle box), then the Tail hit; this returns which region was struck.
|
||||
FooterHit hitTestFooterBar(int px, int py, const FooterBarLayout& layout);
|
||||
|
||||
} // namespace reasampler
|
||||
Reference in New Issue
Block a user