fix: set fullscreen-compatible behavior BEFORE making window visible
Browse filesPrevious fix set collection behavior after setVisible(true) which is
too late — macOS has already assigned the window to a Space.
Now sets canJoinAllSpaces + fullScreenAuxiliary + stationary (1<<4)
BEFORE the window becomes visible. Also uses NSPanel-level behavior
and moves window to active Space explicitly via orderWindow:relativeTo:.
If this still fails in fullscreen, the only remaining option is to
render the plugin GUI in-process (framebuffer sharing).
Source/Helper/HelperPluginHost.cpp
CHANGED
|
@@ -9,37 +9,46 @@
|
|
| 9 |
#include <objc/message.h>
|
| 10 |
#endif
|
| 11 |
|
| 12 |
-
//
|
| 13 |
-
//
|
| 14 |
-
static void
|
| 15 |
{
|
| 16 |
#if JUCE_MAC
|
| 17 |
if (auto* peer = window->getPeer())
|
| 18 |
{
|
| 19 |
auto* nsWindow = (id)peer->getNativeHandle();
|
| 20 |
|
| 21 |
-
// --- COLLECTION BEHAVIOR (
|
| 22 |
-
// canJoinAllSpaces
|
| 23 |
-
// fullScreenAuxiliary
|
| 24 |
-
//
|
| 25 |
-
|
|
|
|
| 26 |
((void(*)(id, SEL, long))objc_msgSend)(nsWindow, sel_registerName("setCollectionBehavior:"), behavior);
|
| 27 |
|
| 28 |
// --- WINDOW LEVEL ---
|
| 29 |
-
//
|
| 30 |
-
// NSFloatingWindowLevel = 3 is NOT enough — fullscreen apps are higher
|
| 31 |
((void(*)(id, SEL, long))objc_msgSend)(nsWindow, sel_registerName("setLevel:"), 25);
|
| 32 |
|
| 33 |
-
// Don't hide
|
| 34 |
((void(*)(id, SEL, int))objc_msgSend)(nsWindow, sel_registerName("setHidesOnDeactivate:"), 0);
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
((void(*)(id, SEL))objc_msgSend)(nsWindow, sel_registerName("orderFrontRegardless"));
|
| 38 |
}
|
| 39 |
#endif
|
| 40 |
}
|
| 41 |
|
| 42 |
-
// Custom window
|
| 43 |
class PluginGuiWindow : public juce::DocumentWindow
|
| 44 |
{
|
| 45 |
public:
|
|
@@ -285,8 +294,10 @@ void HelperPluginHost::showGui()
|
|
| 285 |
// If window exists but hidden, just show it
|
| 286 |
if (guiWindow)
|
| 287 |
{
|
|
|
|
|
|
|
| 288 |
guiWindow->setVisible(true);
|
| 289 |
-
|
| 290 |
return;
|
| 291 |
}
|
| 292 |
|
|
@@ -297,10 +308,15 @@ void HelperPluginHost::showGui()
|
|
| 297 |
guiWindow->setContentOwned(editor, true);
|
| 298 |
guiWindow->setResizable(true, true);
|
| 299 |
guiWindow->centreWithSize(editor->getWidth(), editor->getHeight());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
guiWindow->setVisible(true);
|
| 301 |
|
| 302 |
-
//
|
| 303 |
-
|
| 304 |
});
|
| 305 |
}
|
| 306 |
|
|
|
|
| 9 |
#include <objc/message.h>
|
| 10 |
#endif
|
| 11 |
|
| 12 |
+
// Configure window for floating-panel behavior that works in fullscreen
|
| 13 |
+
// MUST be called BEFORE the window is made visible
|
| 14 |
+
static void configureAsOverlayPanel(juce::Component* window)
|
| 15 |
{
|
| 16 |
#if JUCE_MAC
|
| 17 |
if (auto* peer = window->getPeer())
|
| 18 |
{
|
| 19 |
auto* nsWindow = (id)peer->getNativeHandle();
|
| 20 |
|
| 21 |
+
// --- COLLECTION BEHAVIOR (MUST be set before visible) ---
|
| 22 |
+
// canJoinAllSpaces = 1 << 0 (appear on all desktops/spaces)
|
| 23 |
+
// fullScreenAuxiliary = 1 << 8 (can appear over fullscreen apps)
|
| 24 |
+
// stationary = 1 << 4 (don't move with space switches)
|
| 25 |
+
// ignoresCycle = 1 << 6 (skip in Cmd+` cycling)
|
| 26 |
+
long behavior = (1 << 0) | (1 << 8) | (1 << 4) | (1 << 6);
|
| 27 |
((void(*)(id, SEL, long))objc_msgSend)(nsWindow, sel_registerName("setCollectionBehavior:"), behavior);
|
| 28 |
|
| 29 |
// --- WINDOW LEVEL ---
|
| 30 |
+
// Level 25 = NSStatusWindowLevel (above fullscreen apps)
|
|
|
|
| 31 |
((void(*)(id, SEL, long))objc_msgSend)(nsWindow, sel_registerName("setLevel:"), 25);
|
| 32 |
|
| 33 |
+
// Don't hide when the helper app loses focus
|
| 34 |
((void(*)(id, SEL, int))objc_msgSend)(nsWindow, sel_registerName("setHidesOnDeactivate:"), 0);
|
| 35 |
+
}
|
| 36 |
+
#endif
|
| 37 |
+
}
|
| 38 |
|
| 39 |
+
// Make the window appear without stealing focus (call AFTER visible)
|
| 40 |
+
static void showWithoutFocusSteal(juce::Component* window)
|
| 41 |
+
{
|
| 42 |
+
#if JUCE_MAC
|
| 43 |
+
if (auto* peer = window->getPeer())
|
| 44 |
+
{
|
| 45 |
+
auto* nsWindow = (id)peer->getNativeHandle();
|
| 46 |
((void(*)(id, SEL))objc_msgSend)(nsWindow, sel_registerName("orderFrontRegardless"));
|
| 47 |
}
|
| 48 |
#endif
|
| 49 |
}
|
| 50 |
|
| 51 |
+
// Custom window — no native title bar close issues with accessory policy
|
| 52 |
class PluginGuiWindow : public juce::DocumentWindow
|
| 53 |
{
|
| 54 |
public:
|
|
|
|
| 294 |
// If window exists but hidden, just show it
|
| 295 |
if (guiWindow)
|
| 296 |
{
|
| 297 |
+
// Re-apply overlay config before showing (in case Space changed)
|
| 298 |
+
configureAsOverlayPanel(guiWindow.get());
|
| 299 |
guiWindow->setVisible(true);
|
| 300 |
+
showWithoutFocusSteal(guiWindow.get());
|
| 301 |
return;
|
| 302 |
}
|
| 303 |
|
|
|
|
| 308 |
guiWindow->setContentOwned(editor, true);
|
| 309 |
guiWindow->setResizable(true, true);
|
| 310 |
guiWindow->centreWithSize(editor->getWidth(), editor->getHeight());
|
| 311 |
+
|
| 312 |
+
// CRITICAL: configure overlay behavior BEFORE making visible
|
| 313 |
+
configureAsOverlayPanel(guiWindow.get());
|
| 314 |
+
|
| 315 |
+
// NOW make visible
|
| 316 |
guiWindow->setVisible(true);
|
| 317 |
|
| 318 |
+
// Show without stealing focus from Ableton
|
| 319 |
+
showWithoutFocusSteal(guiWindow.get());
|
| 320 |
});
|
| 321 |
}
|
| 322 |
|