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_Displays.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
29Displays::Displays (Desktop& desktop)
30{
31 init (desktop);
32}
33
34void Displays::init (Desktop& desktop)
35{
36 findDisplays (desktop.getGlobalScaleFactor());
37}
38
40{
41 int maxArea = -1;
42 const Display* foundDisplay = nullptr;
43
44 for (auto& display : displays)
45 {
46 auto displayArea = display.totalArea;
47
48 if (isPhysical)
49 displayArea = (displayArea.withZeroOrigin() * display.scale) + display.topLeftPhysical;
50
51 displayArea = displayArea.getIntersection (rect);
52 auto area = displayArea.getWidth() * displayArea.getHeight();
53
54 if (area >= maxArea)
55 {
56 maxArea = area;
57 foundDisplay = &display;
58 }
59 }
60
61 return foundDisplay;
62}
63
65{
67 const Display* foundDisplay = nullptr;
68
69 for (auto& display : displays)
70 {
71 auto displayArea = display.totalArea;
72
73 if (isPhysical)
74 displayArea = (displayArea.withZeroOrigin() * display.scale) + display.topLeftPhysical;
75
76 if (displayArea.contains (point))
77 return &display;
78
79 auto distance = displayArea.getCentre().getDistanceFrom (point);
80
81 if (distance <= minDistance)
82 {
83 minDistance = distance;
84 foundDisplay = &display;
85 }
86 }
87
88 return foundDisplay;
89}
90
92{
93 return physicalToLogical (rect.toFloat(), useScaleFactorOfDisplay).toNearestInt();
94}
95
97{
98 const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
99 : getDisplayForRect (rect.toNearestInt(), true);
100
101 if (display == nullptr)
102 return rect;
103
105
106 return ((rect - display->topLeftPhysical.toFloat()) / (display->scale / globalScale))
107 + (display->totalArea.getTopLeft().toFloat() * globalScale);
108}
109
111{
112 return logicalToPhysical (rect.toFloat(), useScaleFactorOfDisplay).toNearestInt();
113}
114
116{
117 const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
118 : getDisplayForRect (rect.toNearestInt(), false);
119
120 if (display == nullptr)
121 return rect;
122
124
125 return ((rect.toFloat() - (display->totalArea.getTopLeft().toFloat() * globalScale)) * (display->scale / globalScale))
126 + display->topLeftPhysical.toFloat();
127}
128
129template <typename ValueType>
131{
132 const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
133 : getDisplayForPoint (point.roundToInt(), true);
134
135 if (display == nullptr)
136 return point;
137
139
140 Point<ValueType> logicalTopLeft (static_cast<ValueType> (display->totalArea.getX()), static_cast<ValueType> (display->totalArea.getY()));
141 Point<ValueType> physicalTopLeft (static_cast<ValueType> (display->topLeftPhysical.getX()), static_cast<ValueType> (display->topLeftPhysical.getY()));
142
143 return ((point - physicalTopLeft) / (display->scale / globalScale)) + (logicalTopLeft * globalScale);
144}
145
146template <typename ValueType>
148{
149 const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
150 : getDisplayForPoint (point.roundToInt(), false);
151
152 if (display == nullptr)
153 return point;
154
156
157 Point<ValueType> logicalTopLeft (static_cast<ValueType> (display->totalArea.getX()), static_cast<ValueType> (display->totalArea.getY()));
158 Point<ValueType> physicalTopLeft (static_cast<ValueType> (display->topLeftPhysical.getX()), static_cast<ValueType> (display->topLeftPhysical.getY()));
159
160 return ((point - (logicalTopLeft * globalScale)) * (display->scale / globalScale)) + physicalTopLeft;
161}
162
164{
166
167 const auto iter = std::find_if (displays.begin(), displays.end(), [] (auto& d) { return d.isMain; });
168 return iter != displays.end() ? iter : nullptr;
169}
170
172{
175
176 for (auto& d : displays)
177 rl.addWithoutMerging (userAreasOnly ? d.userArea : d.totalArea);
178
179 return rl;
180}
181
186
187void Displays::refresh()
188{
191
192 init (Desktop::getInstance());
193
194 if (oldDisplays != displays)
195 {
196 for (auto i = ComponentPeer::getNumPeers(); --i >= 0;)
197 if (auto* peer = ComponentPeer::getPeer (i))
198 peer->handleScreenSizeChange();
199 }
200}
201
202static auto tie (const Displays::Display& d)
203{
204 return std::tie (d.dpi,
205 d.isMain,
206 d.keyboardInsets,
207 d.safeAreaInsets,
208 d.scale,
209 d.topLeftPhysical,
210 d.totalArea,
211 d.userArea);
212}
213
214static bool operator== (const Displays::Display& d1, const Displays::Display& d2) noexcept
215{
216 return tie (d1) == tie (d2);
217}
218
219//==============================================================================
220// These methods are used for converting the totalArea and userArea Rectangles in Display from physical to logical
221// pixels. We do this by constructing a graph of connected displays where the root node has position (0, 0); this can be
222// safely converted to logical pixels using its scale factor and we can then traverse the graph and work out the logical pixels
223// for all the other connected displays. We need to do this as the logical bounds of a display depend not only on its scale
224// factor but also the scale factor of the displays connected to it.
225
245
247static void processDisplay (DisplayNode* currentNode, Array<DisplayNode>& allNodes)
248{
249 const auto physicalArea = currentNode->display->totalArea.toDouble();
250 const auto scale = currentNode->display->scale;
251
252 if (! currentNode->isRoot)
253 {
254 const auto logicalWidth = physicalArea.getWidth() / scale;
255 const auto logicalHeight = physicalArea.getHeight() / scale;
256
257 const auto physicalParentArea = currentNode->parent->display->totalArea.toDouble();
258 const auto logicalParentArea = currentNode->parent->logicalArea; // logical area of parent has already been calculated
259 const auto parentScale = currentNode->parent->display->scale;
260
261 Rectangle<double> logicalArea (0.0, 0.0, logicalWidth, logicalHeight);
262
263 if (approximatelyEqual (physicalArea.getRight(), physicalParentArea.getX())) logicalArea.setPosition ({ logicalParentArea.getX() - logicalWidth, physicalArea.getY() / parentScale }); // on left
264 else if (approximatelyEqual (physicalArea.getX(), physicalParentArea.getRight())) logicalArea.setPosition ({ logicalParentArea.getRight(), physicalArea.getY() / parentScale }); // on right
265 else if (approximatelyEqual (physicalArea.getBottom(), physicalParentArea.getY())) logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getY() - logicalHeight }); // on top
266 else if (approximatelyEqual (physicalArea.getY(), physicalParentArea.getBottom())) logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getBottom() }); // on bottom
267 else jassertfalse;
268
269 currentNode->logicalArea = logicalArea;
270 }
271 else
272 {
273 // If currentNode is the root (position (0, 0)) then we can just scale the physical area
274 currentNode->logicalArea = physicalArea / scale;
275 currentNode->parent = currentNode;
276 }
277
278 // Find child nodes
279 Array<DisplayNode*> children;
280 for (auto& node : allNodes)
281 {
282 // Already calculated
283 if (node.parent != nullptr)
284 continue;
285
286 const auto otherPhysicalArea = node.display->totalArea.toDouble();
287
288 // If the displays are touching on any side
291 {
292 node.parent = currentNode;
293 children.add (&node);
294 }
295 }
296
297 // Recursively process all child nodes
298 for (auto child : children)
299 processDisplay (child, allNodes);
300}
301
305void Displays::updateToLogical()
306{
307 if (displays.size() == 1)
308 {
309 auto& display = displays.getReference (0);
310
311 display.totalArea = (display.totalArea.toDouble() / display.scale).toNearestInt();
312 display.userArea = (display.userArea.toDouble() / display.scale).toNearestInt();
313
314 return;
315 }
316
318
319 for (auto& d : displays)
320 {
321 DisplayNode node;
322
323 node.display = &d;
324
325 if (d.totalArea.getTopLeft() == Point<int>())
326 node.isRoot = true;
327
328 displayNodes.add (node);
329 }
330
331 auto* root = [&displayNodes]() -> DisplayNode*
332 {
333 for (auto& node : displayNodes)
334 if (node.isRoot)
335 return &node;
336
338 DisplayNode* retVal = nullptr;
339
340 for (auto& node : displayNodes)
341 {
342 auto distance = node.display->totalArea.getTopLeft().getDistanceFrom ({});
343
344 if (distance < minDistance)
345 {
347 retVal = &node;
348 }
349 }
350
351 if (retVal != nullptr)
352 retVal->isRoot = true;
353
354 return retVal;
355 }();
356
357 // Must have a root node!
358 jassert (root != nullptr);
359
360 // Recursively traverse the display graph from the root and work out logical bounds
361 processDisplay (root, displayNodes);
362
363 for (auto& node : displayNodes)
364 {
365 // All of the nodes should have a parent
366 jassert (node.parent != nullptr);
367
368 auto relativeUserArea = (node.display->userArea.toDouble() - node.display->totalArea.toDouble().getTopLeft()) / node.display->scale;
369
370 // Now set Display::totalArea and ::userArea using the logical area that we have calculated
371 node.display->topLeftPhysical = node.display->totalArea.getTopLeft();
372 node.display->totalArea = node.logicalArea.toNearestInt();
373 node.display->userArea = (relativeUserArea + node.logicalArea.getTopLeft()).toNearestInt();
374 }
375}
376
377#ifndef DOXYGEN
378 // explicit template instantiations
379 template Point<int> Displays::physicalToLogical (Point<int>, const Display*) const noexcept;
380 template Point<float> Displays::physicalToLogical (Point<float>, const Display*) const noexcept;
381
382 template Point<int> Displays::logicalToPhysical (Point<int>, const Display*) const noexcept;
383 template Point<float> Displays::logicalToPhysical (Point<float>, const Display*) const noexcept;
384#endif
385
386//==============================================================================
387// Deprecated methods
388const Displays::Display& Displays::getDisplayContaining (Point<int> position) const noexcept
389{
391 const auto* best = &displays.getReference (0);
393
394 for (auto& d : displays)
395 {
396 if (d.totalArea.contains (position))
397 {
398 best = &d;
399 break;
400 }
401
402 auto distance = d.totalArea.getCentre().getDistanceFrom (position);
403
404 if (distance < bestDistance)
405 {
407 best = &d;
408 }
409 }
410
411 return *best;
412}
413
414const Displays::Display& Displays::findDisplayForRect (Rectangle<int> rect, bool isPhysical) const noexcept
415{
416 if (auto* display = getDisplayForRect (rect, isPhysical))
417 return *display;
418
419 return emptyDisplay;
420}
421
422const Displays::Display& Displays::findDisplayForPoint (Point<int> point, bool isPhysical) const noexcept
423{
424 if (auto* display = getDisplayForPoint (point, isPhysical))
425 return *display;
426
427 return emptyDisplay;
428}
429
430const Displays::Display& Displays::getMainDisplay() const noexcept
431{
432 if (auto* display = getPrimaryDisplay())
433 return *display;
434
435 return emptyDisplay;
436}
437
438} // namespace juce
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:56
void swapWith(OtherArrayType &otherArray) noexcept
This swaps the contents of this array with those of another array.
Definition juce_Array.h:621
static int getNumPeers() noexcept
Returns the number of currently-active peers.
static ComponentPeer * getPeer(int index) noexcept
Returns one of the currently-active peers.
float getGlobalScaleFactor() const noexcept
Returns the current global scale factor, as set by setGlobalScaleFactor().
static Desktop &JUCE_CALLTYPE getInstance()
There's only one desktop object, and this method will return it.
Rectangle< int > logicalToPhysical(Rectangle< int > logicalRect, const Display *useScaleFactorOfDisplay=nullptr) const noexcept
Converts an integer Rectangle from logical to physical pixels.
const Display * getDisplayForPoint(Point< int > point, bool isPhysical=false) const noexcept
Returns the Display object representing the display containing a given Point (either in logical or ph...
const Display * getDisplayForRect(Rectangle< int > rect, bool isPhysical=false) const noexcept
Returns the Display object representing the display containing a given Rectangle (either in logical o...
Array< Display > displays
An Array containing the Display objects for all of the connected displays.
Rectangle< int > physicalToLogical(Rectangle< int > physicalRect, const Display *useScaleFactorOfDisplay=nullptr) const noexcept
Converts an integer Rectangle from physical to logical pixels.
Rectangle< int > totalArea
The total area of this display in logical pixels including any OS-dependent objects like the taskbar,...
Rectangle< int > getTotalBounds(bool userAreasOnly) const
Returns the smallest bounding box which contains all the displays in LOGICAL pixels.
const Display * getPrimaryDisplay() const noexcept
Returns the Display object representing the display acting as the user's main screen,...
RectangleList< int > getRectangleList(bool userAreasOnly) const
Returns a RectangleList made up of all the displays in LOGICAL pixels.
Represents a connected display device.
A pair of (x, y) coordinates.
Definition juce_Point.h:42
Maintains a set of rectangles as a complex region.
RectangleType getBounds() const noexcept
Returns the smallest rectangle that can enclose the whole of this region.
void addWithoutMerging(RectangleType rect)
Dumbly adds a rectangle to the list without checking for overlaps.
Manages a rectangle and allows geometric operations to be performed on it.
Rectangle< double > toDouble() const noexcept
Casts this rectangle to a Rectangle<double>.
Rectangle< int > toNearestInt() const noexcept
Casts this rectangle to a Rectangle<int>.
void setPosition(Point< ValueType > newPos) noexcept
Changes the position of the rectangle's top-left corner (leaving its size unchanged).
T distance(T... args)
T find_if(T... args)
#define JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
This macro is used to catch unsafe use of functions which expect to only be called on the message thr...
#define jassert(expression)
Platform-independent assertion macro.
#define jassertfalse
This will always cause an assertion failure.
T max(T... args)
JUCE Namespace.
DisplayNode * parent
The parent node of this node in our display graph.
bool isRoot
True if this represents the 'root' display with position (0, 0).
Rectangle< double > logicalArea
The logical area to be calculated.
Displays::Display * display
The Display object that this represents.
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
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
Represents a node in our graph of displays.
T tie(T... args)