Files
reasampler/src/shell/instrument/editor_input_chrome.cpp
T

136 lines
5.7 KiB
C++

// editor_input_chrome.cpp — the CHROME band's input: the Browse nav, the preview trigger,
// the preview-velocity knob grab, the channel toggle, and the 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 key)
#include "core/instrument/ui/knob_deck.h" // inKnobFace (the shared reset target rule)
#include "shell/instrument/editor_internal.h"
#include "shell/instrument/reasampler_processor.h"
namespace reasampler::vst {
using namespace reasampler::ui;
using namespace reasampler::instrument::ui;
bool ReaSamplerEditor::mouseDownChrome(const FaceLayout& fl, int x, int y) {
const ChromeRects& cr = fl.chrome;
if (contains(cr.navBrowse, x, y)) {
// Open the Browse modal; seed its pending pick from the loaded id so the current
// capture reads as pre-selected.
browsePendingId_ = selectedId_;
lastBrowseClickCard_ = -1;
view_ = View::kBrowse;
invalidate();
return true;
}
if (selectedId_.empty()) return false; // empty state — nav only
// Preview-trigger button: fire the loaded capture at its root through the voice engine
// (momentary — note-on on press, note-off on release).
if (contains(cr.preview, x, y)) {
const int note = effectiveRoot();
if (previewingNote_ >= 0) processor_->previewNoteOff(previewingNote_);
previewingNote_ = note;
processor_->previewNoteOn(note);
invalidate();
return true;
}
// Resample bake. Arm only: onSyncTimer runs it a tick later, off this handler's stack
// and with no mouse capture held (instrument_bake.h says why that matters). A click
// while the extension is absent is swallowed — the button already paints Disabled.
if (contains(cr.bake, x, y)) {
if (bakeAvailable_) bakePending_ = true;
invalidate();
return true;
}
// Radial preview-velocity knob: grab-anchored vertical drag — the grab itself never
// jumps the value; the delta from the grab point maps via knobDragValue.
if (contains(cr.velCell, x, y)) {
drag_ = DragKind::kDeckKnob;
dragParamId_ = -2; // sentinel: the preview velocity knob (a processor param)
dragInnerCellId_ = -1; // peer of mouseDownDeck's own grab-time init, for symmetry
dragKnobStartValue_ = previewVelocity01();
dragStartX_ = x;
dragStartY_ = y;
invalidate();
return true;
}
if (contains(cr.chanMono, x, y)) {
channelMode_ = ChannelMode::Mono;
processor_->setChannelMode(ChannelMode::Mono);
invalidate();
return true;
}
if (contains(cr.chanStereo, x, y)) {
channelMode_ = ChannelMode::Stereo;
processor_->setChannelMode(ChannelMode::Stereo);
invalidate();
return true;
}
// 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);
if (keyAtPoint(sl, x - cr.rootStrip.x, y - cr.rootStrip.y) >= 0) {
drag_ = DragKind::kRootMarker;
dragStartParams_ = params_;
onMouseMove(x, y);
return true;
}
}
// 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);
}
bool ReaSamplerEditor::doubleClickChrome(const FaceLayout& fl, int x, int y) {
// The preview-velocity dial answers the same reset gesture the deck's knobs do — it is a
// radial knob drawn by the same primitive, so it resolves through the same target rule
// (inKnobFace), against the drawn circle rather than the cell's label band.
if (selectedId_.empty() || !processor_) return false;
if (!inKnobFace(fl.chrome.velKnob, x, y)) return false;
processor_->setPreviewVelocity(kPreviewVelocityDefault);
invalidate();
return true;
}
void ReaSamplerEditor::dragChrome(const FaceLayout& fl, int x, int y) {
const Rect& stripArea = fl.chrome.rootStrip;
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);
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
}
ReaSamplerEditor::HoverTarget ReaSamplerEditor::hoverChrome(const FaceLayout& fl, int x,
int y) const {
const ChromeRects& cr = fl.chrome;
if (contains(cr.navBrowse, x, y)) return {HoverKind::kNavBrowse, -1};
if (selectedId_.empty()) return {}; // empty state — no interactive surfaces beyond nav
if (contains(cr.bake, x, y)) return {HoverKind::kBake, -1};
if (contains(cr.preview, x, y)) return {HoverKind::kPreview, -1};
if (contains(cr.velCell, x, y)) return {HoverKind::kVelKnob, -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 {};
}
} // namespace reasampler::vst
#endif // _WIN32