#pragma once #include "core/ui/rect.h" // drag_out — decision logic behind the bank_panel's native OS drag-out. OLE/SWELL initiation and // the panel gesture hook stay in the shell (drag_out_win.* + shell/panel/panel_drag.cpp). // // Gesture boundary: the panel's own internal drag (press a selected cell, drop onto a pool/bank // region or tab) lives entirely inside the panel client rect. The moment the pointer LEAVES that // rect while a drag is armed with samples, the gesture becomes OS-bound — dragged out to another // window/Explorer/DAW. A single-capture drag that leaves the rect but is still over REAPER's own // UI is instead an InstrumentDrop (heading for a track's FX button); do not regress this boundary. // // Path-list assembly: turns armed sample ids into the absolute path list the OS drop carries // (Windows CF_HDROP / macOS file-list pasteboard) — set algebra only; the shell resolves each id // to its on-disk bank file. No temp files; copy-only is enforced at the OS layer (drag_out_win). #include #include namespace reasampler::ui { // --- Gesture boundary --------------------------------------------------------- // The panel's client rect, own client coords, top-left origin. Half-open: [x, x+width) x // [y, y+height). using PanelClientRect = Rect; // Live drag state reduced to what the boundary decision needs. Pre-threshold "armed but not yet // dragging" is not a drag for this decision. struct DragState { bool dragging = false; // threshold crossed; a drag is in progress bool hasArmedSamples = false; // payload holds >= 1 sample id bool singleCapture = false; // payload holds EXACTLY one sample (arms InstrumentDrop) bool overReaperUi = false; // 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 Internal, // dragging inside the panel — bank-to-bank move/copy InstrumentDrop, // single-capture drag left the panel but is over REAPER's UI — shell // hover-tracks the TCP FX button; on release adds a preloaded instance OsDrag, // dragging with samples, pointer left REAPER entirely — hand to the OS }; // Decides the gesture for a drag at pointer (px, py) over `client`, given `state`. Position-only // + state-only (no hidden state), so re-entry back inside always returns Internal. DragGesture decideGesture(int px, int py, const PanelClientRect& client, const DragState& state); // --- Path-list assembly ------------------------------------------------------- // One armed sample reduced to what path assembly needs: the shell-resolved absolute path (empty // if unresolvable) and whether it exists on disk. struct ResolvedSample { std::string absolutePath; bool fileExists = false; }; // Outcome of assembling the drag's path list. An empty `paths` means nothing draggable — do not // start a drag. struct PathList { std::vector paths; // de-duped, existing files, first-seen order int skippedMissing = 0; // resolved but file doesn't exist (stale index entry) int skippedUnresolved = 0; // shell couldn't resolve a path at all int skippedDuplicate = 0; // same absolute path seen more than once }; // Assembles the drag path list from the resolved samples (selection order). Comparison is // exact-string — the shell normalizes case/slashes upstream if it wants Windows-style dedup. PathList assemblePathList(const std::vector& resolved); } // namespace reasampler::ui