|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2017 - ROLI Ltd.
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 5 End-User License
- Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
- 27th April 2017).
-
- End User License Agreement: www.juce.com/juce-5-licence
- Privacy Policy: www.juce.com/juce-5-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- //==============================================================================
- /**
- Creates a floating carbon window that can be used to hold a carbon UI.
-
- This is a handy class that's designed to be inlined where needed, e.g.
- in the audio plugin hosting code.
- */
- class CarbonViewWrapperComponent : public Component,
- public ComponentMovementWatcher,
- public Timer
- {
- public:
- CarbonViewWrapperComponent()
- : ComponentMovementWatcher (this),
- carbonWindow (nil),
- keepPluginWindowWhenHidden (false),
- wrapperWindow (nil),
- embeddedView (0),
- recursiveResize (false),
- repaintChildOnCreation (true)
- {
- }
-
- ~CarbonViewWrapperComponent()
- {
- jassert (embeddedView == 0); // must call deleteWindow() in the subclass's destructor!
- }
-
- virtual HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) = 0;
- virtual void removeView (HIViewRef embeddedView) = 0;
- virtual void handleMouseDown (int, int) {}
- virtual void handlePaint() {}
-
- virtual bool getEmbeddedViewSize (int& w, int& h)
- {
- if (embeddedView == 0)
- return false;
-
- HIRect bounds;
- HIViewGetBounds (embeddedView, &bounds);
- w = jmax (1, roundToInt (bounds.size.width));
- h = jmax (1, roundToInt (bounds.size.height));
- return true;
- }
-
- void createWindow()
- {
- if (wrapperWindow == nil)
- {
- Rect r;
- r.left = (short) getScreenX();
- r.top = (short) getScreenY();
- r.right = (short) (r.left + getWidth());
- r.bottom = (short) (r.top + getHeight());
-
- CreateNewWindow (kDocumentWindowClass,
- (WindowAttributes) (kWindowStandardHandlerAttribute | kWindowCompositingAttribute
- | kWindowNoShadowAttribute | kWindowNoTitleBarAttribute),
- &r, &wrapperWindow);
-
- jassert (wrapperWindow != 0);
- if (wrapperWindow == 0)
- return;
-
- carbonWindow = [[NSWindow alloc] initWithWindowRef: wrapperWindow];
-
- [getOwnerWindow() addChildWindow: carbonWindow
- ordered: NSWindowAbove];
-
- embeddedView = attachView (wrapperWindow, HIViewGetRoot (wrapperWindow));
-
- // Check for the plugin creating its own floating window, and if there is one,
- // we need to reparent it to make it visible..
- if (carbonWindow.childWindows.count > 0)
- if (NSWindow* floatingChildWindow = [[carbonWindow childWindows] objectAtIndex: 0])
- [getOwnerWindow() addChildWindow: floatingChildWindow
- ordered: NSWindowAbove];
-
- EventTypeSpec windowEventTypes[] =
- {
- { kEventClassWindow, kEventWindowGetClickActivation },
- { kEventClassWindow, kEventWindowHandleDeactivate },
- { kEventClassWindow, kEventWindowBoundsChanging },
- { kEventClassMouse, kEventMouseDown },
- { kEventClassMouse, kEventMouseMoved },
- { kEventClassMouse, kEventMouseDragged },
- { kEventClassMouse, kEventMouseUp },
- { kEventClassWindow, kEventWindowDrawContent },
- { kEventClassWindow, kEventWindowShown },
- { kEventClassWindow, kEventWindowHidden }
- };
-
- EventHandlerUPP upp = NewEventHandlerUPP (carbonEventCallback);
- InstallWindowEventHandler (wrapperWindow, upp,
- sizeof (windowEventTypes) / sizeof (EventTypeSpec),
- windowEventTypes, this, &eventHandlerRef);
-
- setOurSizeToEmbeddedViewSize();
- setEmbeddedWindowToOurSize();
-
- creationTime = Time::getCurrentTime();
- }
- }
-
- void deleteWindow()
- {
- removeView (embeddedView);
- embeddedView = 0;
-
- if (wrapperWindow != nil)
- {
- NSWindow* ownerWindow = getOwnerWindow();
-
- if ([[ownerWindow childWindows] count] > 0)
- {
- [ownerWindow removeChildWindow: carbonWindow];
- [carbonWindow close];
- }
-
- RemoveEventHandler (eventHandlerRef);
- DisposeWindow (wrapperWindow);
- wrapperWindow = nil;
- }
- }
-
- //==============================================================================
- void setOurSizeToEmbeddedViewSize()
- {
- int w, h;
- if (getEmbeddedViewSize (w, h))
- {
- if (w != getWidth() || h != getHeight())
- {
- startTimer (50);
- setSize (w, h);
-
- if (Component* p = getParentComponent())
- p->setSize (w, h);
- }
- else
- {
- startTimer (jlimit (50, 500, getTimerInterval() + 20));
- }
- }
- else
- {
- stopTimer();
- }
- }
-
- void setEmbeddedWindowToOurSize()
- {
- if (! recursiveResize)
- {
- recursiveResize = true;
-
- if (embeddedView != 0)
- {
- HIRect r;
- r.origin.x = 0;
- r.origin.y = 0;
- r.size.width = (float) getWidth();
- r.size.height = (float) getHeight();
- HIViewSetFrame (embeddedView, &r);
- }
-
- if (wrapperWindow != nil)
- {
- jassert (getTopLevelComponent()->getDesktopScaleFactor() == 1.0f);
- Rectangle<int> screenBounds (getScreenBounds() * Desktop::getInstance().getGlobalScaleFactor());
-
- Rect wr;
- wr.left = (short) screenBounds.getX();
- wr.top = (short) screenBounds.getY();
- wr.right = (short) screenBounds.getRight();
- wr.bottom = (short) screenBounds.getBottom();
-
- SetWindowBounds (wrapperWindow, kWindowContentRgn, &wr);
-
- // This group stuff is mainly a workaround for Mackie plugins like FinalMix..
- WindowGroupRef group = GetWindowGroup (wrapperWindow);
- WindowRef attachedWindow;
-
- if (GetIndexedWindow (group, 2, kWindowGroupContentsReturnWindows, &attachedWindow) == noErr)
- {
- SelectWindow (attachedWindow);
- ActivateWindow (attachedWindow, TRUE);
- HideWindow (wrapperWindow);
- }
-
- ShowWindow (wrapperWindow);
- }
-
- recursiveResize = false;
- }
- }
-
- void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
- {
- setEmbeddedWindowToOurSize();
- }
-
- // (overridden to intercept movements of the top-level window)
- void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized) override
- {
- ComponentMovementWatcher::componentMovedOrResized (component, wasMoved, wasResized);
-
- if (&component == getTopLevelComponent())
- setEmbeddedWindowToOurSize();
- }
-
- void componentPeerChanged() override
- {
- deleteWindow();
- createWindow();
- }
-
- void componentVisibilityChanged() override
- {
- if (isShowing())
- createWindow();
- else if (! keepPluginWindowWhenHidden)
- deleteWindow();
-
- setEmbeddedWindowToOurSize();
- }
-
- static void recursiveHIViewRepaint (HIViewRef view)
- {
- HIViewSetNeedsDisplay (view, true);
- HIViewRef child = HIViewGetFirstSubview (view);
-
- while (child != 0)
- {
- recursiveHIViewRepaint (child);
- child = HIViewGetNextView (child);
- }
- }
-
- void timerCallback() override
- {
- if (isShowing())
- {
- setOurSizeToEmbeddedViewSize();
-
- // To avoid strange overpainting problems when the UI is first opened, we'll
- // repaint it a few times during the first second that it's on-screen..
- if (repaintChildOnCreation && (Time::getCurrentTime() - creationTime).inMilliseconds() < 1000)
- recursiveHIViewRepaint (HIViewGetRoot (wrapperWindow));
- }
- }
-
- void setRepaintsChildHIViewWhenCreated (bool b) noexcept
- {
- repaintChildOnCreation = b;
- }
-
- OSStatus carbonEventHandler (EventHandlerCallRef /*nextHandlerRef*/, EventRef event)
- {
- switch (GetEventKind (event))
- {
- case kEventWindowHandleDeactivate:
- ActivateWindow (wrapperWindow, TRUE);
- return noErr;
-
- case kEventWindowGetClickActivation:
- {
- getTopLevelComponent()->toFront (false);
- [carbonWindow makeKeyAndOrderFront: nil];
-
- ClickActivationResult howToHandleClick = kActivateAndHandleClick;
-
- SetEventParameter (event, kEventParamClickActivation, typeClickActivationResult,
- sizeof (ClickActivationResult), &howToHandleClick);
-
- if (embeddedView != 0)
- HIViewSetNeedsDisplay (embeddedView, true);
-
- return noErr;
- }
- }
-
- return eventNotHandledErr;
- }
-
- static pascal OSStatus carbonEventCallback (EventHandlerCallRef nextHandlerRef, EventRef event, void* userData)
- {
- return ((CarbonViewWrapperComponent*) userData)->carbonEventHandler (nextHandlerRef, event);
- }
-
- NSWindow* carbonWindow;
- bool keepPluginWindowWhenHidden;
-
- protected:
- WindowRef wrapperWindow;
- HIViewRef embeddedView;
- bool recursiveResize, repaintChildOnCreation;
- Time creationTime;
-
- EventHandlerRef eventHandlerRef;
-
- NSWindow* getOwnerWindow() const { return [((NSView*) getWindowHandle()) window]; }
- };
-
- //==============================================================================
- // Non-public utility function that hosts can use if they need to get hold of the
- // internals of a carbon wrapper window..
- void* getCarbonWindow (Component* possibleCarbonComponent)
- {
- if (CarbonViewWrapperComponent* cv = dynamic_cast<CarbonViewWrapperComponent*> (possibleCarbonComponent))
- return cv->carbonWindow;
-
- return nullptr;
- }
-
- } // namespace juce
|