RAM2118 commited on
Commit
2dc45d7
·
verified ·
1 Parent(s): e0f6b2a

fix: set fullscreen-compatible behavior BEFORE making window visible

Browse files

Previous 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).

Files changed (1) hide show
  1. Source/Helper/HelperPluginHost.cpp +32 -16
Source/Helper/HelperPluginHost.cpp CHANGED
@@ -9,37 +9,46 @@
9
  #include <objc/message.h>
10
  #endif
11
 
12
- // Show window as floating panel without activating this app or stealing focus
13
- // Works in BOTH windowed and fullscreen mode
14
- static void showAsFloatingPanel(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 (critical for fullscreen) ---
22
- // canJoinAllSpaces = 1 << 0 (window appears on ALL Spaces/desktops)
23
- // fullScreenAuxiliary = 1 << 8 (window can float over fullscreen apps)
24
- // Combined = 257
25
- long behavior = (1 << 0) | (1 << 8);
 
26
  ((void(*)(id, SEL, long))objc_msgSend)(nsWindow, sel_registerName("setCollectionBehavior:"), behavior);
27
 
28
  // --- WINDOW LEVEL ---
29
- // NSStatusWindowLevel = 25 (above fullscreen apps)
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 on deactivate (stay visible when helper loses focus)
34
  ((void(*)(id, SEL, int))objc_msgSend)(nsWindow, sel_registerName("setHidesOnDeactivate:"), 0);
 
 
 
35
 
36
- // Show without activating this app
 
 
 
 
 
 
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
- showAsFloatingPanel(guiWindow.get());
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
- // Make it float without stealing focus — works in windowed AND fullscreen
303
- showAsFloatingPanel(guiWindow.get());
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