The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

260 lines
7.8KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  21. static const auto nsViewFrameChangedSelector = @selector (frameChanged:);
  22. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  23. struct NSViewCallbackInterface
  24. {
  25. virtual ~NSViewCallbackInterface() = default;
  26. virtual void frameChanged() = 0;
  27. };
  28. //==============================================================================
  29. struct NSViewFrameChangeCallbackClass : public ObjCClass<NSObject>
  30. {
  31. NSViewFrameChangeCallbackClass()
  32. : ObjCClass ("JUCE_NSViewCallback_")
  33. {
  34. addIvar<NSViewCallbackInterface*> ("target");
  35. addMethod (nsViewFrameChangedSelector, frameChanged, "v@:@");
  36. registerClass();
  37. }
  38. static void setTarget (id self, NSViewCallbackInterface* c)
  39. {
  40. object_setInstanceVariable (self, "target", c);
  41. }
  42. private:
  43. static void frameChanged (id self, SEL, NSNotification*)
  44. {
  45. if (auto* target = getIvar<NSViewCallbackInterface*> (self, "target"))
  46. target->frameChanged();
  47. }
  48. JUCE_DECLARE_NON_COPYABLE (NSViewFrameChangeCallbackClass)
  49. };
  50. //==============================================================================
  51. class NSViewFrameWatcher : private NSViewCallbackInterface
  52. {
  53. public:
  54. NSViewFrameWatcher (NSView* viewToWatch, std::function<void()> viewResizedIn)
  55. : viewResized (std::move (viewResizedIn)), callback (makeCallbackForView (viewToWatch))
  56. {
  57. }
  58. ~NSViewFrameWatcher() override
  59. {
  60. [[NSNotificationCenter defaultCenter] removeObserver: callback];
  61. [callback release];
  62. callback = nil;
  63. }
  64. JUCE_DECLARE_NON_COPYABLE (NSViewFrameWatcher)
  65. JUCE_DECLARE_NON_MOVEABLE (NSViewFrameWatcher)
  66. private:
  67. id makeCallbackForView (NSView* view)
  68. {
  69. static NSViewFrameChangeCallbackClass cls;
  70. auto* result = [cls.createInstance() init];
  71. NSViewFrameChangeCallbackClass::setTarget (result, this);
  72. [[NSNotificationCenter defaultCenter] addObserver: result
  73. selector: nsViewFrameChangedSelector
  74. name: NSViewFrameDidChangeNotification
  75. object: view];
  76. return result;
  77. }
  78. void frameChanged() override { viewResized(); }
  79. std::function<void()> viewResized;
  80. id callback;
  81. };
  82. //==============================================================================
  83. class NSViewAttachment : public ReferenceCountedObject,
  84. public ComponentMovementWatcher
  85. {
  86. public:
  87. NSViewAttachment (NSView* v, Component& comp)
  88. : ComponentMovementWatcher (&comp),
  89. view (v), owner (comp),
  90. currentPeer (nullptr)
  91. {
  92. [view retain];
  93. [view setPostsFrameChangedNotifications: YES];
  94. updateAlpha();
  95. if (owner.isShowing())
  96. componentPeerChanged();
  97. }
  98. ~NSViewAttachment() override
  99. {
  100. removeFromParent();
  101. [view release];
  102. }
  103. void componentMovedOrResized (Component& comp, bool wasMoved, bool wasResized) override
  104. {
  105. ComponentMovementWatcher::componentMovedOrResized (comp, wasMoved, wasResized);
  106. // The ComponentMovementWatcher version of this method avoids calling
  107. // us when the top-level comp is resized, but if we're listening to the
  108. // top-level comp we still want the NSView to track its size.
  109. if (comp.isOnDesktop() && wasResized)
  110. componentMovedOrResized (wasMoved, wasResized);
  111. }
  112. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  113. {
  114. if (auto* peer = owner.getTopLevelComponent()->getPeer())
  115. {
  116. const auto newArea = peer->getAreaCoveredBy (owner);
  117. if (convertToRectInt ([view frame]) != newArea)
  118. [view setFrame: makeNSRect (newArea)];
  119. }
  120. }
  121. void componentPeerChanged() override
  122. {
  123. auto* peer = owner.getPeer();
  124. if (currentPeer != peer)
  125. {
  126. currentPeer = peer;
  127. if (peer != nullptr)
  128. {
  129. auto peerView = (NSView*) peer->getNativeHandle();
  130. [peerView addSubview: view];
  131. componentMovedOrResized (false, false);
  132. }
  133. else
  134. {
  135. removeFromParent();
  136. }
  137. }
  138. [view setHidden: ! owner.isShowing()];
  139. }
  140. void componentVisibilityChanged() override
  141. {
  142. componentPeerChanged();
  143. }
  144. void updateAlpha()
  145. {
  146. [view setAlphaValue: (CGFloat) owner.getAlpha()];
  147. }
  148. NSView* const view;
  149. using Ptr = ReferenceCountedObjectPtr<NSViewAttachment>;
  150. private:
  151. Component& owner;
  152. ComponentPeer* currentPeer;
  153. NSViewFrameWatcher frameWatcher { view, [this] { owner.childBoundsChanged (nullptr); } };
  154. void removeFromParent()
  155. {
  156. if ([view superview] != nil)
  157. [view removeFromSuperview]; // Must be careful not to call this unless it's required - e.g. some Apple AU views
  158. // override the call and use it as a sign that they're being deleted, which breaks everything..
  159. }
  160. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NSViewAttachment)
  161. };
  162. //==============================================================================
  163. NSViewComponent::NSViewComponent() = default;
  164. NSViewComponent::~NSViewComponent() = default;
  165. void NSViewComponent::setView (void* view)
  166. {
  167. if (view != getView())
  168. {
  169. auto old = attachment;
  170. attachment = nullptr;
  171. if (view != nullptr)
  172. attachment = attachViewToComponent (*this, view);
  173. old = nullptr;
  174. }
  175. }
  176. void* NSViewComponent::getView() const
  177. {
  178. return attachment != nullptr ? static_cast<NSViewAttachment*> (attachment.get())->view
  179. : nullptr;
  180. }
  181. void NSViewComponent::resizeToFitView()
  182. {
  183. if (attachment != nullptr)
  184. {
  185. auto* view = static_cast<NSViewAttachment*> (attachment.get())->view;
  186. auto r = [view frame];
  187. setBounds (Rectangle<int> ((int) r.size.width, (int) r.size.height));
  188. if (auto* peer = getTopLevelComponent()->getPeer())
  189. {
  190. const auto position = peer->getAreaCoveredBy (*this).getPosition();
  191. [view setFrameOrigin: convertToCGPoint (position)];
  192. }
  193. }
  194. }
  195. void NSViewComponent::paint (Graphics&) {}
  196. void NSViewComponent::alphaChanged()
  197. {
  198. if (attachment != nullptr)
  199. (static_cast<NSViewAttachment*> (attachment.get()))->updateAlpha();
  200. }
  201. ReferenceCountedObject* NSViewComponent::attachViewToComponent (Component& comp, void* view)
  202. {
  203. return new NSViewAttachment ((NSView*) view, comp);
  204. }
  205. } // namespace juce