b71a05fbef
OsHandoff now needs the window-ownership proof AND a hit-test that named nothing, so a bridged plugin's UI can't read as off-REAPER.
104 lines
3.9 KiB
C++
104 lines
3.9 KiB
C++
// drag_out — pure implementation. See drag_out.h.
|
|
|
|
#include "core/ui/drag_out.h"
|
|
|
|
#include <unordered_set>
|
|
|
|
namespace reasampler::ui {
|
|
|
|
namespace {
|
|
|
|
bool insideClient(int px, int py, const PanelClientRect& c) {
|
|
return px >= c.x && px < c.x + c.width &&
|
|
py >= c.y && py < c.y + c.height;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
bool needsSurfaceProbe(int px, int py, const PanelClientRect& client) {
|
|
return !insideClient(px, py, client);
|
|
}
|
|
|
|
DropClass decideDropClass(int px, int py, const PanelClientRect& client,
|
|
const DropContext& ctx) {
|
|
if (!ctx.drag.dragging || !ctx.drag.hasArmedSamples) return DropClass::None;
|
|
if (insideClient(px, py, client)) return DropClass::Internal;
|
|
|
|
// The ONE route to OsHandoff, ahead of the surface switch, and it needs BOTH halves. The
|
|
// shell's ownership proof alone is not sufficient: REAPER runs a bridged plugin's UI in a
|
|
// separate process, so a window failing that test can still be REAPER's own UI. A recognised
|
|
// token therefore VETOES the hand-off instead of producing one — a veto can only move the
|
|
// answer toward staying inside REAPER, so pointerOffHost remains the only member whose true
|
|
// value can reach OLE. An off-host point REAPER DID name falls through to that surface's own
|
|
// outcome below: over a bridged FX window that is the instrument drop, as it is for a native
|
|
// one.
|
|
if (ctx.pointerOffHost && ctx.surface == ReaperSurface::Other && !ctx.haveTrack)
|
|
return DropClass::OsHandoff;
|
|
|
|
switch (ctx.surface) {
|
|
case ReaperSurface::TrackPanel:
|
|
case ReaperSurface::FxSurface:
|
|
// One instance holds one capture, so a multi payload names no instrument to build —
|
|
// it refuses with a cue rather than falling through to some other surface's outcome.
|
|
return (ctx.singlePayload && ctx.haveTrack) ? DropClass::InstrumentDrop
|
|
: DropClass::Refuse;
|
|
|
|
case ReaperSurface::Arrange:
|
|
// GetThingFromPoint may return a null track with a valid info string (the SDK says
|
|
// so): over the arrange that is the region below the last track, which names no lane
|
|
// to place on. Refuse rather than guess a track.
|
|
return ctx.haveTrack ? DropClass::ArrangeInsert : DropClass::Refuse;
|
|
|
|
case ReaperSurface::FxEmbed:
|
|
case ReaperSurface::Other:
|
|
return DropClass::Refuse;
|
|
|
|
case ReaperSurface::Count:
|
|
break; // the sentinel is not a surface; it falls to the refusal below
|
|
}
|
|
return DropClass::Refuse; // an unclassifiable surface still refuses visibly, never silently
|
|
}
|
|
|
|
DropCue cueForDropClass(DropClass cls) {
|
|
switch (cls) {
|
|
case DropClass::Internal: return DropCue::Internal;
|
|
case DropClass::InstrumentDrop: return DropCue::Instrument;
|
|
case DropClass::ArrangeInsert: return DropCue::ArrangeInsert;
|
|
case DropClass::Refuse: return DropCue::Refuse;
|
|
case DropClass::OsHandoff: return DropCue::OsOwned;
|
|
case DropClass::None: return DropCue::None;
|
|
}
|
|
return DropCue::None;
|
|
}
|
|
|
|
PathList assemblePathList(const std::vector<ResolvedSample>& resolved) {
|
|
PathList out;
|
|
std::unordered_set<std::string> seen;
|
|
seen.reserve(resolved.size());
|
|
|
|
for (const ResolvedSample& s : resolved) {
|
|
if (s.absolutePath.empty()) {
|
|
++out.skippedUnresolved;
|
|
continue;
|
|
}
|
|
if (!s.fileExists) {
|
|
++out.skippedMissing;
|
|
continue;
|
|
}
|
|
if (!seen.insert(s.absolutePath).second) {
|
|
++out.skippedDuplicate;
|
|
continue;
|
|
}
|
|
out.paths.push_back(s.absolutePath);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
OsHandoff decideOsHandoff(const std::vector<std::string>& paths) {
|
|
OsHandoff out;
|
|
out.handOffToOs = !paths.empty();
|
|
return out;
|
|
}
|
|
|
|
} // namespace reasampler::ui
|