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:
@@ -634,17 +634,31 @@ a clear affordance pointing there.
|
||||
without it via a direct reload). **Spike — do not promise the drop-onto-editor path until
|
||||
the relay is proven.**
|
||||
|
||||
- [ ] Editor-window drop target: accept `WM_DROPFILES`/`IDropTarget` on the editor child
|
||||
- [x] Editor-window drop target: accept `WM_DROPFILES`/`IDropTarget` on the editor child
|
||||
HWND (the same SWELL/Win32 surface `bank_panel` owns), extracting the dropped file
|
||||
path(s). Windows-only (D5). This is the *acceptance* half; the ingest is the extension's.
|
||||
**Landed:** the editor child window calls `DragAcceptFiles(TRUE)` on attach and handles
|
||||
`WM_DROPFILES` (`reasampler_editor.cpp`). Windows-only (D5).
|
||||
- [ ] Cross-artifact ingest relay (the S8-flagged spike): the editor hands the dropped
|
||||
path + this instance's identity to the extension as a **bank-ingest request** over the
|
||||
agreed seam (the instrument never writes the bank). **Prove the relay mechanism before
|
||||
promising it**; if gnarly, fall back to the S8 docked-panel drop path and mark
|
||||
drop-onto-editor deferred.
|
||||
- [ ] UX degrade path: when the relay is unavailable/unproven, the editor shows a clear
|
||||
drop-onto-editor deferred. **SPIKE VERDICT (ps-w12, 2026-07-27): DEGRADED — relay
|
||||
deferred.** The instrument's REAPER bridge (`reaper_bridge`) is deliberately READ-ONLY
|
||||
(resolves only `GetProjExtState`, never `SetProjExtState`); a relay would need (a) a new
|
||||
instrument WRITE seam into ext-state and (b) an extension-side timer poller servicing a
|
||||
drop-ingest inbox key with a claim/clear nonce — the SAME cross-process handshake race the
|
||||
S17 spec rejected for its own alternative (A). Both the read-only-instrument boundary and
|
||||
the new poller are load-bearing design calls, so the relay is deferred to a future wave and
|
||||
surfaced as a decision, not crossed unilaterally. The shipped ingest gesture stays
|
||||
drop-onto-docked-panel (S8). The drop-onto-editor path degrades cleanly (below).
|
||||
- [x] UX degrade path: when the relay is unavailable/unproven, the editor shows a clear
|
||||
"drop files on the ReaSampler panel to add" affordance rather than silently swallowing the
|
||||
drop — the shipped ingest gesture stays discoverable either way.
|
||||
drop — the shipped ingest gesture stays discoverable either way. **Landed:** the editor
|
||||
ACCEPTS the drop and flashes a transient banner ("drop files onto the ReaSampler bank panel
|
||||
to add them") that decays over a few sync ticks, plus a persistent affordance line in the
|
||||
empty state ("drop a file onto the ReaSampler bank panel"). No file is ingested; NO timeline
|
||||
item is ever inserted (the hard invariant — the editor only displays guidance).
|
||||
|
||||
## S15 — sampling modes: Trigger vs Gate (per-sample play-mode; core + editor)
|
||||
**Goal:** Give each played sample a **play mode** — **Gate** (classic held note) or
|
||||
@@ -1020,22 +1034,41 @@ the editor). S17 is the third integration gesture: drop *onto a track's FX butto
|
||||
instantiate a player. Gated on the rest of Phase S; the instrument seam is a Phase S
|
||||
artifact, not extension-only.
|
||||
|
||||
- [ ] Extend the `drag_out` pure module with the third gesture: `decideGesture` (or a
|
||||
- [x] Extend the `drag_out` pure module with the third gesture: `decideGesture` (or a
|
||||
successor) returns `InstrumentDrop` when a drag armed with a single capture is over
|
||||
REAPER's UI outside the panel client rect, `OsDrag` only when it has left REAPER
|
||||
entirely, `Internal`/`OsDrag`/`None` otherwise unchanged. Pure over (drag state +
|
||||
pointer + panel rect + an "over-own-UI" predicate the shell supplies). Unit-tested —
|
||||
the existing `drag_out` invariants (M11) must not regress.
|
||||
- [ ] Shell (extension): hover-track the pointer over REAPER's UI during the drag,
|
||||
the existing `drag_out` invariants (M11) must not regress. **Landed:** `DragGesture::
|
||||
InstrumentDrop` + two defaulted `DragState` fields (`singleCapture`, `overReaperUi`, the
|
||||
shell-supplied predicate). Defaults false, so an M11 caller filling only
|
||||
`{dragging, hasArmedSamples}` gets byte-identical M11 behavior — the existing tests are
|
||||
the non-regression proof. **OPEN QUESTION RESOLVED (multi-capture over FX button):
|
||||
REJECT** — only `singleCapture` arms InstrumentDrop; a multi payload over REAPER's UI
|
||||
falls through to `OsDrag` (the natural multi-file drag-out), matching the spec's Tier-0
|
||||
reject-or-first lean toward reject.
|
||||
- [x] Shell (extension): hover-track the pointer over REAPER's UI during the drag,
|
||||
resolve the hovered track + its FX button (verify the TCP/FX-button hit surface against
|
||||
the SDK — see must-verify), highlight it as a drop target, and on release drive the
|
||||
drop. Extends the `bank_panel` drag hook alongside the M11 `drag_out_win` path.
|
||||
- [ ] Shell (extension): on drop, `TrackFX_AddByName(track, "VST3:ReaSampler 9000",
|
||||
**Landed:** `instrument_drop_win::resolveFxDropTarget` wraps `GetThingFromPoint`
|
||||
(verified present; its info string reports `"fx_chain"`/`"fx_N"` for the FX region and a
|
||||
null-track-empty-info for off-REAPER). **OPEN QUESTION RESOLVED (FX hotspot vs. whole
|
||||
TCP): FX HOTSPOT** — the drop target is the FX region specifically, decided from the SDK's
|
||||
own hit-test string (info prefix `"fx_"`), not a home-grown geometry guess. The
|
||||
bank_panel drag hook now hover-tracks outside the client rect (holding internal-drag
|
||||
capture, NOT the modal OS loop) and only hands to `drag_out_win` (OsDrag) when the pointer
|
||||
has left REAPER entirely.
|
||||
- [x] Shell (extension): on drop, `TrackFX_AddByName(track, "VST3:ReaSampler 9000",
|
||||
false, /*instantiate*/ negative)` to always add a fresh instance; capture the returned
|
||||
FX index; then invoke the load-capture seam to point the new instance at the dragged
|
||||
capture. Batched into one REAPER undo point (`Undo_BeginBlock2`/`EndBlock2`) so the
|
||||
whole gesture is one Ctrl-Z (mirrors the bank-verb undo discipline).
|
||||
- [ ] **ReaSampler 9000 (Phase S artifact):** add the **load-capture seam** — the entry
|
||||
whole gesture is one Ctrl-Z (mirrors the bank-verb undo discipline). **Landed:**
|
||||
`instrument_drop_win::performInstrumentDrop` — `TrackFX_AddByName` with the CHANNEL-correct
|
||||
name (`"VST3:" + app_version::vstPluginName()`, so a beta extension drops the beta VST),
|
||||
negative instantiate, then `TrackFX_SetNamedConfigParm(..., "vst_chunk", blob)`, all in one
|
||||
`Undo_BeginBlock2`/`EndBlock2` block. NEVER inserts a timeline item.
|
||||
- [x] **ReaSampler 9000 (Phase S artifact):** add the **load-capture seam** — the entry
|
||||
point that lets the just-added instance be told which capture to play via **(B)
|
||||
component-state injection**: the instrument's `getChunk`/`setChunk` must round-trip a
|
||||
component-state blob whose byte layout is the **shared cross-artifact contract** (defined
|
||||
@@ -1043,9 +1076,27 @@ artifact, not extension-only.
|
||||
via `TrackFX_SetNamedConfigParm(..., "vst_chunk", ...)`. This is the cross-artifact half;
|
||||
it lands in the instrument, not the extension. **Coordination dependency:** the blob
|
||||
format must be agreed between extension and instrument before either half is final.
|
||||
- [ ] Tests: gesture disambiguation (inside-panel / over-REAPER-UI / left-REAPER) across
|
||||
**Landed (no NEW instrument code needed — the seam already exists):** the instrument's
|
||||
`setState`/`getState` already round-trip the full `ComponentState` via
|
||||
`sample_map::serializeComponentState`/`deserializeComponentState` (S10). The extension side
|
||||
REUSES that exact serializer through the new pure `instrument_drop` module
|
||||
(`buildInstrumentDropChunk` -> `serializeComponentState` -> base64) — the shared-writer
|
||||
requirement is met STRUCTURALLY (one serializer, called from both artifacts), so the blob
|
||||
format cannot drift. Pure round-trip test decodes back through the instrument's OWN reader
|
||||
(`deserializeComponentState`) and asserts the capture is selected. **DAW-VERIFY (the one
|
||||
live unknown):** whether REAPER's `vst_chunk` write-parm expects the plugin's raw
|
||||
IComponent-state bytes as we write them, or wraps them in a REAPER container header — the
|
||||
load-bearing caveat is only confirmable in the DAW; if REAPER wraps, the fix is to match
|
||||
its container, the shared serializer stays.
|
||||
- [x] Tests: gesture disambiguation (inside-panel / over-REAPER-UI / left-REAPER) across
|
||||
single- and multi-capture payloads; FX-button hit resolution (pure geometry where it
|
||||
can be factored out); M11 OS drag-out and internal bank-to-bank drag both unchanged.
|
||||
**Landed:** `test_drag_out.cpp` adds the InstrumentDrop cases (single over REAPER UI ->
|
||||
InstrumentDrop; single inside -> Internal; single off-REAPER -> OsDrag; multi over UI ->
|
||||
OsDrag; not-dragging short-circuits) with the M11 cases retained as the non-regression
|
||||
guard; `test_instrument_drop.cpp` is the blob round-trip + base64 codec coverage. FX hit
|
||||
resolution is REAPER-API-bound (`GetThingFromPoint`), so it is DAW-verified in the shell,
|
||||
not pure-tested — noted honestly.
|
||||
|
||||
## S18 — VST3 channel isolation: a beta ReaSampler 9000 that pairs with the beta extension only
|
||||
**Goal:** Extend Phase V's beta/stable channel split (V4 — the *extension* is fully
|
||||
|
||||
Reference in New Issue
Block a user