diff --git a/modules/juce_gui_extra/embedding/juce_AndroidViewComponent.h b/modules/juce_gui_extra/embedding/juce_AndroidViewComponent.h index 9e8285329d..5257371980 100644 --- a/modules/juce_gui_extra/embedding/juce_AndroidViewComponent.h +++ b/modules/juce_gui_extra/embedding/juce_AndroidViewComponent.h @@ -30,7 +30,7 @@ namespace juce moved and resized to follow the movements of this component. Of course, since the view is a native object, it'll obliterate any - juce components that may overlap this component, but that's life. + JUCE components that may overlap this component, but that's life. @tags{GUI} */ diff --git a/modules/juce_gui_extra/embedding/juce_HWNDComponent.h b/modules/juce_gui_extra/embedding/juce_HWNDComponent.h new file mode 100644 index 0000000000..216ab21eea --- /dev/null +++ b/modules/juce_gui_extra/embedding/juce_HWNDComponent.h @@ -0,0 +1,84 @@ +/* + ============================================================================== + + 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 +{ + +#if JUCE_WINDOWS || DOXYGEN + +//============================================================================== +/** + A Windows-specific class that can create and embed a HWND inside itself. + + To use it, create one of these, put it in place and make sure it's visible in a + window, then use setHWND() to assign a HWND to it. The window will then be + moved and resized to follow the movements of this component. + + Of course, since the window is a native object, it'll obliterate any + JUCE components that may overlap this component, but that's life. + + @tags{GUI} +*/ +class JUCE_API HWNDComponent : public Component +{ +public: + //============================================================================== + /** Create an initially-empty container. */ + HWNDComponent(); + + /** Destructor. */ + ~HWNDComponent() override; + + /** Assigns a HWND to this peer. + + The window will be retained and released by this component for as long as + it is needed. To remove the current HWND, just call setHWND (nullptr). + + Note: A void* is used here to avoid including the Windows headers as + part of JuceHeader.h, but the method expects a HWND. + */ + void setHWND (void* hwnd); + + /** Returns the current HWND. + + Note: A void* is returned here to avoid the needing to include the Windows + headers, so you should just cast the return value to a HWND. + */ + void* getHWND() const; + + /** Resizes this component to fit the HWND that it contains. */ + void resizeToFit(); + +private: + class Pimpl; + std::unique_ptr pimpl; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HWNDComponent) +}; + +#endif + +} // namespace juce diff --git a/modules/juce_gui_extra/embedding/juce_NSViewComponent.h b/modules/juce_gui_extra/embedding/juce_NSViewComponent.h index 4c33c37577..6396d64739 100644 --- a/modules/juce_gui_extra/embedding/juce_NSViewComponent.h +++ b/modules/juce_gui_extra/embedding/juce_NSViewComponent.h @@ -30,7 +30,7 @@ namespace juce moved and resized to follow the movements of this component. Of course, since the view is a native object, it'll obliterate any - juce components that may overlap this component, but that's life. + JUCE components that may overlap this component, but that's life. @tags{GUI} */ @@ -50,7 +50,7 @@ public: it is needed. To remove the current view, just call setView (nullptr). Note: A void* is used here to avoid including the cocoa headers as - part of the juce.h, but the method expects an NSView*. + part of JuceHeader.h, but the method expects an NSView*. */ void setView (void* nsView); @@ -61,7 +61,6 @@ public: */ void* getView() const; - /** Resizes this component to fit the view that it contains. */ void resizeToFitView(); diff --git a/modules/juce_gui_extra/embedding/juce_UIViewComponent.h b/modules/juce_gui_extra/embedding/juce_UIViewComponent.h index 466f88d776..8f0391b3d3 100644 --- a/modules/juce_gui_extra/embedding/juce_UIViewComponent.h +++ b/modules/juce_gui_extra/embedding/juce_UIViewComponent.h @@ -30,7 +30,7 @@ namespace juce moved and resized to follow the movements of this component. Of course, since the view is a native object, it'll obliterate any - juce components that may overlap this component, but that's life. + JUCE components that may overlap this component, but that's life. @tags{GUI} */ @@ -50,7 +50,7 @@ public: it is needed. To remove the current view, just call setView (nullptr). Note: A void* is used here to avoid including the cocoa headers as - part of the juce.h, but the method expects an UIView*. + part of JuceHeader.h, but the method expects an UIView*. */ void setView (void* uiView); @@ -61,7 +61,6 @@ public: */ void* getView() const; - /** Resizes this component to fit the view that it contains. */ void resizeToFitView(); diff --git a/modules/juce_gui_extra/juce_gui_extra.cpp b/modules/juce_gui_extra/juce_gui_extra.cpp index b4b02575e8..fbd8152cf1 100644 --- a/modules/juce_gui_extra/juce_gui_extra.cpp +++ b/modules/juce_gui_extra/juce_gui_extra.cpp @@ -165,6 +165,7 @@ //============================================================================== #elif JUCE_WINDOWS #include "native/juce_win32_ActiveXComponent.cpp" + #include "native/juce_win32_HWNDComponent.cpp" #if JUCE_WEB_BROWSER #include "native/juce_win32_WebBrowserComponent.cpp" #endif diff --git a/modules/juce_gui_extra/juce_gui_extra.h b/modules/juce_gui_extra/juce_gui_extra.h index 143f2bf2a5..10cc461129 100644 --- a/modules/juce_gui_extra/juce_gui_extra.h +++ b/modules/juce_gui_extra/juce_gui_extra.h @@ -98,6 +98,7 @@ #include "embedding/juce_UIViewComponent.h" #include "embedding/juce_XEmbedComponent.h" #include "embedding/juce_ScopedDPIAwarenessDisabler.h" +#include "embedding/juce_HWNDComponent.h" #include "misc/juce_AppleRemote.h" #include "misc/juce_BubbleMessageComponent.h" #include "misc/juce_ColourSelector.h" diff --git a/modules/juce_gui_extra/native/juce_win32_HWNDComponent.cpp b/modules/juce_gui_extra/native/juce_win32_HWNDComponent.cpp new file mode 100644 index 0000000000..548ddb9adc --- /dev/null +++ b/modules/juce_gui_extra/native/juce_win32_HWNDComponent.cpp @@ -0,0 +1,169 @@ +/* + ============================================================================== + + 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 +{ + +class HWNDComponent::Pimpl : public ComponentMovementWatcher +{ +public: + Pimpl (HWND h, Component& comp) + : ComponentMovementWatcher (&comp), + hwnd (h), + owner (comp) + { + if (owner.isShowing()) + componentPeerChanged(); + } + + ~Pimpl() override + { + removeFromParent(); + DestroyWindow (hwnd); + } + + void componentMovedOrResized (bool wasMoved, bool wasResized) override + { + auto* topComponent = owner.getTopLevelComponent(); + + if (auto* peer = owner.getPeer()) + { + auto pos = topComponent->getLocalPoint (&owner, Point()); + + auto scaled = (Rectangle (pos.x, pos.y, owner.getWidth(), owner.getHeight()).toDouble() + * peer->getPlatformScaleFactor()).getSmallestIntegerContainer(); + + DWORD windowFlags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER; + if (! wasMoved) windowFlags |= SWP_NOMOVE; + if (! wasResized) windowFlags |= SWP_NOSIZE; + + SetWindowPos (hwnd, 0, scaled.getX(), scaled.getY(), scaled.getWidth(), scaled.getHeight(), windowFlags); + } + } + + void componentPeerChanged() override + { + auto* peer = owner.getPeer(); + + if (currentPeer != peer) + { + removeFromParent(); + currentPeer = peer; + + addToParent(); + } + + auto isShowing = owner.isShowing(); + + ShowWindow (hwnd, isShowing ? SW_SHOWNA : SW_HIDE); + + if (isShowing) + InvalidateRect (hwnd, 0, 0); + } + + void componentVisibilityChanged() override + { + componentPeerChanged(); + } + + void componentBroughtToFront (Component& comp) override + { + ComponentMovementWatcher::componentBroughtToFront (comp); + } + + Rectangle getHWNDBounds() const + { + if (auto* peer = owner.getPeer()) + { + RECT r; + GetWindowRect (hwnd, &r); + + return (Rectangle::leftTopRightBottom (r.left, r.top, r.right, r.bottom).toDouble() + / peer->getPlatformScaleFactor()).getSmallestIntegerContainer(); + } + + return {}; + } + + HWND hwnd; + +private: + void addToParent() + { + if (currentPeer != nullptr) + { + auto windowFlags = GetWindowLongPtr (hwnd, -16); + + windowFlags &= ~(WS_POPUP | WS_CHILD); + + SetWindowLongPtr (hwnd, -16, windowFlags); + SetParent (hwnd, (HWND) currentPeer->getNativeHandle()); + + componentMovedOrResized (true, true); + } + } + + void removeFromParent() + { + SetParent (hwnd, NULL); + } + + Component& owner; + ComponentPeer* currentPeer = nullptr; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) +}; + +//============================================================================== +HWNDComponent::HWNDComponent() +{ +} + +HWNDComponent::~HWNDComponent() {} + +void HWNDComponent::setHWND (void* hwnd) +{ + if (hwnd != getHWND()) + { + pimpl.reset(); + + if (hwnd != nullptr) + pimpl.reset (new Pimpl ((HWND) hwnd, *this)); + } +} + +void* HWNDComponent::getHWND() const +{ + return pimpl == nullptr ? nullptr : (void*) pimpl->hwnd; +} + +void HWNDComponent::resizeToFit() +{ + if (pimpl != nullptr) + setBounds (pimpl->getHWNDBounds()); +} + +} // namespace juce