Rebuild the chrome band: full-width piano strip with uniform key widths, note tooltips, one toolbar font
This commit is contained in:
@@ -90,7 +90,7 @@ scattered `#ifdef`s in the VST shell, except the one described below).
|
||||
- `reasampler_editor` — VST3 `IPlugView` LICE editor shell: hosts a LICE-drawn child window; the Sample face is home and Browse is a modal picker over it. Split on the Sample face's BAND axis, mirroring the pure `sample_bands` allocator: `editor_session` (session/bridge state, caches, commit-and-reload), `editor_controls` (parameter plumbing + the ONE `faceLayout` band resolve every paint and hit-test path shares), then matching paint and input sets — `editor_paint`/`editor_input` (dispatch + drag router + hover dispatch), `_chrome`, `_waveform`, `_deck` — plus the two band-independent surfaces (`_browse` for the modal picker, `_curve` for the velocity-curve popup) and `editor_platform` (IPlugView/Win32 window plumbing). Shared internals in `editor_internal.h`, no TU of its own. Drop-onto-editor ingest is NOT shipped (deferred).
|
||||
- `reasampler_embed` — implements `IReaperUIEmbedInterface` so the instrument draws inline in the TCP/MCP without a plugin-owned HWND; delegates layout to `embed_strip`. A read-only readout: the loaded capture across the keyboard span with its root marked, plus the activity level. It takes no mouse input (there is nothing on the strip to select).
|
||||
- `vst_entry` — VST3 entry point: `GetPluginFactory` export, class registration, channel-forked class UIDs.
|
||||
- `editor_internal.h` — INTERNAL shared helpers for the `reasampler_editor` TU family, included only by the editor's own shell TUs (`editor_session` / `editor_controls` / `editor_paint_*` / `editor_input_*` / `editor_platform`), never a public seam: the `Rect`↔kit adapters, small draw primitives (knob face / spectral strip / root marker / title band), label helpers, deck group ids, and the velocity-curve box derivation — the helpers more than one band TU needs.
|
||||
- `editor_internal.h` — INTERNAL shared helpers for the `reasampler_editor` TU family, included only by the editor's own shell TUs (`editor_session` / `editor_controls` / `editor_paint_*` / `editor_input_*` / `editor_platform`), never a public seam: the `Rect`↔kit adapters, small draw primitives (knob face / title band), label helpers, deck group ids, and the velocity-curve box derivation — the helpers more than one band TU needs. The piano-strip and root-key draws live in `editor_paint_chrome`, their only consumer, not here.
|
||||
- `reasampler_vst.h` — shared identity constants for the ReaSampler VST3 instrument (Phase S): the plugin's class UID (the channel-selected `Steinberg::FUID`, built from the FOREVER-FROZEN macros in `core/wire/reasampler_uid.h`), vendor name/URL/email, so the processor, factory, and editor agree. A class UID is FOREVER-STABLE once shipped — minted once, never regenerated. *(Newly authored per this dispatch's brief — no existing root-CLAUDE.md bullet; verified by reading `src/shell/instrument/reasampler_vst.h` directly.)*
|
||||
|
||||
## Gotchas
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// editor_input_chrome.cpp — the CHROME band's input: the Browse nav, the preview trigger,
|
||||
// the preview-velocity knob grab, the curve-button summon, the channel toggle, and the
|
||||
// root-marker grab plus its live drag. Windows-only.
|
||||
// piano strip's root grab plus its live drag. Windows-only.
|
||||
|
||||
#include "shell/instrument/reasampler_editor.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "core/instrument/ui/keyboard_strip.h" // keyAtPoint / resolveDragNote (root marker)
|
||||
#include "core/instrument/ui/keyboard_strip.h" // keyAtPoint / resolveDragNote (root key)
|
||||
#include "shell/instrument/editor_internal.h"
|
||||
#include "shell/instrument/reasampler_processor.h"
|
||||
|
||||
@@ -69,31 +69,32 @@ bool ReaSamplerEditor::mouseDownChrome(const FaceLayout& fl, int x, int y) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The root strip: grab the root marker. A plain click sets the root to the clicked key
|
||||
// (applied below as the first delta==0 move).
|
||||
if (cr.rootStrip.width > 0) {
|
||||
// The piano strip: clicking a key sets the root, and holding tracks the pointer. The
|
||||
// click itself lands below as the first (unmoved) drag resolve.
|
||||
if (!cr.rootStrip.empty()) {
|
||||
const StripLayout sl = layoutStrip(cr.rootStrip.width, cr.rootStrip.height);
|
||||
const int note = keyAtPoint(sl, x - cr.rootStrip.x, y - cr.rootStrip.y);
|
||||
if (note >= 0) {
|
||||
if (keyAtPoint(sl, x - cr.rootStrip.x, y - cr.rootStrip.y) >= 0) {
|
||||
drag_ = DragKind::kRootMarker;
|
||||
dragStartX_ = x;
|
||||
dragStartRoot_ = note;
|
||||
dragStartParams_ = params_;
|
||||
onMouseMove(x, y); // apply the click as the first delta==0 set
|
||||
onMouseMove(x, y);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// A click on the control row's background is consumed so it can't fall through to a
|
||||
// A click on the strip row's background is consumed so it can't fall through to a
|
||||
// band the user cannot see under the chrome.
|
||||
return contains(cr.controls, x, y);
|
||||
}
|
||||
|
||||
void ReaSamplerEditor::dragChrome(const FaceLayout& fl, int x, int y) {
|
||||
(void)y;
|
||||
const Rect& stripArea = fl.chrome.rootStrip;
|
||||
if (stripArea.width <= 0) return;
|
||||
if (stripArea.empty()) return;
|
||||
// Absolute tracking, not a pixel delta: with black keys overlaying whites there is no
|
||||
// one pixels-per-semitone rate a delta could use.
|
||||
const StripLayout sl = layoutStrip(stripArea.width, stripArea.height);
|
||||
params_.rootOverride = resolveDragNote(sl, dragStartRoot_, x - dragStartX_);
|
||||
const int note = resolveDragNote(sl, x - stripArea.x, y - stripArea.y);
|
||||
if (note < 0) return;
|
||||
params_.rootOverride = note;
|
||||
invalidate(); // live feedback; the commit lands on WM_LBUTTONUP
|
||||
}
|
||||
|
||||
@@ -107,6 +108,11 @@ ReaSamplerEditor::HoverTarget ReaSamplerEditor::hoverChrome(const FaceLayout& fl
|
||||
if (contains(cr.curveBtn, x, y)) return {HoverKind::kCurveButton, -1};
|
||||
if (contains(cr.chanMono, x, y)) return {HoverKind::kChanMono, -1};
|
||||
if (contains(cr.chanStereo, x, y)) return {HoverKind::kChanStereo, -1};
|
||||
if (!cr.rootStrip.empty()) {
|
||||
const StripLayout sl = layoutStrip(cr.rootStrip.width, cr.rootStrip.height);
|
||||
const int note = keyAtPoint(sl, x - cr.rootStrip.x, y - cr.rootStrip.y);
|
||||
if (note >= 0) return {HoverKind::kStripKey, note};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// editor_internal.h — shared helpers for the ReaSamplerEditor TU family. Included ONLY by
|
||||
// the editor's own shell TUs (editor_session / editor_controls / editor_paint_* /
|
||||
// editor_input_* / editor_platform) — never a public seam. Holds the Rect<->kit adapters,
|
||||
// small draw primitives (knob face / spectral strip / root marker / title band), label
|
||||
// helpers, deck group ids, and the velocity-curve box derivation. All inline.
|
||||
// small draw primitives (knob face / title band), label helpers, deck group ids, and the
|
||||
// velocity-curve box derivation. All inline.
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "core/instrument/engine/velocity_curve.h" // VelocityCurve::Box (curveBoxFromRect)
|
||||
#include "core/instrument/map/sample_map.h" // SampleChoice / SampleRefs (sampleLabel)
|
||||
#include "core/instrument/ui/editor_geometry.h" // Rect (the shared sub-rect type)
|
||||
#include "core/instrument/ui/keyboard_strip.h" // noteName (the one note-naming source)
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "wdltypes.h"
|
||||
@@ -22,7 +23,6 @@
|
||||
#include "core/audio/peaks.h" // Envelope (drawEnvelope)
|
||||
#include "core/instrument/ui/capture_browser.h" // BrowserLayout / cardThumbnailRect (thumbBins)
|
||||
#include "core/instrument/ui/param_slider.h" // KnobGeometry / KnobArc (drawKnobFace)
|
||||
#include "core/instrument/ui/keyboard_strip.h" // StripLayout / keyRect / isNaturalKey (spectral strip)
|
||||
#include "core/ui/component_geometry.h" // KitBox / waveformColumnCount
|
||||
#include "core/ui/theme.h" // Role / InteractionState / KitColor / spectralColor
|
||||
#include "shell/panel/draw_kit.h" // the L1 draw kit: fillSurface/text/drawWaveform/toLice
|
||||
@@ -55,15 +55,10 @@ inline instrument::engine::VelocityCurve::Box curveBoxFromRect(
|
||||
(std::max)(0, r.height - 2 * kVelCurveInset)};
|
||||
}
|
||||
|
||||
// A short MIDI-note label ("C4", "F#3") for the root badge. Middle C (60) is C4 (the
|
||||
// common DAW convention REAPER uses).
|
||||
// A short MIDI-note label ("C4", "F#3"). The naming itself is the pure strip module's, so
|
||||
// a browser badge and a strip tooltip can never disagree about what a note is called.
|
||||
inline std::string noteLabel(int note) {
|
||||
static const char* kNames[12] = {"C", "C#", "D", "D#", "E", "F",
|
||||
"F#", "G", "G#", "A", "A#", "B"};
|
||||
if (note < 0) note = 0;
|
||||
if (note > 127) note = 127;
|
||||
const int octave = note / 12 - 1; // MIDI 0 = C-1; 60 = C4
|
||||
return std::string(kNames[note % 12]) + std::to_string(octave);
|
||||
return instrument::ui::noteName(note);
|
||||
}
|
||||
|
||||
// A display name for a bank sample id: the snapshotted bank list first, then the
|
||||
@@ -170,53 +165,6 @@ inline void drawKnobFace(LICE_IBitmap* bmp, const instrument::ui::Rect& knobRect
|
||||
toLice(ui::roleColor(needleRole)), 1.0f, 0, true);
|
||||
}
|
||||
|
||||
// Draws the pastel spectral keyboard-strip background: each MIDI key column filled with
|
||||
// its spectral hue, accidentals darkened with an overlay wash so pitch position reads as
|
||||
// a keyboard at a glance.
|
||||
inline void drawSpectralStrip(LICE_IBitmap* bmp, const instrument::ui::Rect& stripArea) {
|
||||
using instrument::ui::StripLayout;
|
||||
if (stripArea.width <= 0 || stripArea.height <= 0) return;
|
||||
const StripLayout sl = instrument::ui::layoutStrip(stripArea.width, stripArea.height);
|
||||
const int sx = stripArea.x;
|
||||
const int sy = stripArea.y;
|
||||
const int h = stripArea.height;
|
||||
const LICE_pixel darkKey = toLice(ui::roleColor(ui::Role::BgBase));
|
||||
for (int n = 0; n <= 127; ++n) {
|
||||
const instrument::ui::Rect k = instrument::ui::keyRect(sl, n);
|
||||
const int x0 = k.x + sx;
|
||||
const int x1 =
|
||||
(n < 127) ? instrument::ui::keyRect(sl, n + 1).x + sx : stripArea.right();
|
||||
const int cw = (std::max)(1, x1 - x0);
|
||||
const ui::KitColor hue = ui::spectralColor(static_cast<double>(n) / 127.0);
|
||||
LICE_FillRect(bmp, x0, sy, cw, h, toLice(hue), 0.55f, 0);
|
||||
if (!instrument::ui::isNaturalKey(n)) {
|
||||
LICE_FillRect(bmp, x0, sy, cw, h, darkKey, 0.55f, 0);
|
||||
}
|
||||
}
|
||||
// Faint per-octave key ticks (hairline role) for orientation.
|
||||
const LICE_pixel tick = toLice(ui::roleColor(ui::Role::LineHairline));
|
||||
for (int n = 0; n <= 127; n += 12) {
|
||||
const instrument::ui::Rect k = instrument::ui::keyRect(sl, n);
|
||||
LICE_Line(bmp, k.x + sx, sy, k.x + sx, sy + h, tick, 1.0f, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Draws the single-capture root marker: an accent-primary bar with a soft static glow —
|
||||
// the "this is live" mark.
|
||||
inline void drawRootMarker(LICE_IBitmap* bmp, const instrument::ui::Rect& stripArea,
|
||||
const instrument::ui::StripLayout& sl, int root) {
|
||||
const int sx = stripArea.x;
|
||||
const int sy = stripArea.y;
|
||||
const int h = stripArea.height;
|
||||
const instrument::ui::Rect marker = instrument::ui::rootMarkerRect(sl, root);
|
||||
const int mw = (std::max)(2, marker.width);
|
||||
const LICE_pixel accent = toLice(ui::roleColor(ui::Role::AccentPrimary));
|
||||
const LICE_pixel glow = toLice(ui::roleColor(ui::Role::AccentHot));
|
||||
// Static glow: a wider low-alpha halo behind the crisp bar (a drawn state, not a pulse).
|
||||
LICE_FillRect(bmp, marker.x + sx - 3, sy, mw + 6, h, glow, 0.30f, 0);
|
||||
LICE_FillRect(bmp, marker.x + sx, sy, mw, h, accent, 1.0f, 0);
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
} // namespace reasampler::vst
|
||||
|
||||
@@ -71,6 +71,9 @@ void ReaSamplerEditor::paintSample(LICE_IBitmap* bmp, int w, int h) {
|
||||
|
||||
// The curve popup: a centered sheet over the whole face, drawn last.
|
||||
if (curvePopupOpen_) paintCurvePopup(bmp, w, h);
|
||||
|
||||
// The piano strip's note-name chip overhangs its band, so it goes on top of everything.
|
||||
paintChromeTooltip(bmp, fl, w, h);
|
||||
}
|
||||
|
||||
void ReaSamplerEditor::paintEmptyState(LICE_IBitmap* bmp, const Rect& area) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// editor_paint_chrome.cpp — the CHROME band's painter: the toolbar row (product title +
|
||||
// live readout + Browse) and the control row (root/piano strip with its root marker, the
|
||||
// preview trigger, the preview-velocity knob cell, the curve-preview button, and the
|
||||
// Mono|Stereo toggle). Windows-only; all rects come from the pure sample_chrome interior.
|
||||
// live readout, then the control run — preview, preview-velocity knob, curve button,
|
||||
// Mono|Stereo, Browse) over the strip row, which the piano strip has to itself. Windows-only;
|
||||
// all rects come from the pure sample_chrome interior and the pure keyboard_strip geometry.
|
||||
|
||||
#include "shell/instrument/reasampler_editor.h"
|
||||
|
||||
@@ -10,9 +10,11 @@
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "core/instrument/ui/keyboard_strip.h" // StripLayout / keyRect / noteName
|
||||
#include "core/instrument/ui/knob_deck.h" // kDeckKnobSize (the shared knob square)
|
||||
#include "core/ui/tooltip.h" // computeTooltip (shared placement math)
|
||||
#include "core/version/app_version.h" // vstPluginName (channel-derived title band)
|
||||
#include "shell/instrument/editor_internal.h" // kit adapters + knob face / spectral strip / root marker
|
||||
#include "shell/instrument/editor_internal.h" // kit adapters + knob face
|
||||
#include "shell/instrument/reasampler_processor.h"
|
||||
|
||||
namespace reasampler::vst {
|
||||
@@ -21,9 +23,81 @@ using namespace reasampler::ui; // kit vocabulary
|
||||
using namespace reasampler::instrument::ui; // chrome geometry + keyboard strip
|
||||
using namespace reasampler::instrument::map; // SampleRefs / findRef (title readout fallback)
|
||||
|
||||
namespace {
|
||||
|
||||
// Every text element on the toolbar row draws at this one size/weight — including the
|
||||
// product title, which used to be the row's odd one out.
|
||||
constexpr Font kToolbarFont = Font::Label;
|
||||
|
||||
// Kit font is proportional, so the char width is a generous estimate (pads, never clips).
|
||||
constexpr int kTooltipCharPx = 7;
|
||||
constexpr int kTooltipTextH = 14;
|
||||
|
||||
constexpr int kRootBadgeW = 38;
|
||||
constexpr int kRootBadgeH = 13;
|
||||
|
||||
// The piano strip: white keys tiled at one width, black keys overlaid at one width, each
|
||||
// tinted with its spectral hue so pitch position reads at a glance. `hoverNote` is outlined
|
||||
// (-1 for none). All rects are strip-local; `area` supplies the origin.
|
||||
void drawKeyboard(LICE_IBitmap* bmp, const Rect& area, const StripLayout& sl, int hoverNote) {
|
||||
fillSurface(bmp, toKitBox(area), Role::BgBase, InteractionState::Rest);
|
||||
if (sl.keys.empty()) return;
|
||||
|
||||
const LICE_pixel hairline = toLice(roleColor(Role::LineHairline));
|
||||
const LICE_pixel shadow = toLice(roleColor(Role::BgBase));
|
||||
const auto hueOf = [](int n) {
|
||||
return toLice(spectralColor(static_cast<double>(n) / (kStripKeyCount - 1)));
|
||||
};
|
||||
|
||||
for (int n = 0; n < kStripKeyCount; ++n) {
|
||||
if (!isNaturalKey(n)) continue;
|
||||
const Rect k = keyRect(sl, n);
|
||||
LICE_FillRect(bmp, area.x + k.x, area.y + k.y, k.width, k.height, hueOf(n), 0.55f, 0);
|
||||
LICE_Line(bmp, area.x + k.right() - 1, area.y + k.y, area.x + k.right() - 1,
|
||||
area.y + k.bottom() - 1, hairline, 0.6f, 0, false);
|
||||
}
|
||||
// Blacks last: they overlap the whites they straddle.
|
||||
for (int n = 0; n < kStripKeyCount; ++n) {
|
||||
if (isNaturalKey(n)) continue;
|
||||
const Rect k = keyRect(sl, n);
|
||||
LICE_FillRect(bmp, area.x + k.x, area.y + k.y, k.width, k.height, shadow, 1.0f, 0);
|
||||
LICE_FillRect(bmp, area.x + k.x, area.y + k.y, k.width, k.height, hueOf(n), 0.35f, 0);
|
||||
}
|
||||
if (hoverNote >= 0) {
|
||||
const Rect k = keyRect(sl, hoverNote);
|
||||
LICE_DrawRect(bmp, area.x + k.x, area.y + k.y, k.width - 1, k.height - 1,
|
||||
toLice(roleColor(Role::TextPrimary)), 0.8f, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// The root affordance: the root key lit accent-primary with a static glow, plus a name badge
|
||||
// (a key is far too narrow to carry text itself). The badge is clamped inside the strip.
|
||||
void drawRootKey(LICE_IBitmap* bmp, const Rect& area, const StripLayout& sl, int root) {
|
||||
if (sl.keys.empty()) return;
|
||||
const Rect k = rootMarkerRect(sl, root);
|
||||
const int kx = area.x + k.x;
|
||||
const LICE_pixel accent = toLice(roleColor(Role::AccentPrimary));
|
||||
const LICE_pixel glow = toLice(roleColor(Role::AccentHot));
|
||||
// Static glow: a wider low-alpha halo behind the lit key (a drawn state, not a pulse).
|
||||
LICE_FillRect(bmp, kx - 3, area.y + k.y, k.width + 6, k.height, glow, 0.30f, 0);
|
||||
LICE_FillRect(bmp, kx, area.y + k.y, k.width, k.height, accent, 1.0f, 0);
|
||||
|
||||
const int badgeH = (std::min)(kRootBadgeH, area.height);
|
||||
const int badgeW = (std::min)(kRootBadgeW, area.width);
|
||||
int bx = kx + (k.width - badgeW) / 2;
|
||||
bx = (std::max)(area.x, (std::min)(bx, area.right() - badgeW));
|
||||
const Rect badge = Rect::ltrb(bx, area.bottom() - badgeH, bx + badgeW, area.bottom());
|
||||
LICE_FillRect(bmp, badge.x, badge.y, badge.width, badge.height, accent, 0.92f, 0);
|
||||
kitTextCentered(bmp, badge, noteName(root).c_str(), Font::Micro, Role::BgBase);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ReaSamplerEditor::paintChrome(LICE_IBitmap* bmp, const FaceLayout& fl, bool empty) {
|
||||
const ChromeRects& cr = fl.chrome;
|
||||
|
||||
fillSurface(bmp, toKitBox(cr.toolbar), Role::BgPanel, InteractionState::Rest);
|
||||
|
||||
// Toolbar: product name + live readout. The beta channel gets no distinct accent; the
|
||||
// channel-derived vstPluginName is the only beta-vs-stable signal.
|
||||
std::string title = version::vstPluginName();
|
||||
@@ -38,7 +112,7 @@ void ReaSamplerEditor::paintChrome(LICE_IBitmap* bmp, const FaceLayout& fl, bool
|
||||
} else {
|
||||
title += " [host: no bridge]";
|
||||
}
|
||||
drawTitleBand(bmp, cr.toolbar, title);
|
||||
kitText(bmp, cr.title, title.c_str(), kToolbarFont, Role::TextPrimary);
|
||||
|
||||
// Browse: the picker. When nothing is loaded it is the empty state's dominant
|
||||
// call-to-action — draw it Active (accent-primary) so it reads as "start here".
|
||||
@@ -50,19 +124,9 @@ void ReaSamplerEditor::paintChrome(LICE_IBitmap* bmp, const FaceLayout& fl, bool
|
||||
drawButton(bmp, box, "Browse", st, /*warn=*/false);
|
||||
}
|
||||
|
||||
// The control row draws only once a capture is loaded — with nothing picked there is no
|
||||
// root, no preview and no channel decision to make.
|
||||
if (empty || cr.controls.empty()) return;
|
||||
|
||||
fillSurface(bmp, toKitBox(cr.controls), Role::BgPanel, InteractionState::Rest);
|
||||
|
||||
// Root strip: the full 128-key spectral band with the root marked. The loaded capture
|
||||
// responds across the whole strip, repitched from that root.
|
||||
if (cr.rootStrip.width > 0) {
|
||||
drawSpectralStrip(bmp, cr.rootStrip);
|
||||
const StripLayout sl = layoutStrip(cr.rootStrip.width, cr.rootStrip.height);
|
||||
drawRootMarker(bmp, cr.rootStrip, sl, effectiveRoot());
|
||||
}
|
||||
// The rest of the run and the strip row draw only once a capture is loaded — with
|
||||
// nothing picked there is no root, no preview and no channel decision to make.
|
||||
if (empty) return;
|
||||
|
||||
// Preview-trigger button (fires the loaded capture at root through the live voice engine).
|
||||
{
|
||||
@@ -106,11 +170,50 @@ void ReaSamplerEditor::paintChrome(LICE_IBitmap* bmp, const FaceLayout& fl, bool
|
||||
: InteractionState::Rest);
|
||||
fillSurface(bmp, toKitBox(cr.chanMono), Role::BgCell, monoState);
|
||||
fillSurface(bmp, toKitBox(cr.chanStereo), Role::BgCell, stereoState);
|
||||
kitTextCentered(bmp, cr.chanMono, "Mono", Font::Label,
|
||||
kitTextCentered(bmp, cr.chanMono, "Mono", kToolbarFont,
|
||||
!isStereo ? Role::BgBase : Role::TextPrimary);
|
||||
kitTextCentered(bmp, cr.chanStereo, "Stereo", Font::Label,
|
||||
kitTextCentered(bmp, cr.chanStereo, "Stereo", kToolbarFont,
|
||||
isStereo ? Role::BgBase : Role::TextPrimary);
|
||||
}
|
||||
|
||||
// The strip row: the full 128-key piano with the root lit. The loaded capture responds
|
||||
// across the whole strip, repitched from that root.
|
||||
if (cr.controls.empty() || cr.rootStrip.empty()) return;
|
||||
fillSurface(bmp, toKitBox(cr.controls), Role::BgPanel, InteractionState::Rest);
|
||||
const StripLayout sl = layoutStrip(cr.rootStrip.width, cr.rootStrip.height);
|
||||
// Same staleness guard as the tooltip: a latched hover note outlives a drag it started.
|
||||
const int hoverNote = (drag_ == DragKind::kNone && hover_.kind == HoverKind::kStripKey)
|
||||
? hover_.index : -1;
|
||||
drawKeyboard(bmp, cr.rootStrip, sl, hoverNote);
|
||||
drawRootKey(bmp, cr.rootStrip, sl, effectiveRoot());
|
||||
}
|
||||
|
||||
// Drawn after every band so the chip is never painted over. No hover delay: the strip is a
|
||||
// continuous readout you sweep, and a delay there reads as a dead surface — unlike the bank
|
||||
// panel's buttons, where the delay stops tooltips firing on every traverse.
|
||||
void ReaSamplerEditor::paintChromeTooltip(LICE_IBitmap* bmp, const FaceLayout& fl, int w,
|
||||
int h) {
|
||||
if (hover_.kind != HoverKind::kStripKey || hover_.index < 0) return;
|
||||
// Hover is deliberately not re-resolved mid-drag, so the latched note would go stale
|
||||
// under a root drag — the root badge is the live readout there.
|
||||
if (drag_ != DragKind::kNone) return;
|
||||
const Rect& area = fl.chrome.rootStrip;
|
||||
if (area.empty()) return;
|
||||
const StripLayout sl = layoutStrip(area.width, area.height);
|
||||
const Rect key = keyRect(sl, hover_.index);
|
||||
if (key.empty()) return;
|
||||
|
||||
const std::string label = noteName(hover_.index);
|
||||
const int textW = static_cast<int>(label.size()) * kTooltipCharPx;
|
||||
const TooltipBox tb = computeTooltip(area.x + key.x, area.y + key.y, key.width, key.height,
|
||||
textW, kTooltipTextH, w, h, TooltipSpec{});
|
||||
if (tb.empty()) return;
|
||||
|
||||
const Rect box = Rect::ltrb(tb.x, tb.y, tb.x + tb.width, tb.y + tb.height);
|
||||
fillSurface(bmp, toKitBox(box), Role::BgCell, InteractionState::Hover);
|
||||
LICE_DrawRect(bmp, box.x, box.y, box.width, box.height,
|
||||
toLice(roleColor(Role::LineHairline)), 1.0f, 0);
|
||||
kitTextCentered(bmp, box, label.c_str(), Font::Label, Role::TextPrimary);
|
||||
}
|
||||
|
||||
} // namespace reasampler::vst
|
||||
|
||||
@@ -130,6 +130,7 @@ private:
|
||||
kControl, // a knob-deck element (index = control id)
|
||||
kCurveNode, // a velocity-curve control point (index = point index)
|
||||
kVelKnob, // the chrome preview-velocity radial knob
|
||||
kStripKey, // a piano-strip key (index = MIDI note); carries the name tooltip
|
||||
kCurveButton, // the chrome mini curve-preview button (opens the popup)
|
||||
kPopupClose, // the curve popup's Close (x) button
|
||||
};
|
||||
@@ -160,6 +161,9 @@ private:
|
||||
// Chrome: title band + Browse nav + the control row (root strip, preview, velocity knob,
|
||||
// curve button, channel toggle).
|
||||
void paintChrome(LICE_IBitmap* bmp, const FaceLayout& fl, bool empty);
|
||||
// The hovered piano key's note-name chip. Drawn after every band — it overhangs the
|
||||
// chrome into whatever is below it.
|
||||
void paintChromeTooltip(LICE_IBitmap* bmp, const FaceLayout& fl, int w, int h);
|
||||
// Waveform: the channel lane(s), the loop/start markers, and the envelope overlay.
|
||||
void paintWaveform(LICE_IBitmap* bmp, const Rect& band);
|
||||
// Decks: the group fence + caption + compact caption toggles + radial knobs with
|
||||
@@ -417,7 +421,6 @@ private:
|
||||
int dragStartY_ = 0; // grab y (px), for the vertical scrollbar-thumb drag
|
||||
int dragCurX_ = 0; // live cursor x (px) during a drag — updated in onMouseMove
|
||||
int dragCurY_ = 0; // live cursor y (px) during a drag — updated in onMouseMove
|
||||
int dragStartRoot_ = 60; // the root note at grab time
|
||||
InstrumentParams dragStartParams_; // params_ snapshotted at grab; restored on capture-loss
|
||||
|
||||
// Waveform-marker drag: which marker + the marker set snapshotted at grab time, so the
|
||||
|
||||
Reference in New Issue
Block a user