Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

juce_mac_CarbonViewWrapperComponent.h 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. //==============================================================================
  16. /**
  17. Creates a floating carbon window that can be used to hold a carbon UI.
  18. This is a handy class that's designed to be inlined where needed, e.g.
  19. in the audio plugin hosting code.
  20. @tags{GUI}
  21. */
  22. class CarbonViewWrapperComponent : public Component,
  23. public ComponentMovementWatcher,
  24. public Timer
  25. {
  26. public:
  27. CarbonViewWrapperComponent()
  28. : ComponentMovementWatcher (this),
  29. carbonWindow (nil),
  30. keepPluginWindowWhenHidden (false),
  31. wrapperWindow (nil),
  32. embeddedView (0),
  33. recursiveResize (false),
  34. repaintChildOnCreation (true)
  35. {
  36. }
  37. ~CarbonViewWrapperComponent()
  38. {
  39. jassert (embeddedView == 0); // must call deleteWindow() in the subclass's destructor!
  40. }
  41. virtual HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) = 0;
  42. virtual void removeView (HIViewRef embeddedView) = 0;
  43. virtual void handleMouseDown (int, int) {}
  44. virtual void handlePaint() {}
  45. virtual bool getEmbeddedViewSize (int& w, int& h)
  46. {
  47. if (embeddedView == 0)
  48. return false;
  49. HIRect bounds;
  50. HIViewGetBounds (embeddedView, &bounds);
  51. w = jmax (1, roundToInt (bounds.size.width));
  52. h = jmax (1, roundToInt (bounds.size.height));
  53. return true;
  54. }
  55. void createWindow()
  56. {
  57. if (wrapperWindow == nil)
  58. {
  59. Rect r;
  60. r.left = (short) getScreenX();
  61. r.top = (short) getScreenY();
  62. r.right = (short) (r.left + getWidth());
  63. r.bottom = (short) (r.top + getHeight());
  64. CreateNewWindow (kDocumentWindowClass,
  65. (WindowAttributes) (kWindowStandardHandlerAttribute | kWindowCompositingAttribute
  66. | kWindowNoShadowAttribute | kWindowNoTitleBarAttribute),
  67. &r, &wrapperWindow);
  68. jassert (wrapperWindow != 0);
  69. if (wrapperWindow == 0)
  70. return;
  71. carbonWindow = [[NSWindow alloc] initWithWindowRef: wrapperWindow];
  72. [getOwnerWindow() addChildWindow: carbonWindow
  73. ordered: NSWindowAbove];
  74. embeddedView = attachView (wrapperWindow, HIViewGetRoot (wrapperWindow));
  75. // Check for the plugin creating its own floating window, and if there is one,
  76. // we need to reparent it to make it visible..
  77. if (carbonWindow.childWindows.count > 0)
  78. if (NSWindow* floatingChildWindow = [[carbonWindow childWindows] objectAtIndex: 0])
  79. [getOwnerWindow() addChildWindow: floatingChildWindow
  80. ordered: NSWindowAbove];
  81. EventTypeSpec windowEventTypes[] =
  82. {
  83. { kEventClassWindow, kEventWindowGetClickActivation },
  84. { kEventClassWindow, kEventWindowHandleDeactivate },
  85. { kEventClassWindow, kEventWindowBoundsChanging },
  86. { kEventClassMouse, kEventMouseDown },
  87. { kEventClassMouse, kEventMouseMoved },
  88. { kEventClassMouse, kEventMouseDragged },
  89. { kEventClassMouse, kEventMouseUp },
  90. { kEventClassWindow, kEventWindowDrawContent },
  91. { kEventClassWindow, kEventWindowShown },
  92. { kEventClassWindow, kEventWindowHidden }
  93. };
  94. EventHandlerUPP upp = NewEventHandlerUPP (carbonEventCallback);
  95. InstallWindowEventHandler (wrapperWindow, upp,
  96. sizeof (windowEventTypes) / sizeof (EventTypeSpec),
  97. windowEventTypes, this, &eventHandlerRef);
  98. setOurSizeToEmbeddedViewSize();
  99. setEmbeddedWindowToOurSize();
  100. creationTime = Time::getCurrentTime();
  101. }
  102. }
  103. void deleteWindow()
  104. {
  105. removeView (embeddedView);
  106. embeddedView = 0;
  107. if (wrapperWindow != nil)
  108. {
  109. NSWindow* ownerWindow = getOwnerWindow();
  110. if ([[ownerWindow childWindows] count] > 0)
  111. {
  112. [ownerWindow removeChildWindow: carbonWindow];
  113. [carbonWindow close];
  114. }
  115. RemoveEventHandler (eventHandlerRef);
  116. DisposeWindow (wrapperWindow);
  117. wrapperWindow = nil;
  118. }
  119. }
  120. //==============================================================================
  121. void setOurSizeToEmbeddedViewSize()
  122. {
  123. int w, h;
  124. if (getEmbeddedViewSize (w, h))
  125. {
  126. if (w != getWidth() || h != getHeight())
  127. {
  128. startTimer (50);
  129. setSize (w, h);
  130. if (Component* p = getParentComponent())
  131. p->setSize (w, h);
  132. }
  133. else
  134. {
  135. startTimer (jlimit (50, 500, getTimerInterval() + 20));
  136. }
  137. }
  138. else
  139. {
  140. stopTimer();
  141. }
  142. }
  143. void setEmbeddedWindowToOurSize()
  144. {
  145. if (! recursiveResize)
  146. {
  147. recursiveResize = true;
  148. if (embeddedView != 0)
  149. {
  150. HIRect r;
  151. r.origin.x = 0;
  152. r.origin.y = 0;
  153. r.size.width = (float) getWidth();
  154. r.size.height = (float) getHeight();
  155. HIViewSetFrame (embeddedView, &r);
  156. }
  157. if (wrapperWindow != nil)
  158. {
  159. jassert (getTopLevelComponent()->getDesktopScaleFactor() == 1.0f);
  160. Rectangle<int> screenBounds (getScreenBounds() * Desktop::getInstance().getGlobalScaleFactor());
  161. Rect wr;
  162. wr.left = (short) screenBounds.getX();
  163. wr.top = (short) screenBounds.getY();
  164. wr.right = (short) screenBounds.getRight();
  165. wr.bottom = (short) screenBounds.getBottom();
  166. SetWindowBounds (wrapperWindow, kWindowContentRgn, &wr);
  167. // This group stuff is mainly a workaround for Mackie plugins like FinalMix..
  168. WindowGroupRef group = GetWindowGroup (wrapperWindow);
  169. WindowRef attachedWindow;
  170. if (GetIndexedWindow (group, 2, kWindowGroupContentsReturnWindows, &attachedWindow) == noErr)
  171. {
  172. SelectWindow (attachedWindow);
  173. ActivateWindow (attachedWindow, TRUE);
  174. HideWindow (wrapperWindow);
  175. }
  176. ShowWindow (wrapperWindow);
  177. }
  178. recursiveResize = false;
  179. }
  180. }
  181. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  182. {
  183. setEmbeddedWindowToOurSize();
  184. }
  185. // (overridden to intercept movements of the top-level window)
  186. void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized) override
  187. {
  188. ComponentMovementWatcher::componentMovedOrResized (component, wasMoved, wasResized);
  189. if (&component == getTopLevelComponent())
  190. setEmbeddedWindowToOurSize();
  191. }
  192. void componentPeerChanged() override
  193. {
  194. deleteWindow();
  195. createWindow();
  196. }
  197. void componentVisibilityChanged() override
  198. {
  199. if (isShowing())
  200. createWindow();
  201. else if (! keepPluginWindowWhenHidden)
  202. deleteWindow();
  203. setEmbeddedWindowToOurSize();
  204. }
  205. static void recursiveHIViewRepaint (HIViewRef view)
  206. {
  207. HIViewSetNeedsDisplay (view, true);
  208. HIViewRef child = HIViewGetFirstSubview (view);
  209. while (child != 0)
  210. {
  211. recursiveHIViewRepaint (child);
  212. child = HIViewGetNextView (child);
  213. }
  214. }
  215. void timerCallback() override
  216. {
  217. if (isShowing())
  218. {
  219. setOurSizeToEmbeddedViewSize();
  220. // To avoid strange overpainting problems when the UI is first opened, we'll
  221. // repaint it a few times during the first second that it's on-screen..
  222. if (repaintChildOnCreation && (Time::getCurrentTime() - creationTime).inMilliseconds() < 1000)
  223. recursiveHIViewRepaint (HIViewGetRoot (wrapperWindow));
  224. }
  225. }
  226. void setRepaintsChildHIViewWhenCreated (bool b) noexcept
  227. {
  228. repaintChildOnCreation = b;
  229. }
  230. OSStatus carbonEventHandler (EventHandlerCallRef /*nextHandlerRef*/, EventRef event)
  231. {
  232. switch (GetEventKind (event))
  233. {
  234. case kEventWindowHandleDeactivate:
  235. ActivateWindow (wrapperWindow, TRUE);
  236. return noErr;
  237. case kEventWindowGetClickActivation:
  238. {
  239. getTopLevelComponent()->toFront (false);
  240. [carbonWindow makeKeyAndOrderFront: nil];
  241. ClickActivationResult howToHandleClick = kActivateAndHandleClick;
  242. SetEventParameter (event, kEventParamClickActivation, typeClickActivationResult,
  243. sizeof (ClickActivationResult), &howToHandleClick);
  244. if (embeddedView != 0)
  245. HIViewSetNeedsDisplay (embeddedView, true);
  246. return noErr;
  247. }
  248. }
  249. return eventNotHandledErr;
  250. }
  251. static pascal OSStatus carbonEventCallback (EventHandlerCallRef nextHandlerRef, EventRef event, void* userData)
  252. {
  253. return ((CarbonViewWrapperComponent*) userData)->carbonEventHandler (nextHandlerRef, event);
  254. }
  255. NSWindow* carbonWindow;
  256. bool keepPluginWindowWhenHidden;
  257. protected:
  258. WindowRef wrapperWindow;
  259. HIViewRef embeddedView;
  260. bool recursiveResize, repaintChildOnCreation;
  261. Time creationTime;
  262. EventHandlerRef eventHandlerRef;
  263. NSWindow* getOwnerWindow() const { return [((NSView*) getWindowHandle()) window]; }
  264. };
  265. //==============================================================================
  266. // Non-public utility function that hosts can use if they need to get hold of the
  267. // internals of a carbon wrapper window..
  268. void* getCarbonWindow (Component* possibleCarbonComponent)
  269. {
  270. if (CarbonViewWrapperComponent* cv = dynamic_cast<CarbonViewWrapperComponent*> (possibleCarbonComponent))
  271. return cv->carbonWindow;
  272. return nullptr;
  273. }
  274. } // namespace juce