132 lines
5.9 KiB
C++
132 lines
5.9 KiB
C++
// editor_input_curve.cpp — the velocity-curve popup's input: the modal click routing,
|
|
// node grab/add/toggle/delete inside the curve box, the live node drag, the right-click
|
|
// delete on EITHER spline surface, and the popup's hover. Band-independent (the sheet floats
|
|
// over the whole face).
|
|
// Windows-only.
|
|
|
|
#include "shell/instrument/reasampler_editor.h"
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include "core/instrument/ui/curve_popup.h" // computeCurvePopup / popupOutsideSheet
|
|
#include "core/instrument/ui/spline_edit.h" // the shared point-editing grammar
|
|
#include "core/instrument/ui/waveform_view.h" // waveformOverlayArea (the overlay right-click)
|
|
#include "shell/instrument/editor_internal.h" // curveBoxFromRect
|
|
#include "shell/instrument/reasampler_processor.h"
|
|
|
|
namespace reasampler::vst {
|
|
|
|
using namespace reasampler::ui;
|
|
using namespace reasampler::instrument::ui;
|
|
|
|
bool ReaSamplerEditor::handlePopupMouseDown(int w, int h, int x, int y) {
|
|
// While open the sheet is modal over the face — it owns every left-click. Close click /
|
|
// outside-wash click dismiss (outside only when no drag is in flight); in-box clicks
|
|
// route to the curve machinery; anything else on the sheet is swallowed.
|
|
if (curvePopup_ == CurveTarget::kNone) return false;
|
|
const CurvePopupLayout pl = computeCurvePopup(w, h);
|
|
if (contains(pl.close, x, y)) {
|
|
closeCurvePopup();
|
|
invalidate();
|
|
return true;
|
|
}
|
|
if (contains(pl.curveBox, x, y)) {
|
|
handleCurveMouseDown(pl.curveBox, x, y);
|
|
return true;
|
|
}
|
|
if (popupOutsideSheet(pl, x, y) && drag_ == DragKind::kNone) {
|
|
closeCurvePopup();
|
|
invalidate();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ReaSamplerEditor::handleCurveMouseDown(const Rect& r, int x, int y) {
|
|
const VelocityCurve::Box box = curveBoxFromRect(r);
|
|
if (box.width <= 0 || box.height <= 1) return;
|
|
|
|
// Alt-click delete is retired (the spec's right-click supersedes it — one grammar, no
|
|
// migration on either side): every gesture here routes through the shared resolver.
|
|
const SplineGesture gesture =
|
|
dragModifiers().ctrl ? SplineGesture::kControlLeft : SplineGesture::kLeft;
|
|
const SplineEdit edit = resolveSplineEdit(editedCurve(), box, gesture, x, y);
|
|
if (edit.kind == SplineEditKind::kToggleHard) {
|
|
if (editedCurve().toggleHard(static_cast<std::size_t>(edit.index))) commitAndReload();
|
|
return;
|
|
}
|
|
if (edit.kind == SplineEditKind::kNone) return; // ring click with no node hit
|
|
|
|
// Snapshot BEFORE any mutation so a capture-loss rollback also cancels an in-flight ADD
|
|
// (mirror of the other parameter-editing drags' dragStartParams_ contract).
|
|
dragStartParams_ = params_;
|
|
|
|
int idx = edit.index;
|
|
if (edit.kind == SplineEditKind::kAdd) {
|
|
const VelocityPoint p = editedCurve().pointFromPixel(box, x, y);
|
|
idx = editedCurve().addPoint(p.velocity, p.value);
|
|
if (idx < 0) return; // at the point ceiling: refused, curve untouched
|
|
}
|
|
|
|
drag_ = DragKind::kCurveNode;
|
|
curvePointIndex_ = idx;
|
|
dragStartCurve_ = editedCurve(); // AFTER the add — resolvePointDrag's delta base
|
|
dragCurveRect_ = r;
|
|
dragStartX_ = x;
|
|
dragStartY_ = y;
|
|
invalidate(); // live feedback; the commit lands on WM_LBUTTONUP
|
|
}
|
|
|
|
void ReaSamplerEditor::dragCurve(int x, int y) {
|
|
// Resolve the grabbed control point from the pixel delta through the pure inverse map
|
|
// (box + neighbour-X + endpoint-pin clamps), against the grab-time curve + box (absolute
|
|
// delta — the mirror of the envelope-node drag). Live feedback only; commit on release.
|
|
if (curvePointIndex_ < 0) return;
|
|
editedCurve() = VelocityCurve::resolvePointDrag(
|
|
dragStartCurve_, static_cast<std::size_t>(curvePointIndex_),
|
|
curveBoxFromRect(dragCurveRect_), x - dragStartX_, y - dragStartY_);
|
|
invalidate();
|
|
}
|
|
|
|
void ReaSamplerEditor::onMouseRDown(int x, int y) {
|
|
// Right-click deletes a node on EITHER spline surface — the primary delete affordance;
|
|
// Alt-click and drag-off remain as landed alternates in the popup. deletePoint's endpoint
|
|
// guard makes an endpoint right-click a safe no-op. Never acts during an in-flight left
|
|
// drag; the modal popup wins when it is open.
|
|
if (!processor_ || view_ == View::kBrowse) return;
|
|
if (drag_ != DragKind::kNone) return;
|
|
RECT rc{};
|
|
GetClientRect(childHwnd_, &rc);
|
|
if (curvePopup_ != CurveTarget::kNone) {
|
|
const CurvePopupLayout pl = computeCurvePopup(rc.right - rc.left, rc.bottom - rc.top);
|
|
if (!contains(pl.curveBox, x, y)) return;
|
|
const SplineEdit edit = resolveSplineEdit(editedCurve(), curveBoxFromRect(pl.curveBox),
|
|
SplineGesture::kRight, x, y);
|
|
if (edit.kind != SplineEditKind::kDelete) return;
|
|
if (editedCurve().deletePoint(static_cast<std::size_t>(edit.index))) {
|
|
hover_ = HoverTarget{}; // a stale kCurveNode index would light a shifted node
|
|
commitAndReload();
|
|
}
|
|
return;
|
|
}
|
|
if (!overlayIsSpline() || !overlayEnvEnabled(overlayEnv_, deckEnableState())) return;
|
|
const FaceLayout fl = faceLayout(rc.right - rc.left, rc.bottom - rc.top);
|
|
splineOverlayClick(waveformOverlayArea(fl.bands.waveform), x, y, SplineGesture::kRight,
|
|
/*addOnEmptySpace=*/false);
|
|
}
|
|
|
|
ReaSamplerEditor::HoverTarget ReaSamplerEditor::hoverCurvePopup(int w, int h, int x,
|
|
int y) const {
|
|
const CurvePopupLayout pl = computeCurvePopup(w, h);
|
|
if (contains(pl.close, x, y)) return {HoverKind::kPopupClose, -1};
|
|
if (!contains(pl.curveBox, x, y)) return {};
|
|
// A curve node under the pointer lights accent-hot.
|
|
const int idx =
|
|
editedCurve().pointAtPixel(curveBoxFromRect(pl.curveBox), x, y);
|
|
if (idx < 0) return {};
|
|
return {HoverKind::kCurveNode, idx};
|
|
}
|
|
|
|
} // namespace reasampler::vst
|
|
|
|
#endif // _WIN32
|