// 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