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.

254 lines
7.5KB

  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. struct NSViewResizeWatcher
  21. {
  22. NSViewResizeWatcher() : callback (nil) {}
  23. virtual ~NSViewResizeWatcher()
  24. {
  25. // must call detachViewWatcher() first
  26. jassert (callback == nil);
  27. }
  28. void attachViewWatcher (NSView* view)
  29. {
  30. static ViewFrameChangeCallbackClass cls;
  31. callback = [cls.createInstance() init];
  32. ViewFrameChangeCallbackClass::setTarget (callback, this);
  33. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  34. [[NSNotificationCenter defaultCenter] addObserver: callback
  35. selector: @selector (frameChanged:)
  36. name: NSViewFrameDidChangeNotification
  37. object: view];
  38. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  39. }
  40. void detachViewWatcher()
  41. {
  42. if (callback != nil)
  43. {
  44. [[NSNotificationCenter defaultCenter] removeObserver: callback];
  45. [callback release];
  46. callback = nil;
  47. }
  48. }
  49. virtual void viewResized() = 0;
  50. private:
  51. id callback;
  52. //==============================================================================
  53. struct ViewFrameChangeCallbackClass : public ObjCClass<NSObject>
  54. {
  55. ViewFrameChangeCallbackClass() : ObjCClass<NSObject> ("JUCE_NSViewCallback_")
  56. {
  57. addIvar<NSViewResizeWatcher*> ("target");
  58. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  59. addMethod (@selector (frameChanged:), frameChanged, "v@:@");
  60. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  61. registerClass();
  62. }
  63. static void setTarget (id self, NSViewResizeWatcher* c)
  64. {
  65. object_setInstanceVariable (self, "target", c);
  66. }
  67. private:
  68. static void frameChanged (id self, SEL, NSNotification*)
  69. {
  70. if (auto* target = getIvar<NSViewResizeWatcher*> (self, "target"))
  71. target->viewResized();
  72. }
  73. JUCE_DECLARE_NON_COPYABLE (ViewFrameChangeCallbackClass)
  74. };
  75. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NSViewResizeWatcher)
  76. };
  77. //==============================================================================
  78. class NSViewAttachment : public ReferenceCountedObject,
  79. public ComponentMovementWatcher,
  80. private NSViewResizeWatcher
  81. {
  82. public:
  83. NSViewAttachment (NSView* v, Component& comp)
  84. : ComponentMovementWatcher (&comp),
  85. view (v), owner (comp),
  86. currentPeer (nullptr)
  87. {
  88. [view retain];
  89. [view setPostsFrameChangedNotifications: YES];
  90. updateAlpha();
  91. if (owner.isShowing())
  92. componentPeerChanged();
  93. attachViewWatcher (view);
  94. }
  95. ~NSViewAttachment() override
  96. {
  97. detachViewWatcher();
  98. removeFromParent();
  99. [view release];
  100. }
  101. void componentMovedOrResized (Component& comp, bool wasMoved, bool wasResized) override
  102. {
  103. ComponentMovementWatcher::componentMovedOrResized (comp, wasMoved, wasResized);
  104. // The ComponentMovementWatcher version of this method avoids calling
  105. // us when the top-level comp is resized, but for an NSView we need to know this
  106. // because with inverted coordinates, we need to update the position even if the
  107. // top-left pos hasn't changed
  108. if (comp.isOnDesktop() && wasResized)
  109. componentMovedOrResized (wasMoved, wasResized);
  110. }
  111. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  112. {
  113. if (auto* peer = owner.getTopLevelComponent()->getPeer())
  114. {
  115. auto r = makeNSRect (peer->getAreaCoveredBy (owner));
  116. r.origin.y = [[view superview] frame].size.height - (r.origin.y + r.size.height);
  117. [view setFrame: r];
  118. }
  119. }
  120. void componentPeerChanged() override
  121. {
  122. auto* peer = owner.getPeer();
  123. if (currentPeer != peer)
  124. {
  125. currentPeer = peer;
  126. if (peer != nullptr)
  127. {
  128. auto peerView = (NSView*) peer->getNativeHandle();
  129. [peerView addSubview: view];
  130. componentMovedOrResized (false, false);
  131. }
  132. else
  133. {
  134. removeFromParent();
  135. }
  136. }
  137. [view setHidden: ! owner.isShowing()];
  138. }
  139. void componentVisibilityChanged() override
  140. {
  141. componentPeerChanged();
  142. }
  143. void viewResized() override
  144. {
  145. owner.childBoundsChanged (nullptr);
  146. }
  147. void updateAlpha()
  148. {
  149. [view setAlphaValue: (CGFloat) owner.getAlpha()];
  150. }
  151. NSView* const view;
  152. using Ptr = ReferenceCountedObjectPtr<NSViewAttachment>;
  153. private:
  154. Component& owner;
  155. ComponentPeer* currentPeer;
  156. void removeFromParent()
  157. {
  158. if ([view superview] != nil)
  159. [view removeFromSuperview]; // Must be careful not to call this unless it's required - e.g. some Apple AU views
  160. // override the call and use it as a sign that they're being deleted, which breaks everything..
  161. }
  162. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NSViewAttachment)
  163. };
  164. //==============================================================================
  165. NSViewComponent::NSViewComponent() {}
  166. NSViewComponent::~NSViewComponent() {}
  167. void NSViewComponent::setView (void* view)
  168. {
  169. if (view != getView())
  170. {
  171. auto old = attachment;
  172. attachment = nullptr;
  173. if (view != nullptr)
  174. attachment = attachViewToComponent (*this, view);
  175. old = nullptr;
  176. }
  177. }
  178. void* NSViewComponent::getView() const
  179. {
  180. return attachment != nullptr ? static_cast<NSViewAttachment*> (attachment.get())->view
  181. : nullptr;
  182. }
  183. void NSViewComponent::resizeToFitView()
  184. {
  185. if (attachment != nullptr)
  186. {
  187. auto r = [static_cast<NSViewAttachment*> (attachment.get())->view frame];
  188. setBounds (Rectangle<int> ((int) r.size.width, (int) r.size.height));
  189. }
  190. }
  191. void NSViewComponent::paint (Graphics&) {}
  192. void NSViewComponent::alphaChanged()
  193. {
  194. if (attachment != nullptr)
  195. (static_cast<NSViewAttachment*> (attachment.get()))->updateAlpha();
  196. }
  197. ReferenceCountedObject* NSViewComponent::attachViewToComponent (Component& comp, void* view)
  198. {
  199. return new NSViewAttachment ((NSView*) view, comp);
  200. }
  201. } // namespace juce