instrument: spline EGs — hard points on the one shared spline, a drawn contour per envelope beside its staged state, payload v13

This commit is contained in:
2026-07-31 21:33:59 -04:00
parent f115904e4f
commit e44bd42dd9
33 changed files with 1739 additions and 364 deletions
+39 -8
View File
@@ -20,6 +20,7 @@
#include "core/instrument/ui/envelope_overlay.h" // StageEnvelope / EnvNode (envelope overlay draw seam)
#include "core/instrument/ui/knob_deck.h" // DeckGroupDesc / DeckLayout (the deck band)
#include "core/instrument/ui/sample_bands.h" // SampleBands (the band-stack allocator)
#include "core/instrument/ui/spline_edit.h" // the shared point-editing grammar
#include "core/instrument/ui/sample_chrome.h" // ChromeRects (chrome-band interior)
#include "core/audio/peaks.h" // Envelope (the cached peak thumbnail)
#include "core/instrument/map/sample_map.h" // SampleChoice, BankChoice, InstrumentParams
@@ -78,8 +79,11 @@ private:
// What a mouse drag is currently editing. kWaveMarker/kEnvNode/kCurveNode track their
// grabbed item in waveMarker_/envNode_/curvePointIndex_; kDeckKnob is a grab-anchored
// knob drag (control in dragParamId_, grab value in dragKnobStartValue_).
// kSplineNode is the overlay's peer of kCurveNode: the same VelocityCurve point drag, over
// the waveform overlay's box and the overlay-active envelope's contour rather than the
// popup's box and curve.
enum class DragKind { kNone, kRootMarker, kWaveMarker, kScrollThumb, kEnvNode,
kCurveNode, kDeckKnob };
kCurveNode, kSplineNode, kDeckKnob };
// Which envelope the waveform overlay is drawing and editing. The selection type and its
// whole state machine are the pure deck_groups module's; this alias keeps the shell's
@@ -168,9 +172,20 @@ private:
// The velocity transfer-curve editor (X = velocity 0-127, Y = the curve's own domain); its
// only host is the popup sheet. `r` empty -> draws nothing.
void paintVelocityCurve(LICE_IBitmap* bmp, const Rect& r);
// Traces the amp-envelope overlay + its draggable node handles over `waveArea`, ONCE at
// full band height (never per lane).
// Traces the overlay-active envelope + its draggable handles over `waveArea`, ONCE at
// full band height (never per lane). Dispatches on the envelope's mode: the staged
// polyline, or the drawn contour below.
void paintEnvelopeOverlay(LICE_IBitmap* bmp, const OverlayArea& waveArea, std::int64_t frames);
// The spline EG's contour + point handles, spanning the overlay 1:1 with the sample's time
// axis. Hard points draw hollow so a corner is legible before it is steep.
void paintSplineOverlay(LICE_IBitmap* bmp, const OverlayArea& waveArea);
// A click on the spline overlay, routed through the shared grammar; true when it consumed
// the click. `addOnEmptySpace` false resolves node actions only — the waveform band calls
// it that way BEFORE the start/loop markers and again after, since the contour's box is the
// whole band and an unconditional add would make every marker unreachable.
bool splineOverlayClick(const OverlayArea& waveArea, int x, int y,
instrument::ui::SplineGesture gesture, bool addOnEmptySpace);
// --- Input: the mouse-down dispatch and its per-band branches ---
void onMouseDown(int x, int y);
@@ -194,13 +209,15 @@ private:
void onMouseMove(int x, int y);
void onMouseUp(int x, int y);
// Right-click is the curve popup's primary node-delete affordance; only acts while the
// popup is open (deletePoint's endpoint guard makes an endpoint right-click a no-op).
// Right-click is the primary node-delete affordance on both spline surfaces — the popup
// while it is open, else the spline EG overlay (deletePoint's endpoint guard makes an
// endpoint right-click a no-op).
void onMouseRDown(int x, int y);
// Mouse-down inside curve-editor box `r`: a node grab starts a kCurveNode drag;
// Alt-click on an interior node deletes it at once; an empty-space click adds a point
// and grabs it.
// Mouse-down inside curve-editor box `r`, resolved through the shared point-editing
// grammar (spline_edit): a node grab starts a kCurveNode drag, an empty-space click adds a
// point and grabs it, control-click toggles a node hard/smooth. Alt-click delete predates
// that grammar and is kept as a landed alternate.
void handleCurveMouseDown(const Rect& r, int x, int y);
// Left-click while the curve popup is open (modal over the Sample face): Close /
@@ -466,6 +483,20 @@ private:
CurveTarget curvePopup_ = CurveTarget::kNone;
void closeCurvePopup();
// The group-gate state the two pure inert predicates read, built once from the parameter
// set so paint and hit-test can never assemble it differently.
instrument::ui::DeckEnableState deckEnableState() const;
// THE one switch from an overlay selection to the drawn contour it names, mirroring
// curveFor. kNone reads as amp — harmless for a target-agnostic caller, and every mutating
// path is gated on overlayIsSpline() first.
const VelocityCurve& splineFor(OverlayEnv which) const;
VelocityCurve& splineFor(OverlayEnv which);
// Whether the overlay-active envelope is in Spline mode — the branch every overlay paint,
// hit-test and drag path takes before touching either model.
bool overlayIsSpline() const;
// THE one switch from a CurveTarget to the parameter-set curve it names — paint (button
// thumbnails, for every target) and edit (editedCurve, for curvePopup_ specifically) both
// route through it, so a fourth curve or a moved field is a one-place edit.