Files
reasampler/src/core/instrument/ui/sample_chrome.cpp
T
daniel 9d38f87a2d instrument: one VELOCITY deck for all three velocity curves, bipolar and off by default for pitch and filter
Payload v12 appends the new velocity->pitch curve and folds the retired filter velAmount into its now-bipolar curve, so pre-v12 projects reopen sounding identical. Preview button takes a drawn play triangle.
2026-07-31 19:15:17 -04:00

112 lines
4.8 KiB
C++

// sample_chrome.cpp — see sample_chrome.h. Pure math; no host types.
#include "core/instrument/ui/sample_chrome.h"
#include <algorithm>
#include "core/instrument/ui/sample_bands.h" // kPad
namespace reasampler::instrument::ui {
namespace {
// The toolbar row carries the whole control run, so it is taller than the Browse modal's
// plain kTitleHeight bar — the velocity knob cell (knob over label) sets the floor. Both
// rows still fit the band the allocator hands out (kTitleHeight + kChromeRowHeight).
constexpr int kToolbarHeight = 44;
constexpr int kStripBandHeight = 30;
constexpr int kRunGap = 6; // between adjacent items of the toolbar run
constexpr int kChanSegW = 52;
constexpr int kChanSegH = 18;
constexpr int kVelCellW = 44;
constexpr int kVelLabelH = 12;
constexpr int kPreviewBtnW = 64;
constexpr int kRunButtonH = 24; // Browse and Preview
constexpr int kPreviewGlyphMinH = 6;
constexpr int kPreviewGlyphMaxH = 14;
constexpr int kPreviewGlyphPad = 4; // clearance between the glyph and the button edge
} // namespace
ChromeRects chromeRects(const Rect& chrome, int knobSize) {
ChromeRects r;
if (chrome.empty()) return r;
const int toolbarH = std::min(kToolbarHeight, chrome.height);
r.toolbar = Rect::ltrb(chrome.x, chrome.y, chrome.right(), chrome.y + toolbarH);
r.controls = Rect::ltrb(chrome.x, r.toolbar.bottom(), chrome.right(), chrome.bottom());
const Rect& row = r.toolbar;
const auto topFor = [&row](int h) { return row.y + (row.height - h) / 2; };
const auto leftOf = [&row](int edge, int w) { return std::max(row.x, edge - w); };
// The fixed run, right to left: Browse, Mono|Stereo, velocity cell, preview. The
// velocity-curve button that used to sit here now lives in the deck's VELOCITY group.
const int navH = std::min(kRunButtonH, row.height);
const int navTop = topFor(navH);
const int navRight = std::max(row.x, row.right() - kPad);
r.navBrowse = Rect::ltrb(leftOf(navRight, kNavButtonWidth), navTop, navRight,
navTop + navH);
const int chanTop = topFor(kChanSegH);
const int chanRight = leftOf(r.navBrowse.x, kRunGap);
r.chanStereo = Rect::ltrb(leftOf(chanRight, kChanSegW), chanTop, chanRight,
chanTop + kChanSegH);
r.chanMono = Rect::ltrb(leftOf(r.chanStereo.x, kChanSegW), chanTop, r.chanStereo.x,
chanTop + kChanSegH);
const int cellH = std::min(row.height, knobSize + kVelLabelH);
const int cellTop = topFor(cellH);
const int cellRight = leftOf(r.chanMono.x, kRunGap);
r.velCell = Rect::ltrb(leftOf(cellRight, kVelCellW), cellTop, cellRight, cellTop + cellH);
const int knobLeft = r.velCell.x + (r.velCell.width - knobSize) / 2;
r.velKnob = Rect::ltrb(knobLeft, r.velCell.y, knobLeft + knobSize,
r.velCell.y + std::min(knobSize, cellH));
r.velLabel = Rect::ltrb(r.velCell.x, r.velKnob.bottom(), r.velCell.right(),
r.velCell.bottom());
const int prevTop = topFor(std::min(kRunButtonH, row.height));
const int prevRight = leftOf(r.velCell.x, kRunGap);
r.preview = Rect::ltrb(leftOf(prevRight, kPreviewBtnW), prevTop, prevRight,
prevTop + std::min(kRunButtonH, row.height));
// The title takes what the run leaves; clamped so a narrow window collapses it rather
// than inverting it.
r.title = Rect::ltrb(row.x + kPad, row.y, std::max(row.x + kPad, r.preview.x - kRunGap),
row.bottom());
if (r.controls.empty()) return r;
// The strip row belongs to the strip alone — inset only by the shared band pad, so it
// lines up with the waveform band directly beneath it.
const int stripH = std::min(kStripBandHeight, r.controls.height);
const int stripTop = r.controls.y + (r.controls.height - stripH) / 2;
r.rootStrip = Rect::ltrb(r.controls.x + kPad, stripTop,
std::max(r.controls.x + kPad, r.controls.right() - kPad),
stripTop + stripH);
return r;
}
PreviewGlyph previewGlyph(const Rect& button) {
PreviewGlyph g;
if (button.empty()) return g;
// Even height so the apex lands exactly on the button's horizontal centre line rather
// than a half-pixel off it.
int h = std::min({kPreviewGlyphMaxH, button.height - 2 * kPreviewGlyphPad,
button.width - 2 * kPreviewGlyphPad});
h -= h % 2;
if (h < kPreviewGlyphMinH) return g;
const int w = std::max(2, (h * 7) / 8); // ~equilateral: the classic transport triangle
const int cx = button.x + button.width / 2;
const int cy = button.y + button.height / 2;
g.leftX = cx - w / 2;
g.apexX = g.leftX + w;
g.topY = cy - h / 2;
g.bottomY = g.topY + h;
g.apexY = cy;
return g;
}
} // namespace reasampler::instrument::ui