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