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
26 namespace juce
27 {
28
Desktop()29 Desktop::Desktop()
30 : mouseSources (new MouseInputSource::SourceList()),
31 masterScaleFactor ((float) getDefaultMasterScale())
32 {
33 displays.reset (new Displays (*this));
34 }
35
~Desktop()36 Desktop::~Desktop()
37 {
38 setScreenSaverEnabled (true);
39 animator.cancelAllAnimations (false);
40
41 jassert (instance == this);
42 instance = nullptr;
43
44 // doh! If you don't delete all your windows before exiting, you're going to
45 // be leaking memory!
46 jassert (desktopComponents.size() == 0);
47 }
48
getInstance()49 Desktop& JUCE_CALLTYPE Desktop::getInstance()
50 {
51 if (instance == nullptr)
52 instance = new Desktop();
53
54 return *instance;
55 }
56
57 Desktop* Desktop::instance = nullptr;
58
59 //==============================================================================
getNumComponents() const60 int Desktop::getNumComponents() const noexcept
61 {
62 return desktopComponents.size();
63 }
64
getComponent(int index) const65 Component* Desktop::getComponent (int index) const noexcept
66 {
67 return desktopComponents [index];
68 }
69
findComponentAt(Point<int> screenPosition) const70 Component* Desktop::findComponentAt (Point<int> screenPosition) const
71 {
72 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
73
74 for (int i = desktopComponents.size(); --i >= 0;)
75 {
76 auto* c = desktopComponents.getUnchecked(i);
77
78 if (c->isVisible())
79 {
80 auto relative = c->getLocalPoint (nullptr, screenPosition);
81
82 if (c->contains (relative))
83 return c->getComponentAt (relative);
84 }
85 }
86
87 return nullptr;
88 }
89
90 //==============================================================================
getDefaultLookAndFeel()91 LookAndFeel& Desktop::getDefaultLookAndFeel() noexcept
92 {
93 if (auto lf = currentLookAndFeel.get())
94 return *lf;
95
96 if (defaultLookAndFeel == nullptr)
97 defaultLookAndFeel.reset (new LookAndFeel_V4());
98
99 auto lf = defaultLookAndFeel.get();
100 jassert (lf != nullptr);
101 currentLookAndFeel = lf;
102 return *lf;
103 }
104
setDefaultLookAndFeel(LookAndFeel * newDefaultLookAndFeel)105 void Desktop::setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel)
106 {
107 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
108 currentLookAndFeel = newDefaultLookAndFeel;
109
110 for (int i = getNumComponents(); --i >= 0;)
111 if (auto* c = getComponent (i))
112 c->sendLookAndFeelChange();
113 }
114
115 //==============================================================================
addDesktopComponent(Component * c)116 void Desktop::addDesktopComponent (Component* c)
117 {
118 jassert (c != nullptr);
119 jassert (! desktopComponents.contains (c));
120 desktopComponents.addIfNotAlreadyThere (c);
121 }
122
removeDesktopComponent(Component * c)123 void Desktop::removeDesktopComponent (Component* c)
124 {
125 desktopComponents.removeFirstMatchingValue (c);
126 }
127
componentBroughtToFront(Component * c)128 void Desktop::componentBroughtToFront (Component* c)
129 {
130 auto index = desktopComponents.indexOf (c);
131 jassert (index >= 0);
132
133 if (index >= 0)
134 {
135 int newIndex = -1;
136
137 if (! c->isAlwaysOnTop())
138 {
139 newIndex = desktopComponents.size();
140
141 while (newIndex > 0 && desktopComponents.getUnchecked (newIndex - 1)->isAlwaysOnTop())
142 --newIndex;
143
144 --newIndex;
145 }
146
147 desktopComponents.move (index, newIndex);
148 }
149 }
150
151 //==============================================================================
getMousePosition()152 Point<int> Desktop::getMousePosition()
153 {
154 return getMousePositionFloat().roundToInt();
155 }
156
getMousePositionFloat()157 Point<float> Desktop::getMousePositionFloat()
158 {
159 return getInstance().getMainMouseSource().getScreenPosition();
160 }
161
setMousePosition(Point<int> newPosition)162 void Desktop::setMousePosition (Point<int> newPosition)
163 {
164 getInstance().getMainMouseSource().setScreenPosition (newPosition.toFloat());
165 }
166
getLastMouseDownPosition()167 Point<int> Desktop::getLastMouseDownPosition()
168 {
169 return getInstance().getMainMouseSource().getLastMouseDownPosition().roundToInt();
170 }
171
getMouseButtonClickCounter() const172 int Desktop::getMouseButtonClickCounter() const noexcept { return mouseClickCounter; }
getMouseWheelMoveCounter() const173 int Desktop::getMouseWheelMoveCounter() const noexcept { return mouseWheelCounter; }
174
incrementMouseClickCounter()175 void Desktop::incrementMouseClickCounter() noexcept { ++mouseClickCounter; }
incrementMouseWheelCounter()176 void Desktop::incrementMouseWheelCounter() noexcept { ++mouseWheelCounter; }
177
getMouseSources() const178 const Array<MouseInputSource>& Desktop::getMouseSources() const noexcept { return mouseSources->sourceArray; }
getNumMouseSources() const179 int Desktop::getNumMouseSources() const noexcept { return mouseSources->sources.size(); }
getNumDraggingMouseSources() const180 int Desktop::getNumDraggingMouseSources() const noexcept { return mouseSources->getNumDraggingMouseSources(); }
getMouseSource(int index) const181 MouseInputSource* Desktop::getMouseSource (int index) const noexcept { return mouseSources->getMouseSource (index); }
getDraggingMouseSource(int index) const182 MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept { return mouseSources->getDraggingMouseSource (index); }
getMainMouseSource() const183 MouseInputSource Desktop::getMainMouseSource() const noexcept { return MouseInputSource (mouseSources->sources.getUnchecked(0)); }
beginDragAutoRepeat(int interval)184 void Desktop::beginDragAutoRepeat (int interval) { mouseSources->beginDragAutoRepeat (interval); }
185
186 //==============================================================================
addFocusChangeListener(FocusChangeListener * l)187 void Desktop::addFocusChangeListener (FocusChangeListener* l) { focusListeners.add (l); }
removeFocusChangeListener(FocusChangeListener * l)188 void Desktop::removeFocusChangeListener (FocusChangeListener* l) { focusListeners.remove (l); }
triggerFocusCallback()189 void Desktop::triggerFocusCallback() { triggerAsyncUpdate(); }
190
handleAsyncUpdate()191 void Desktop::handleAsyncUpdate()
192 {
193 // The component may be deleted during this operation, but we'll use a SafePointer rather than a
194 // BailOutChecker so that any remaining listeners will still get a callback (with a null pointer).
195 WeakReference<Component> currentFocus (Component::getCurrentlyFocusedComponent());
196 focusListeners.call ([&] (FocusChangeListener& l) { l.globalFocusChanged (currentFocus.get()); });
197 }
198
199 //==============================================================================
resetTimer()200 void Desktop::resetTimer()
201 {
202 if (mouseListeners.size() == 0)
203 stopTimer();
204 else
205 startTimer (100);
206
207 lastFakeMouseMove = getMousePositionFloat();
208 }
209
getMouseListeners()210 ListenerList<MouseListener>& Desktop::getMouseListeners()
211 {
212 resetTimer();
213 return mouseListeners;
214 }
215
addGlobalMouseListener(MouseListener * listener)216 void Desktop::addGlobalMouseListener (MouseListener* listener)
217 {
218 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
219 mouseListeners.add (listener);
220 resetTimer();
221 }
222
removeGlobalMouseListener(MouseListener * listener)223 void Desktop::removeGlobalMouseListener (MouseListener* listener)
224 {
225 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
226 mouseListeners.remove (listener);
227 resetTimer();
228 }
229
timerCallback()230 void Desktop::timerCallback()
231 {
232 if (lastFakeMouseMove != getMousePositionFloat())
233 sendMouseMove();
234 }
235
sendMouseMove()236 void Desktop::sendMouseMove()
237 {
238 if (! mouseListeners.isEmpty())
239 {
240 startTimer (20);
241
242 lastFakeMouseMove = getMousePositionFloat();
243
244 if (auto* target = findComponentAt (lastFakeMouseMove.roundToInt()))
245 {
246 Component::BailOutChecker checker (target);
247 auto pos = target->getLocalPoint (nullptr, lastFakeMouseMove);
248 auto now = Time::getCurrentTime();
249
250 const MouseEvent me (getMainMouseSource(), pos, ModifierKeys::currentModifiers, MouseInputSource::invalidPressure,
251 MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
252 MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
253 target, target, now, pos, now, 0, false);
254
255 if (me.mods.isAnyMouseButtonDown())
256 mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseDrag (me); });
257 else
258 mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseMove (me); });
259 }
260 }
261 }
262
263 //==============================================================================
setKioskModeComponent(Component * componentToUse,bool allowMenusAndBars)264 void Desktop::setKioskModeComponent (Component* componentToUse, bool allowMenusAndBars)
265 {
266 if (kioskModeReentrant)
267 return;
268
269 const ScopedValueSetter<bool> setter (kioskModeReentrant, true, false);
270
271 if (kioskModeComponent != componentToUse)
272 {
273 // agh! Don't delete or remove a component from the desktop while it's still the kiosk component!
274 jassert (kioskModeComponent == nullptr || ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
275
276 if (auto* oldKioskComp = kioskModeComponent)
277 {
278 kioskModeComponent = nullptr; // (to make sure that isKioskMode() returns false when resizing the old one)
279 setKioskComponent (oldKioskComp, false, allowMenusAndBars);
280 oldKioskComp->setBounds (kioskComponentOriginalBounds);
281 }
282
283 kioskModeComponent = componentToUse;
284
285 if (kioskModeComponent != nullptr)
286 {
287 // Only components that are already on the desktop can be put into kiosk mode!
288 jassert (ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
289
290 kioskComponentOriginalBounds = kioskModeComponent->getBounds();
291 setKioskComponent (kioskModeComponent, true, allowMenusAndBars);
292 }
293 }
294 }
295
296 //==============================================================================
setOrientationsEnabled(int newOrientations)297 void Desktop::setOrientationsEnabled (int newOrientations)
298 {
299 if (allowedOrientations != newOrientations)
300 {
301 // Dodgy set of flags being passed here! Make sure you specify at least one permitted orientation.
302 jassert (newOrientations != 0 && (newOrientations & ~allOrientations) == 0);
303
304 allowedOrientations = newOrientations;
305 allowedOrientationsChanged();
306 }
307 }
308
getOrientationsEnabled() const309 int Desktop::getOrientationsEnabled() const noexcept
310 {
311 return allowedOrientations;
312 }
313
isOrientationEnabled(DisplayOrientation orientation) const314 bool Desktop::isOrientationEnabled (DisplayOrientation orientation) const noexcept
315 {
316 // Make sure you only pass one valid flag in here...
317 jassert (orientation == upright || orientation == upsideDown
318 || orientation == rotatedClockwise || orientation == rotatedAntiClockwise);
319
320 return (allowedOrientations & orientation) != 0;
321 }
322
setGlobalScaleFactor(float newScaleFactor)323 void Desktop::setGlobalScaleFactor (float newScaleFactor) noexcept
324 {
325 JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
326
327 if (masterScaleFactor != newScaleFactor)
328 {
329 masterScaleFactor = newScaleFactor;
330 displays->refresh();
331 }
332 }
333
334 } // namespace juce
335