Fix drag-out giving live drags to OLE: prove the pointer left REAPER
OffReaper leaves the surface vocabulary; OsHandoff now gates on a window-ownership proof resolved in the shell, so no in-REAPER surface can reach DoDragDrop.
This commit is contained in:
@@ -186,39 +186,39 @@ static void testBadClassIdRejected() {
|
||||
|
||||
using ui::ReaperSurface;
|
||||
|
||||
static ReaperSurface onTrack(const std::string& info) {
|
||||
return classifyReaperSurface(info, /*haveTrack=*/true);
|
||||
static ReaperSurface surfaceFor(const std::string& info) {
|
||||
return classifyReaperSurface(info);
|
||||
}
|
||||
|
||||
// The FX chain / floating-FX windows.
|
||||
static void testFxWindowIsFxSurface() {
|
||||
CHECK(onTrack("fx_chain") == ReaperSurface::FxSurface);
|
||||
CHECK(onTrack("fx_0") == ReaperSurface::FxSurface);
|
||||
CHECK(onTrack("fx_12") == ReaperSurface::FxSurface);
|
||||
CHECK(surfaceFor("fx_chain") == ReaperSurface::FxSurface);
|
||||
CHECK(surfaceFor("fx_0") == ReaperSurface::FxSurface);
|
||||
CHECK(surfaceFor("fx_12") == ReaperSurface::FxSurface);
|
||||
}
|
||||
|
||||
// The FX-button family within the TCP/MCP — the surface the glyph-only rule used to be limited
|
||||
// to, still an instrument surface (as TrackPanel, which resolves identically).
|
||||
static void testTcpMcpFxFamilyIsTrackPanel() {
|
||||
CHECK(onTrack("tcp.fx") == ReaperSurface::TrackPanel);
|
||||
CHECK(onTrack("mcp.fx") == ReaperSurface::TrackPanel);
|
||||
CHECK(onTrack("tcp.fxbyp") == ReaperSurface::TrackPanel);
|
||||
CHECK(onTrack("tcp.fxparm") == ReaperSurface::TrackPanel);
|
||||
CHECK(onTrack("mcp.fxlist") == ReaperSurface::TrackPanel);
|
||||
CHECK(onTrack("tcp.fx.1") == ReaperSurface::TrackPanel); // appended info
|
||||
CHECK(onTrack("tcp.fx extra") == ReaperSurface::TrackPanel); // appended info, arbitrary form
|
||||
CHECK(surfaceFor("tcp.fx") == ReaperSurface::TrackPanel);
|
||||
CHECK(surfaceFor("mcp.fx") == ReaperSurface::TrackPanel);
|
||||
CHECK(surfaceFor("tcp.fxbyp") == ReaperSurface::TrackPanel);
|
||||
CHECK(surfaceFor("tcp.fxparm") == ReaperSurface::TrackPanel);
|
||||
CHECK(surfaceFor("mcp.fxlist") == ReaperSurface::TrackPanel);
|
||||
CHECK(surfaceFor("tcp.fx.1") == ReaperSurface::TrackPanel); // appended info
|
||||
CHECK(surfaceFor("tcp.fx extra") == ReaperSurface::TrackPanel); // appended info, arbitrary form
|
||||
}
|
||||
|
||||
// THE ROOT-CAUSE FIX: the bare panel token and every non-FX sub-element are now the instrument
|
||||
// hotspot too. A TCP too narrow to draw the FX button reports "tcp", which under the old
|
||||
// glyph-only rule produced a cue-less no-op.
|
||||
static void testWholeTrackPanelIsTheHotspot() {
|
||||
CHECK(onTrack("tcp") == ReaperSurface::TrackPanel); // bare track control panel
|
||||
CHECK(onTrack("mcp") == ReaperSurface::TrackPanel); // bare mixer control panel
|
||||
CHECK(onTrack("tcp.mute") == ReaperSurface::TrackPanel); // mute button
|
||||
CHECK(onTrack("tcp.vol") == ReaperSurface::TrackPanel); // volume fader
|
||||
CHECK(onTrack("tcp.meter") == ReaperSurface::TrackPanel); // meter
|
||||
CHECK(onTrack("tcp.f") == ReaperSurface::TrackPanel); // truncated token — still the panel
|
||||
CHECK(surfaceFor("tcp") == ReaperSurface::TrackPanel); // bare track control panel
|
||||
CHECK(surfaceFor("mcp") == ReaperSurface::TrackPanel); // bare mixer control panel
|
||||
CHECK(surfaceFor("tcp.mute") == ReaperSurface::TrackPanel); // mute button
|
||||
CHECK(surfaceFor("tcp.vol") == ReaperSurface::TrackPanel); // volume fader
|
||||
CHECK(surfaceFor("tcp.meter") == ReaperSurface::TrackPanel); // meter
|
||||
CHECK(surfaceFor("tcp.f") == ReaperSurface::TrackPanel); // truncated token — still the panel
|
||||
}
|
||||
|
||||
// The embed strip is where a ReaSampler 9000 instance already draws inline via
|
||||
@@ -226,44 +226,42 @@ static void testWholeTrackPanelIsTheHotspot() {
|
||||
// instance on the first. Matched before the "tcp"/"mcp" rule, so widening the panel hotspot
|
||||
// cannot swallow it; do not reorder these two checks in the classifier.
|
||||
static void testEmbedStripIsItsOwnSurface() {
|
||||
CHECK(onTrack("tcp.fxembed") == ReaperSurface::FxEmbed);
|
||||
CHECK(onTrack("mcp.fxembed") == ReaperSurface::FxEmbed);
|
||||
CHECK(onTrack("tcp.fxembed.1") == ReaperSurface::FxEmbed);
|
||||
CHECK(onTrack("mcp.fxembed extra") == ReaperSurface::FxEmbed);
|
||||
CHECK(surfaceFor("tcp.fxembed") == ReaperSurface::FxEmbed);
|
||||
CHECK(surfaceFor("mcp.fxembed") == ReaperSurface::FxEmbed);
|
||||
CHECK(surfaceFor("tcp.fxembed.1") == ReaperSurface::FxEmbed);
|
||||
CHECK(surfaceFor("mcp.fxembed extra") == ReaperSurface::FxEmbed);
|
||||
}
|
||||
|
||||
// The arrange, including a token with appended information.
|
||||
static void testArrangeIsArrange() {
|
||||
CHECK(onTrack("arrange") == ReaperSurface::Arrange);
|
||||
CHECK(onTrack("arrange extra") == ReaperSurface::Arrange);
|
||||
CHECK(surfaceFor("arrange") == ReaperSurface::Arrange);
|
||||
CHECK(surfaceFor("arrange extra") == ReaperSurface::Arrange);
|
||||
}
|
||||
|
||||
// Anything else REAPER names is Other — a defined refusal, never a guessed outcome. Includes
|
||||
// tokens REAPER may add in future versions.
|
||||
static void testUnnamedReaperSurfacesAreOther() {
|
||||
CHECK(onTrack("spacer_0") == ReaperSurface::Other);
|
||||
CHECK(onTrack("trans") == ReaperSurface::Other);
|
||||
CHECK(onTrack("envcp") == ReaperSurface::Other);
|
||||
CHECK(onTrack("ruler") == ReaperSurface::Other);
|
||||
CHECK(onTrack("something_reaper_adds_in_2030") == ReaperSurface::Other);
|
||||
CHECK(surfaceFor("spacer_0") == ReaperSurface::Other);
|
||||
CHECK(surfaceFor("trans") == ReaperSurface::Other);
|
||||
CHECK(surfaceFor("envcp") == ReaperSurface::Other);
|
||||
CHECK(surfaceFor("ruler") == ReaperSurface::Other);
|
||||
CHECK(surfaceFor("something_reaper_adds_in_2030") == ReaperSurface::Other);
|
||||
}
|
||||
|
||||
// The empty info string splits on whether a track came back with it. No track means the pointer
|
||||
// has left REAPER (the OS hand-off's trigger); a track with no info means we are over REAPER on
|
||||
// a surface we cannot name, which must refuse rather than be treated as off-REAPER.
|
||||
static void testEmptyInfoSplitsOnTrackPresence() {
|
||||
CHECK(classifyReaperSurface("", /*haveTrack=*/false) == ReaperSurface::OffReaper);
|
||||
CHECK(classifyReaperSurface("", /*haveTrack=*/true) == ReaperSurface::Other);
|
||||
// An empty info string is Other, NOT an off-REAPER verdict. GetThingFromPoint documents no
|
||||
// off-REAPER return at all, so its silence over the transport, the toolbar or the docker chrome
|
||||
// says only "nothing I name" — reading it as "the user left REAPER" is what handed live drags to
|
||||
// OLE mid-gesture. Whether the pointer left REAPER is a window-ownership fact the shell proves
|
||||
// separately (drag_out_win::pointerOverHostWindow) and feeds to the law as
|
||||
// DropContext::pointerOffHost; no classifier output can produce a hand-off on its own.
|
||||
static void testEmptyInfoIsOtherNotOffReaper() {
|
||||
CHECK(surfaceFor("") == ReaperSurface::Other);
|
||||
}
|
||||
|
||||
// The SDK's documented null-track-with-valid-info case: the surface is read from the string
|
||||
// alone, so the classifier reports it faithfully and the gesture law decides what a missing
|
||||
// track means for that surface.
|
||||
static void testNullTrackStillClassifiesTheSurface() {
|
||||
CHECK(classifyReaperSurface("arrange", false) == ReaperSurface::Arrange);
|
||||
CHECK(classifyReaperSurface("tcp", false) == ReaperSurface::TrackPanel);
|
||||
CHECK(classifyReaperSurface("fx_chain", false) == ReaperSurface::FxSurface);
|
||||
}
|
||||
// There is deliberately no second empty-info case: track presence stopped being an input to the
|
||||
// classifier when the off-REAPER verdict left the vocabulary. "No classifier output can trigger a
|
||||
// hand-off" is now structural (no such member exists; test_drag_out's static_assert pins the
|
||||
// member count) rather than something a per-token loop could falsify.
|
||||
|
||||
// Do not reintroduce a per-surface "capture carries" loop test: buildInstrumentDropPreset takes
|
||||
// only sampleId (proven by testPresetRoundTripsThroughInstrumentReader), and per-surface
|
||||
@@ -324,8 +322,7 @@ int main() {
|
||||
testEmbedStripIsItsOwnSurface();
|
||||
testArrangeIsArrange();
|
||||
testUnnamedReaperSurfacesAreOther();
|
||||
testEmptyInfoSplitsOnTrackPresence();
|
||||
testNullTrackStillClassifiesTheSurface();
|
||||
testEmptyInfoIsOtherNotOffReaper();
|
||||
|
||||
testAddFailureLeavesNothingToRollBack();
|
||||
testPresetFailureRollsBackTheCreatedIndex();
|
||||
|
||||
Reference in New Issue
Block a user