JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_DropShadower.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - 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 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-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
30{
31public:
32 ShadowWindow (Component* comp, const DropShadow& ds)
33 : target (comp), shadow (ds)
34 {
35 setVisible (true);
36 setAccessible (false);
37 setInterceptsMouseClicks (false, false);
38
39 if (comp->isOnDesktop())
40 {
41 #if JUCE_WINDOWS
42 const auto scope = [&]() -> std::unique_ptr<ScopedThreadDPIAwarenessSetter>
43 {
44 if (comp != nullptr)
45 if (auto* handle = comp->getWindowHandle())
46 return std::make_unique<ScopedThreadDPIAwarenessSetter> (handle);
47
48 return nullptr;
49 }();
50 #endif
51
52 setSize (1, 1); // to keep the OS happy by not having zero-size windows
56 }
57 else if (Component* const parent = comp->getParentComponent())
58 {
59 parent->addChildComponent (this);
60 }
61 }
62
63 void paint (Graphics& g) override
64 {
65 if (Component* c = target)
66 shadow.drawForRectangle (g, getLocalArea (c, c->getLocalBounds()));
67 }
68
69 void resized() override
70 {
71 repaint(); // (needed for correct repainting)
72 }
73
74 float getDesktopScaleFactor() const override
75 {
76 if (target != nullptr)
77 return target->getDesktopScaleFactor();
78
80 }
81
82private:
84 DropShadow shadow;
85
87};
88
90 private Timer
91{
92public:
93 //==============================================================================
94 VirtualDesktopWatcher (Component& c) : component (&c)
95 {
96 component->addComponentListener (this);
97 update();
98 }
99
100 ~VirtualDesktopWatcher() override
101 {
102 stopTimer();
103
104 if (auto* c = component.get())
105 c->removeComponentListener (this);
106 }
107
108 bool shouldHideDropShadow() const
109 {
110 return hasReasonToHide;
111 }
112
113 void addListener (void* listener, std::function<void()> cb)
114 {
115 listeners[listener] = std::move (cb);
116 }
117
118 void removeListener (void* listener)
119 {
120 listeners.erase (listener);
121 }
122
123 //==============================================================================
125 {
126 if (component.get() == &c)
127 update();
128 }
129
130private:
131 //==============================================================================
132 void update()
133 {
134 bool newHasReasonToHide = false;
135
136 if (! component.wasObjectDeleted() && isWindows && component->isOnDesktop())
137 {
138 startTimerHz (5);
139
141
142 // During scaling changes this call can trigger a call to HWNDComponentPeer::handleDPIChanging()
143 // which deletes this VirtualDesktopWatcher.
144 newHasReasonToHide = ! detail::WindowingHelpers::isWindowOnCurrentVirtualDesktop (component->getWindowHandle());
145
146 if (weakThis == nullptr)
147 return;
148 }
149 else
150 {
151 stopTimer();
152 }
153
154 if (std::exchange (hasReasonToHide, newHasReasonToHide) != newHasReasonToHide)
155 for (auto& l : listeners)
156 l.second();
157 }
158
159 void timerCallback() override
160 {
161 update();
162 }
163
164 //==============================================================================
165 WeakReference<Component> component;
166 const bool isWindows = (SystemStats::getOperatingSystemType() & SystemStats::Windows) != 0;
167 bool hasReasonToHide = false;
168 std::map<void*, std::function<void()>> listeners;
169
170 JUCE_DECLARE_WEAK_REFERENCEABLE (VirtualDesktopWatcher)
171};
172
174{
175public:
177 : root (&r), listener (&l)
178 {
179 updateParentHierarchy();
180 }
181
183 {
184 for (auto& compEntry : observedComponents)
185 if (auto* comp = compEntry.get())
186 comp->removeComponentListener (this);
187 }
188
189 void componentVisibilityChanged (Component& component) override
190 {
191 if (root != &component)
192 listener->componentVisibilityChanged (*root);
193 }
194
196 {
197 if (root == &component)
198 updateParentHierarchy();
199 }
200
201private:
202 class ComponentWithWeakReference
203 {
204 public:
205 explicit ComponentWithWeakReference (Component& c)
206 : ptr (&c), ref (&c) {}
207
208 Component* get() const { return ref.get(); }
209
210 bool operator< (const ComponentWithWeakReference& other) const { return ptr < other.ptr; }
211
212 private:
213 Component* ptr;
215 };
216
217 void updateParentHierarchy()
218 {
219 const auto lastSeenComponents = std::exchange (observedComponents, [&]
220 {
222
223 for (auto node = root; node != nullptr; node = node->getParentComponent())
224 result.emplace (*node);
225
226 return result;
227 }());
228
229 const auto withDifference = [] (const auto& rangeA, const auto& rangeB, auto&& callback)
230 {
232 std::set_difference (rangeA.begin(), rangeA.end(), rangeB.begin(), rangeB.end(), std::back_inserter (result));
233
234 for (const auto& item : result)
235 if (auto* c = item.get())
236 callback (*c);
237 };
238
239 withDifference (lastSeenComponents, observedComponents, [this] (auto& comp) { comp.removeComponentListener (this); });
240 withDifference (observedComponents, lastSeenComponents, [this] (auto& comp) { comp.addComponentListener (this); });
241 }
242
243 Component* root = nullptr;
244 ComponentListener* listener = nullptr;
245 std::set<ComponentWithWeakReference> observedComponents;
246
247 JUCE_DECLARE_NON_COPYABLE (ParentVisibilityChangedListener)
248 JUCE_DECLARE_NON_MOVEABLE (ParentVisibilityChangedListener)
249};
250
251//==============================================================================
253
255{
256 if (virtualDesktopWatcher != nullptr)
257 virtualDesktopWatcher->removeListener (this);
258
259 if (owner != nullptr)
260 {
261 owner->removeComponentListener (this);
262 owner = nullptr;
263 }
264
265 updateParent();
266
267 const ScopedValueSetter<bool> setter (reentrant, true);
268 shadowWindows.clear();
269}
270
272{
273 if (componentToFollow != owner)
274 {
275 if (owner != nullptr)
276 owner->removeComponentListener (this);
277
278 // (the component can't be null)
279 jassert (componentToFollow != nullptr);
280
281 owner = componentToFollow;
282 jassert (owner != nullptr);
283
284 updateParent();
285 owner->addComponentListener (this);
286
287 // The visibility of `owner` is transitively affected by the visibility of its parents. Thus we need to trigger the
288 // componentVisibilityChanged() event in case it changes for any of the parents.
289 visibilityChangedListener = std::make_unique<ParentVisibilityChangedListener> (*owner,
290 static_cast<ComponentListener&> (*this));
291
292 virtualDesktopWatcher = std::make_unique<VirtualDesktopWatcher> (*owner);
293 virtualDesktopWatcher->addListener (this, [this]() { updateShadows(); });
294
295 updateShadows();
296 }
297}
298
299void DropShadower::updateParent()
300{
301 if (Component* p = lastParentComp)
302 p->removeComponentListener (this);
303
304 lastParentComp = owner != nullptr ? owner->getParentComponent() : nullptr;
305
306 if (Component* p = lastParentComp)
307 p->addComponentListener (this);
308}
309
310void DropShadower::componentMovedOrResized (Component& c, bool /*wasMoved*/, bool /*wasResized*/)
311{
312 if (owner == &c)
313 updateShadows();
314}
315
316void DropShadower::componentBroughtToFront (Component& c)
317{
318 if (owner == &c)
319 updateShadows();
320}
321
322void DropShadower::componentChildrenChanged (Component&)
323{
324 updateShadows();
325}
326
327void DropShadower::componentParentHierarchyChanged (Component& c)
328{
329 if (owner == &c)
330 {
331 updateParent();
332 updateShadows();
333 }
334}
335
336void DropShadower::componentVisibilityChanged (Component& c)
337{
338 if (owner == &c)
339 updateShadows();
340}
341
342void DropShadower::updateShadows()
343{
344 if (reentrant)
345 return;
346
347 const ScopedValueSetter<bool> setter (reentrant, true);
348
349 if (owner != nullptr
350 && owner->isShowing()
351 && owner->getWidth() > 0 && owner->getHeight() > 0
352 && (Desktop::canUseSemiTransparentWindows() || owner->getParentComponent() != nullptr)
353 && (virtualDesktopWatcher == nullptr || ! virtualDesktopWatcher->shouldHideDropShadow()))
354 {
355 while (shadowWindows.size() < 4)
356 shadowWindows.add (new ShadowWindow (owner, shadow));
357
358 const int shadowEdge = jmax (shadow.offset.x, shadow.offset.y) + shadow.radius;
359 const int x = owner->getX();
360 const int y = owner->getY() - shadowEdge;
361 const int w = owner->getWidth();
362 const int h = owner->getHeight() + shadowEdge + shadowEdge;
363
364 for (int i = 4; --i >= 0;)
365 {
366 // there seem to be rare situations where the dropshadower may be deleted by
367 // callbacks during this loop, so use a weak ref to watch out for this..
368 WeakReference<Component> sw (shadowWindows[i]);
369
370 if (sw != nullptr)
371 {
372 sw->setAlwaysOnTop (owner->isAlwaysOnTop());
373
374 if (sw == nullptr)
375 return;
376
377 switch (i)
378 {
379 case 0: sw->setBounds (x - shadowEdge, y, shadowEdge, h); break;
380 case 1: sw->setBounds (x + w, y, shadowEdge, h); break;
381 case 2: sw->setBounds (x, y, w, shadowEdge); break;
382 case 3: sw->setBounds (x, owner->getBottom(), w, shadowEdge); break;
383 default: break;
384 }
385
386 if (sw == nullptr)
387 return;
388
389 sw->toBehind (i == 3 ? owner.get() : shadowWindows.getUnchecked (i + 1));
390 }
391 }
392 }
393 else
394 {
395 shadowWindows.clear();
396 }
397}
398
399} // namespace juce
T back_inserter(T... args)
Gets informed about changes to a component's hierarchy or position.
@ windowIsTemporary
Indicates that the window is a temporary popup, like a menu, tooltip, etc.
@ windowIgnoresKeyPresses
Tells the window not to catch any keypresses.
@ windowIgnoresMouseClicks
Indicates that the window should let mouse clicks pass through it (may not be possible on some platfo...
The base class for all JUCE user-interface objects.
void removeComponentListener(ComponentListener *listenerToRemove)
Removes a component listener.
void setAccessible(bool shouldBeAccessible)
Sets whether this component and its children are visible to accessibility clients.
void setInterceptsMouseClicks(bool allowClicksOnThisComponent, bool allowClicksOnChildComponents) noexcept
Changes the default return value for the hitTest() method.
Component * getParentComponent() const noexcept
Returns the component which this component is inside.
void * getWindowHandle() const
Returns the underlying native window handle for this component.
Rectangle< int > getLocalArea(const Component *sourceComponent, Rectangle< int > areaRelativeToSourceComponent) const
Converts a rectangle to be relative to this component's coordinate space.
void repaint()
Marks the whole component as needing to be redrawn.
bool isOnDesktop() const noexcept
Returns true if this component is currently showing on the desktop.
virtual void addToDesktop(int windowStyleFlags, void *nativeWindowToAttachTo=nullptr)
Makes this component appear as a window on the desktop.
void setSize(int newWidth, int newHeight)
Changes the size of the component.
virtual void setVisible(bool shouldBeVisible)
Makes the component visible or invisible.
virtual float getDesktopScaleFactor() const
Returns the default scale factor to use for this component when it is placed on the desktop.
static bool canUseSemiTransparentWindows() noexcept
True if the OS supports semitransparent windows.
void componentVisibilityChanged(Component &component) override
Called when the component is made visible or invisible.
void componentParentHierarchyChanged(Component &component) override
Called to indicate that the component's parents have changed.
float getDesktopScaleFactor() const override
Returns the default scale factor to use for this component when it is placed on the desktop.
void paint(Graphics &g) override
Components can override this method to draw their content.
void resized() override
Called when this component's size has been changed.
void componentParentHierarchyChanged(Component &c) override
Called to indicate that the component's parents have changed.
DropShadower(const DropShadow &shadowType)
Creates a DropShadower.
~DropShadower() override
Destructor.
void setOwner(Component *componentToFollow)
Attaches the DropShadower to the component you want to shadow.
A graphics context, used for drawing a component or image.
ValueType y
The point's Y coordinate.
Definition juce_Point.h:252
ValueType x
The point's X coordinate.
Definition juce_Point.h:251
Helper class providing an RAII-based mechanism for temporarily setting and then re-setting a value.
@ Windows
To test whether any version of Windows is running, you can use the expression ((getOperatingSystemTyp...
static OperatingSystemType getOperatingSystemType()
Returns the type of operating system we're running on.
Makes repeated callbacks to a virtual method at a specified time interval.
Definition juce_Timer.h:52
void stopTimer() noexcept
Stops the timer.
void startTimerHz(int timerFrequencyHz) noexcept
Starts the timer with an interval specified in Hertz.
This class acts as a pointer which will automatically become null if the object to which it points is...
T emplace(T... args)
T erase(T... args)
T exchange(T... args)
#define jassert(expression)
Platform-independent assertion macro.
#define JUCE_DECLARE_NON_MOVEABLE(className)
This is a shorthand macro for deleting a class's move constructor and move assignment operator.
#define JUCE_DECLARE_NON_COPYABLE(className)
This is a shorthand macro for deleting a class's copy constructor and copy assignment operator.
auto & get(ProcessorChain< Processors... > &chain) noexcept
Non-member equivalent of ProcessorChain::get which avoids awkward member template syntax.
#define JUCE_DECLARE_WEAK_REFERENCEABLE(Class)
Macro to easily allow a class to be made weak-referenceable.
JUCE Namespace.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
T ref(T... args)
T set_difference(T... args)
Defines a drop-shadow effect.
Point< int > offset
The offset of the shadow.
int radius
The approximate spread of the shadow.
void drawForRectangle(Graphics &g, const Rectangle< int > &area) const
Renders a drop-shadow for a rectangle.