S17 drop-and-load onto track FX button; S13 editor drop-accept (relay degraded)

S17: drag_out InstrumentDrop gesture + instrument_drop blob reusing the
instrument's own serializer; bank_panel FX hover-track + add-VST/vst_chunk
inject. S13 relay deferred (read-only bridge) — editor shows drop affordance.
This commit is contained in:
2026-07-27 03:52:26 -04:00
parent 06494c654e
commit 26e2bf2fc6
13 changed files with 779 additions and 54 deletions
+31 -9
View File
@@ -52,28 +52,50 @@ struct PanelClientRect {
// whether a drag is currently active (threshold crossed) and whether the armed payload
// carries at least one sample. (Pre-threshold "armed but not yet dragging" is NOT a drag
// for this decision — the shell only asks once a drag is under way.)
//
// S17 (drop-and-load) adds two inputs that refine the OUTSIDE-the-panel decision without
// touching the INSIDE decision (the internal bank-to-bank drag stays byte-identical):
// * singleCapture — the payload holds EXACTLY ONE sample id. Only a single-capture drag
// arms the InstrumentDrop gesture (per the S17 open-question lean: a multi-capture drag
// over an FX button is NOT an instrument drop — it falls through to OsDrag, the natural
// multi-file drag-out to Explorer/another DAW). REJECT, not load-first: the whole gesture
// is "make ONE capture a playable instrument", so a multi payload is out of contract here.
// * overReaperUi — a SHELL-SUPPLIED predicate: true when the pointer, though outside the
// panel client rect, is still over REAPER's OWN window/UI (the shell owns the REAPER
// hit query, e.g. GetThingFromPoint; the pure layer owns only the set/boundary algebra).
// Both default false, so an M11-era caller that fills only {dragging, hasArmedSamples} gets
// EXACTLY the M11 behavior: outside the client rect with overReaperUi=false -> OsDrag.
struct DragState {
bool dragging = false; // threshold crossed; a drag is in progress
bool hasArmedSamples = false; // the drag payload holds >= 1 sample id
bool singleCapture = false; // S17: payload holds EXACTLY one sample (arms InstrumentDrop)
bool overReaperUi = false; // S17: pointer is over REAPER's own UI (shell-supplied)
};
// What the shell should do with the drag given the current pointer position.
enum class DragGesture {
None, // no drag under way, or an empty payload — do nothing
Internal, // dragging inside the panel — the existing bank-to-bank move/copy drag
OsDrag, // dragging with samples, pointer left the client area — hand off to the OS
None, // no drag under way, or an empty payload — do nothing
Internal, // dragging inside the panel — the existing bank-to-bank move/copy drag
InstrumentDrop, // S17: single-capture drag left the panel but is over REAPER's UI —
// the shell hover-tracks the TCP FX button and, on release, adds a
// ReaSampler 9000 instance preloaded with the dragged capture.
OsDrag, // dragging with samples, pointer left REAPER entirely — hand off to the OS
};
// Decides the gesture for a drag at pointer (px, py) over `client`, given `state`.
// * Not dragging (or no armed samples): None — the shell ignores the move.
// * Dragging with samples, pointer INSIDE the client rect: Internal — unchanged
// bank-to-bank behavior (invariant #4: the internal drag stays byte-identical).
// * Dragging with samples, pointer OUTSIDE the client rect: OsDrag — the samples are
// leaving the panel; the shell initiates the native OS drag with the resolved paths.
// The boundary is the client rect edge: the internal drag never targets outside it, so
// crossing it is an unambiguous, discoverable OS-drag trigger. Re-entry is the shell's
// concern (the OS drag loop is modal once begun); this function reports OsDrag purely from
// position, so a shell that has already handed off simply will not ask again.
// * Dragging OUTSIDE the client rect, SINGLE capture, over REAPER's UI: InstrumentDrop —
// the drag is heading for a track's FX button (S17); the shell hover-tracks + highlights.
// * Dragging OUTSIDE the client rect otherwise (multi-capture, OR the pointer has left
// REAPER entirely): OsDrag — the samples are leaving to the OS; the shell initiates the
// native OS drag with the resolved paths.
// The INSIDE decision is untouched (M11 internal drag is byte-identical). The M11 boundary
// (left the client rect -> OsDrag) is REFINED, not replaced: leaving the rect now asks
// "single-capture and over REAPER's UI -> InstrumentDrop, else -> OsDrag" — so the M11
// OS-drag-out (multi payload, or pointer off REAPER) keeps its exact behavior. Position-only
// + state-only (no hidden state), so re-entry back inside returns Internal.
DragGesture decideGesture(int px, int py, const PanelClientRect& client,
const DragState& state);