fix(drop): recognize TCP/MCP FX-button hotspot for instrument drop (S-VIEW-BUG-1)

The FX-hotspot classifier matched only fx_ (the FX-chain/floating windows),
so a drop on a track TCP FX button (info tcp*) never armed the drop and the
capture fell through to arrange as audio. Widen to fx_/tcp/mcp in a pure,
unit-tested predicate; shell calls it.
This commit is contained in:
2026-07-27 13:26:10 -04:00
parent 3b9b78b82c
commit fef595be91
4 changed files with 74 additions and 15 deletions
+42
View File
@@ -105,6 +105,44 @@ static void testBase64DecodeRejectsMalformed() {
CHECK(decodeBase64("Zg==Zg==").empty()); // interior padding (pad before the final quad)
}
// --- FX-hotspot classification (S-VIEW-BUG-1) ---------------------------------
//
// THE BUG: dropping a capture onto a track's FX button (in the TCP) never armed the
// instrument drop, because the old predicate matched only "fx_" — which the SDK reserves for
// the FX CHAIN / FLOATING-FX windows. A track-panel hit (where the FX button lives) reports a
// string beginning with "tcp"/"mcp". These tests fix the classifier at the boundary the shell
// consumes: each would FAIL under the old "fx_"-only rule for the tcp/mcp cases.
// The regression case: the TCP FX-button surface reports "tcp*" and MUST classify as an FX
// hotspot. Under the pre-fix "fx_"-only predicate these all returned false — the exact miss
// that produced the "drops as audio to arrange" symptom.
static void testTcpMcpPanelIsHotspot() {
CHECK(infoNamesFxHotspot("tcp")); // bare track control panel
CHECK(infoNamesFxHotspot("tcp.fx")); // the TCP FX-button WALTER element
CHECK(infoNamesFxHotspot("tcp.mute")); // any tcp.* sub-element resolves to the track
CHECK(infoNamesFxHotspot("mcp")); // mixer control panel
CHECK(infoNamesFxHotspot("mcp.fx")); // the MCP FX area
}
// The FX chain / floating-FX windows (the surfaces the ORIGINAL predicate matched) still
// classify as hotspots — the fix widens the rule, it does not regress the "fx_" surface.
static void testFxWindowStillHotspot() {
CHECK(infoNamesFxHotspot("fx_chain")); // FX chain window
CHECK(infoNamesFxHotspot("fx_0")); // first FX, floating
CHECK(infoNamesFxHotspot("fx_12")); // arbitrary floating-FX index
}
// Non-FX surfaces are NOT hotspots — a drop here is not an instrument drop (it would fall
// through to the OS drag / no-op). Guards against over-broad matching (e.g. "spacer_0" must
// not match despite living near the tracks; "arrange" is the audio-import surface).
static void testNonFxSurfacesAreNotHotspot() {
CHECK(!infoNamesFxHotspot("arrange"));
CHECK(!infoNamesFxHotspot("spacer_0"));
CHECK(!infoNamesFxHotspot("")); // pointer over nothing REAPER classifies
CHECK(!infoNamesFxHotspot("trans")); // transport
CHECK(!infoNamesFxHotspot("envcp")); // envelope control panel — a track thing, not FX
}
int main() {
testBlobRoundTripsThroughInstrumentReader();
testGuidLikeIdRoundTrips();
@@ -114,6 +152,10 @@ int main() {
testBase64RoundTripAllBytes();
testBase64DecodeRejectsMalformed();
testTcpMcpPanelIsHotspot();
testFxWindowStillHotspot();
testNonFxSurfacesAreNotHotspot();
if (g_fail == 0) std::printf("All tests passed.\n");
return g_fail ? 1 : 0;
}