mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-13 00:04:19 +00:00
Added Animated App template and examples
This commit is contained in:
parent
fefcf7aca6
commit
ff6520a89a
1141 changed files with 438491 additions and 94 deletions
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_CACHEDCOMPONENTIMAGE_H_INCLUDED
|
||||
#define JUCE_CACHEDCOMPONENTIMAGE_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Base class used internally for structures that can store cached images of
|
||||
component state.
|
||||
|
||||
Most people are unlikely to ever need to know about this class - it's really
|
||||
only for power-users!
|
||||
|
||||
@see Component::setCachedComponentImage
|
||||
*/
|
||||
class JUCE_API CachedComponentImage
|
||||
{
|
||||
public:
|
||||
CachedComponentImage() noexcept {}
|
||||
virtual ~CachedComponentImage() {}
|
||||
|
||||
//==============================================================================
|
||||
/** Called as part of the parent component's paint method, this must draw
|
||||
the given component into the target graphics context, using the cached
|
||||
version where possible.
|
||||
*/
|
||||
virtual void paint (Graphics&) = 0;
|
||||
|
||||
/** Invalidates all cached image data.
|
||||
@returns true if the peer should also be repainted, or false if this object
|
||||
handles all repaint work internally.
|
||||
*/
|
||||
virtual bool invalidateAll() = 0;
|
||||
|
||||
/** Invalidates a section of the cached image data.
|
||||
@returns true if the peer should also be repainted, or false if this object
|
||||
handles all repaint work internally.
|
||||
*/
|
||||
virtual bool invalidate (const Rectangle<int>& area) = 0;
|
||||
|
||||
/** Called to indicate that the component is no longer active, so
|
||||
any cached data should be released if possible.
|
||||
*/
|
||||
virtual void releaseResources() = 0;
|
||||
};
|
||||
|
||||
#endif // JUCE_CACHEDCOMPONENTIMAGE_H_INCLUDED
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
void ComponentListener::componentMovedOrResized (Component&, bool, bool) {}
|
||||
void ComponentListener::componentBroughtToFront (Component&) {}
|
||||
void ComponentListener::componentVisibilityChanged (Component&) {}
|
||||
void ComponentListener::componentChildrenChanged (Component&) {}
|
||||
void ComponentListener::componentParentHierarchyChanged (Component&) {}
|
||||
void ComponentListener::componentNameChanged (Component&) {}
|
||||
void ComponentListener::componentBeingDeleted (Component&) {}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_COMPONENTLISTENER_H_INCLUDED
|
||||
#define JUCE_COMPONENTLISTENER_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Gets informed about changes to a component's hierarchy or position.
|
||||
|
||||
To monitor a component for changes, register a subclass of ComponentListener
|
||||
with the component using Component::addComponentListener().
|
||||
|
||||
Be sure to deregister listeners before you delete them!
|
||||
|
||||
@see Component::addComponentListener, Component::removeComponentListener
|
||||
*/
|
||||
class JUCE_API ComponentListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~ComponentListener() {}
|
||||
|
||||
/** Called when the component's position or size changes.
|
||||
|
||||
@param component the component that was moved or resized
|
||||
@param wasMoved true if the component's top-left corner has just moved
|
||||
@param wasResized true if the component's width or height has just changed
|
||||
@see Component::setBounds, Component::resized, Component::moved
|
||||
*/
|
||||
virtual void componentMovedOrResized (Component& component,
|
||||
bool wasMoved,
|
||||
bool wasResized);
|
||||
|
||||
/** Called when the component is brought to the top of the z-order.
|
||||
|
||||
@param component the component that was moved
|
||||
@see Component::toFront, Component::broughtToFront
|
||||
*/
|
||||
virtual void componentBroughtToFront (Component& component);
|
||||
|
||||
/** Called when the component is made visible or invisible.
|
||||
|
||||
@param component the component that changed
|
||||
@see Component::setVisible
|
||||
*/
|
||||
virtual void componentVisibilityChanged (Component& component);
|
||||
|
||||
/** Called when the component has children added or removed, or their z-order
|
||||
changes.
|
||||
|
||||
@param component the component whose children have changed
|
||||
@see Component::childrenChanged, Component::addChildComponent,
|
||||
Component::removeChildComponent
|
||||
*/
|
||||
virtual void componentChildrenChanged (Component& component);
|
||||
|
||||
/** Called to indicate that the component's parents have changed.
|
||||
|
||||
When a component is added or removed from its parent, all of its children
|
||||
will produce this notification (recursively - so all children of its
|
||||
children will also be called as well).
|
||||
|
||||
@param component the component that this listener is registered with
|
||||
@see Component::parentHierarchyChanged
|
||||
*/
|
||||
virtual void componentParentHierarchyChanged (Component& component);
|
||||
|
||||
/** Called when the component's name is changed.
|
||||
|
||||
@see Component::setName, Component::getName
|
||||
*/
|
||||
virtual void componentNameChanged (Component& component);
|
||||
|
||||
/** Called when the component is in the process of being deleted.
|
||||
|
||||
This callback is made from inside the destructor, so be very, very cautious
|
||||
about what you do in here.
|
||||
|
||||
In particular, bear in mind that it's the Component base class's destructor that calls
|
||||
this - so if the object that's being deleted is a subclass of Component, then the
|
||||
subclass layers of the object will already have been destructed when it gets to this
|
||||
point!
|
||||
*/
|
||||
virtual void componentBeingDeleted (Component& component);
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_COMPONENTLISTENER_H_INCLUDED
|
||||
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
Desktop::Desktop()
|
||||
: mouseSources (new MouseInputSource::SourceList()),
|
||||
mouseClickCounter (0), mouseWheelCounter (0),
|
||||
kioskModeComponent (nullptr),
|
||||
kioskModeReentrant (false),
|
||||
allowedOrientations (allOrientations),
|
||||
masterScaleFactor ((float) getDefaultMasterScale())
|
||||
{
|
||||
displays = new Displays (*this);
|
||||
}
|
||||
|
||||
Desktop::~Desktop()
|
||||
{
|
||||
setScreenSaverEnabled (true);
|
||||
|
||||
jassert (instance == this);
|
||||
instance = nullptr;
|
||||
|
||||
// doh! If you don't delete all your windows before exiting, you're going to
|
||||
// be leaking memory!
|
||||
jassert (desktopComponents.size() == 0);
|
||||
}
|
||||
|
||||
Desktop& JUCE_CALLTYPE Desktop::getInstance()
|
||||
{
|
||||
if (instance == nullptr)
|
||||
instance = new Desktop();
|
||||
|
||||
return *instance;
|
||||
}
|
||||
|
||||
Desktop* Desktop::instance = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
int Desktop::getNumComponents() const noexcept
|
||||
{
|
||||
return desktopComponents.size();
|
||||
}
|
||||
|
||||
Component* Desktop::getComponent (const int index) const noexcept
|
||||
{
|
||||
return desktopComponents [index];
|
||||
}
|
||||
|
||||
Component* Desktop::findComponentAt (Point<int> screenPosition) const
|
||||
{
|
||||
ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
for (int i = desktopComponents.size(); --i >= 0;)
|
||||
{
|
||||
Component* const c = desktopComponents.getUnchecked(i);
|
||||
|
||||
if (c->isVisible())
|
||||
{
|
||||
const Point<int> relative (c->getLocalPoint (nullptr, screenPosition));
|
||||
|
||||
if (c->contains (relative))
|
||||
return c->getComponentAt (relative);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LookAndFeel& Desktop::getDefaultLookAndFeel() noexcept
|
||||
{
|
||||
if (currentLookAndFeel == nullptr)
|
||||
{
|
||||
if (defaultLookAndFeel == nullptr)
|
||||
defaultLookAndFeel = new LookAndFeel_V2();
|
||||
|
||||
currentLookAndFeel = defaultLookAndFeel;
|
||||
}
|
||||
|
||||
return *currentLookAndFeel;
|
||||
}
|
||||
|
||||
void Desktop::setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel)
|
||||
{
|
||||
ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
currentLookAndFeel = newDefaultLookAndFeel;
|
||||
|
||||
for (int i = getNumComponents(); --i >= 0;)
|
||||
if (Component* const c = getComponent (i))
|
||||
c->sendLookAndFeelChange();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::addDesktopComponent (Component* const c)
|
||||
{
|
||||
jassert (c != nullptr);
|
||||
jassert (! desktopComponents.contains (c));
|
||||
desktopComponents.addIfNotAlreadyThere (c);
|
||||
}
|
||||
|
||||
void Desktop::removeDesktopComponent (Component* const c)
|
||||
{
|
||||
desktopComponents.removeFirstMatchingValue (c);
|
||||
}
|
||||
|
||||
void Desktop::componentBroughtToFront (Component* const c)
|
||||
{
|
||||
const int index = desktopComponents.indexOf (c);
|
||||
jassert (index >= 0);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
int newIndex = -1;
|
||||
|
||||
if (! c->isAlwaysOnTop())
|
||||
{
|
||||
newIndex = desktopComponents.size();
|
||||
|
||||
while (newIndex > 0 && desktopComponents.getUnchecked (newIndex - 1)->isAlwaysOnTop())
|
||||
--newIndex;
|
||||
|
||||
--newIndex;
|
||||
}
|
||||
|
||||
desktopComponents.move (index, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Point<int> Desktop::getMousePosition()
|
||||
{
|
||||
return getMousePositionFloat().roundToInt();
|
||||
}
|
||||
|
||||
Point<float> Desktop::getMousePositionFloat()
|
||||
{
|
||||
return getInstance().getMainMouseSource().getScreenPosition();
|
||||
}
|
||||
|
||||
void Desktop::setMousePosition (Point<int> newPosition)
|
||||
{
|
||||
getInstance().getMainMouseSource().setScreenPosition (newPosition.toFloat());
|
||||
}
|
||||
|
||||
Point<int> Desktop::getLastMouseDownPosition()
|
||||
{
|
||||
return getInstance().getMainMouseSource().getLastMouseDownPosition().roundToInt();
|
||||
}
|
||||
|
||||
int Desktop::getMouseButtonClickCounter() const noexcept { return mouseClickCounter; }
|
||||
int Desktop::getMouseWheelMoveCounter() const noexcept { return mouseWheelCounter; }
|
||||
|
||||
void Desktop::incrementMouseClickCounter() noexcept { ++mouseClickCounter; }
|
||||
void Desktop::incrementMouseWheelCounter() noexcept { ++mouseWheelCounter; }
|
||||
|
||||
const Array<MouseInputSource>& Desktop::getMouseSources() const noexcept { return mouseSources->sourceArray; }
|
||||
int Desktop::getNumMouseSources() const noexcept { return mouseSources->sources.size(); }
|
||||
int Desktop::getNumDraggingMouseSources() const noexcept { return mouseSources->getNumDraggingMouseSources(); }
|
||||
MouseInputSource* Desktop::getMouseSource (int index) const noexcept { return mouseSources->getMouseSource (index); }
|
||||
MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept { return mouseSources->getDraggingMouseSource (index); }
|
||||
MouseInputSource Desktop::getMainMouseSource() const noexcept { return MouseInputSource (mouseSources->sources.getUnchecked(0)); }
|
||||
void Desktop::beginDragAutoRepeat (int interval) { mouseSources->beginDragAutoRepeat (interval); }
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::addFocusChangeListener (FocusChangeListener* const listener) { focusListeners.add (listener); }
|
||||
void Desktop::removeFocusChangeListener (FocusChangeListener* const listener) { focusListeners.remove (listener); }
|
||||
void Desktop::triggerFocusCallback() { triggerAsyncUpdate(); }
|
||||
|
||||
void Desktop::handleAsyncUpdate()
|
||||
{
|
||||
// The component may be deleted during this operation, but we'll use a SafePointer rather than a
|
||||
// BailOutChecker so that any remaining listeners will still get a callback (with a null pointer).
|
||||
WeakReference<Component> currentFocus (Component::getCurrentlyFocusedComponent());
|
||||
focusListeners.call (&FocusChangeListener::globalFocusChanged, currentFocus);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::resetTimer()
|
||||
{
|
||||
if (mouseListeners.size() == 0)
|
||||
stopTimer();
|
||||
else
|
||||
startTimer (100);
|
||||
|
||||
lastFakeMouseMove = getMousePositionFloat();
|
||||
}
|
||||
|
||||
ListenerList<MouseListener>& Desktop::getMouseListeners()
|
||||
{
|
||||
resetTimer();
|
||||
return mouseListeners;
|
||||
}
|
||||
|
||||
void Desktop::addGlobalMouseListener (MouseListener* const listener)
|
||||
{
|
||||
ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
mouseListeners.add (listener);
|
||||
resetTimer();
|
||||
}
|
||||
|
||||
void Desktop::removeGlobalMouseListener (MouseListener* const listener)
|
||||
{
|
||||
ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
mouseListeners.remove (listener);
|
||||
resetTimer();
|
||||
}
|
||||
|
||||
void Desktop::timerCallback()
|
||||
{
|
||||
if (lastFakeMouseMove != getMousePositionFloat())
|
||||
sendMouseMove();
|
||||
}
|
||||
|
||||
void Desktop::sendMouseMove()
|
||||
{
|
||||
if (! mouseListeners.isEmpty())
|
||||
{
|
||||
startTimer (20);
|
||||
|
||||
lastFakeMouseMove = getMousePositionFloat();
|
||||
|
||||
if (Component* const target = findComponentAt (lastFakeMouseMove.roundToInt()))
|
||||
{
|
||||
Component::BailOutChecker checker (target);
|
||||
const Point<float> pos (target->getLocalPoint (nullptr, lastFakeMouseMove));
|
||||
const Time now (Time::getCurrentTime());
|
||||
|
||||
const MouseEvent me (getMainMouseSource(), pos, ModifierKeys::getCurrentModifiers(),
|
||||
target, target, now, pos, now, 0, false);
|
||||
|
||||
if (me.mods.isAnyMouseButtonDown())
|
||||
mouseListeners.callChecked (checker, &MouseListener::mouseDrag, me);
|
||||
else
|
||||
mouseListeners.callChecked (checker, &MouseListener::mouseMove, me);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
Desktop::Displays::Displays (Desktop& desktop) { init (desktop); }
|
||||
Desktop::Displays::~Displays() {}
|
||||
|
||||
const Desktop::Displays::Display& Desktop::Displays::getMainDisplay() const noexcept
|
||||
{
|
||||
ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
jassert (displays.getReference(0).isMain);
|
||||
return displays.getReference(0);
|
||||
}
|
||||
|
||||
const Desktop::Displays::Display& Desktop::Displays::getDisplayContaining (Point<int> position) const noexcept
|
||||
{
|
||||
ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
const Display* best = &displays.getReference(0);
|
||||
double bestDistance = 1.0e10;
|
||||
|
||||
for (int i = displays.size(); --i >= 0;)
|
||||
{
|
||||
const Display& d = displays.getReference(i);
|
||||
|
||||
if (d.totalArea.contains (position))
|
||||
{
|
||||
best = &d;
|
||||
break;
|
||||
}
|
||||
|
||||
const double distance = d.totalArea.getCentre().getDistanceFrom (position);
|
||||
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
bestDistance = distance;
|
||||
best = &d;
|
||||
}
|
||||
}
|
||||
|
||||
return *best;
|
||||
}
|
||||
|
||||
RectangleList<int> Desktop::Displays::getRectangleList (bool userAreasOnly) const
|
||||
{
|
||||
ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
RectangleList<int> rl;
|
||||
|
||||
for (int i = 0; i < displays.size(); ++i)
|
||||
{
|
||||
const Display& d = displays.getReference(i);
|
||||
rl.addWithoutMerging (userAreasOnly ? d.userArea : d.totalArea);
|
||||
}
|
||||
|
||||
return rl;
|
||||
}
|
||||
|
||||
Rectangle<int> Desktop::Displays::getTotalBounds (bool userAreasOnly) const
|
||||
{
|
||||
return getRectangleList (userAreasOnly).getBounds();
|
||||
}
|
||||
|
||||
bool operator== (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept;
|
||||
bool operator== (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept
|
||||
{
|
||||
return d1.userArea == d2.userArea
|
||||
&& d1.totalArea == d2.totalArea
|
||||
&& d1.scale == d2.scale
|
||||
&& d1.isMain == d2.isMain;
|
||||
}
|
||||
|
||||
bool operator!= (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept;
|
||||
bool operator!= (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept
|
||||
{
|
||||
return ! (d1 == d2);
|
||||
}
|
||||
|
||||
void Desktop::Displays::init (Desktop& desktop)
|
||||
{
|
||||
findDisplays (desktop.getGlobalScaleFactor());
|
||||
jassert (displays.size() > 0);
|
||||
}
|
||||
|
||||
void Desktop::Displays::refresh()
|
||||
{
|
||||
Array<Display> oldDisplays;
|
||||
oldDisplays.swapWith (displays);
|
||||
|
||||
init (Desktop::getInstance());
|
||||
jassert (displays.size() > 0);
|
||||
|
||||
if (oldDisplays != displays)
|
||||
{
|
||||
for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
|
||||
if (ComponentPeer* const peer = ComponentPeer::getPeer (i))
|
||||
peer->handleScreenSizeChange();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::setKioskModeComponent (Component* componentToUse, const bool allowMenusAndBars)
|
||||
{
|
||||
if (kioskModeReentrant)
|
||||
return;
|
||||
|
||||
const ScopedValueSetter<bool> setter (kioskModeReentrant, true, false);
|
||||
|
||||
if (kioskModeComponent != componentToUse)
|
||||
{
|
||||
// agh! Don't delete or remove a component from the desktop while it's still the kiosk component!
|
||||
jassert (kioskModeComponent == nullptr || ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
|
||||
|
||||
if (Component* const oldKioskComp = kioskModeComponent)
|
||||
{
|
||||
kioskModeComponent = nullptr; // (to make sure that isKioskMode() returns false when resizing the old one)
|
||||
setKioskComponent (oldKioskComp, false, allowMenusAndBars);
|
||||
oldKioskComp->setBounds (kioskComponentOriginalBounds);
|
||||
}
|
||||
|
||||
kioskModeComponent = componentToUse;
|
||||
|
||||
if (kioskModeComponent != nullptr)
|
||||
{
|
||||
// Only components that are already on the desktop can be put into kiosk mode!
|
||||
jassert (ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
|
||||
|
||||
kioskComponentOriginalBounds = kioskModeComponent->getBounds();
|
||||
setKioskComponent (kioskModeComponent, true, allowMenusAndBars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::setOrientationsEnabled (const int newOrientations)
|
||||
{
|
||||
// Dodgy set of flags being passed here! Make sure you specify at least one permitted orientation.
|
||||
jassert (newOrientations != 0 && (newOrientations & ~allOrientations) == 0);
|
||||
|
||||
allowedOrientations = newOrientations;
|
||||
}
|
||||
|
||||
bool Desktop::isOrientationEnabled (const DisplayOrientation orientation) const noexcept
|
||||
{
|
||||
// Make sure you only pass one valid flag in here...
|
||||
jassert (orientation == upright || orientation == upsideDown
|
||||
|| orientation == rotatedClockwise || orientation == rotatedAntiClockwise);
|
||||
|
||||
return (allowedOrientations & orientation) != 0;
|
||||
}
|
||||
|
||||
void Desktop::setGlobalScaleFactor (float newScaleFactor) noexcept
|
||||
{
|
||||
ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
if (masterScaleFactor != newScaleFactor)
|
||||
{
|
||||
masterScaleFactor = newScaleFactor;
|
||||
displays->refresh();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_DESKTOP_H_INCLUDED
|
||||
#define JUCE_DESKTOP_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Classes can implement this interface and register themselves with the Desktop class
|
||||
to receive callbacks when the currently focused component changes.
|
||||
|
||||
@see Desktop::addFocusChangeListener, Desktop::removeFocusChangeListener
|
||||
*/
|
||||
class JUCE_API FocusChangeListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~FocusChangeListener() {}
|
||||
|
||||
/** Callback to indicate that the currently focused component has changed. */
|
||||
virtual void globalFocusChanged (Component* focusedComponent) = 0;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Describes and controls aspects of the computer's desktop.
|
||||
|
||||
*/
|
||||
class JUCE_API Desktop : private DeletedAtShutdown,
|
||||
private Timer,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** There's only one desktop object, and this method will return it. */
|
||||
static Desktop& JUCE_CALLTYPE getInstance();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the mouse position.
|
||||
|
||||
The coordinates are relative to the top-left of the main monitor.
|
||||
|
||||
Note that this is just a shortcut for calling getMainMouseSource().getScreenPosition(), and
|
||||
you should only resort to grabbing the global mouse position if there's really no
|
||||
way to get the coordinates via a mouse event callback instead.
|
||||
*/
|
||||
static Point<int> getMousePosition();
|
||||
|
||||
/** Makes the mouse pointer jump to a given location.
|
||||
The coordinates are relative to the top-left of the main monitor.
|
||||
*/
|
||||
static void setMousePosition (Point<int> newPosition);
|
||||
|
||||
/** Returns the last position at which a mouse button was pressed.
|
||||
|
||||
Note that this is just a shortcut for calling getMainMouseSource().getLastMouseDownPosition(),
|
||||
and in a multi-touch environment, it doesn't make much sense. ALWAYS prefer to
|
||||
get this information via other means, such as MouseEvent::getMouseDownScreenPosition()
|
||||
if possible, and only ever call this as a last resort.
|
||||
*/
|
||||
static Point<int> getLastMouseDownPosition();
|
||||
|
||||
/** Returns the number of times the mouse button has been clicked since the app started.
|
||||
Each mouse-down event increments this number by 1.
|
||||
@see getMouseWheelMoveCounter
|
||||
*/
|
||||
int getMouseButtonClickCounter() const noexcept;
|
||||
|
||||
/** Returns the number of times the mouse wheel has been moved since the app started.
|
||||
Each mouse-wheel event increments this number by 1.
|
||||
@see getMouseButtonClickCounter
|
||||
*/
|
||||
int getMouseWheelMoveCounter() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** This lets you prevent the screensaver from becoming active.
|
||||
|
||||
Handy if you're running some sort of presentation app where having a screensaver
|
||||
appear would be annoying.
|
||||
|
||||
Pass false to disable the screensaver, and true to re-enable it. (Note that this
|
||||
won't enable a screensaver unless the user has actually set one up).
|
||||
|
||||
The disablement will only happen while the Juce application is the foreground
|
||||
process - if another task is running in front of it, then the screensaver will
|
||||
be unaffected.
|
||||
|
||||
@see isScreenSaverEnabled
|
||||
*/
|
||||
static void setScreenSaverEnabled (bool isEnabled);
|
||||
|
||||
/** Returns true if the screensaver has not been turned off.
|
||||
|
||||
This will return the last value passed into setScreenSaverEnabled(). Note that
|
||||
it won't tell you whether the user is actually using a screen saver, just
|
||||
whether this app is deliberately preventing one from running.
|
||||
|
||||
@see setScreenSaverEnabled
|
||||
*/
|
||||
static bool isScreenSaverEnabled();
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a MouseListener that will receive all mouse events that occur on
|
||||
any component.
|
||||
|
||||
@see removeGlobalMouseListener
|
||||
*/
|
||||
void addGlobalMouseListener (MouseListener* listener);
|
||||
|
||||
/** Unregisters a MouseListener that was added with the addGlobalMouseListener()
|
||||
method.
|
||||
|
||||
@see addGlobalMouseListener
|
||||
*/
|
||||
void removeGlobalMouseListener (MouseListener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a MouseListener that will receive a callback whenever the focused
|
||||
component changes.
|
||||
*/
|
||||
void addFocusChangeListener (FocusChangeListener* listener);
|
||||
|
||||
/** Unregisters a listener that was added with addFocusChangeListener(). */
|
||||
void removeFocusChangeListener (FocusChangeListener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Takes a component and makes it full-screen, removing the taskbar, dock, etc.
|
||||
|
||||
The component must already be on the desktop for this method to work. It will
|
||||
be resized to completely fill the screen and any extraneous taskbars, menu bars,
|
||||
etc will be hidden.
|
||||
|
||||
To exit kiosk mode, just call setKioskModeComponent (nullptr). When this is called,
|
||||
the component that's currently being used will be resized back to the size
|
||||
and position it was in before being put into this mode.
|
||||
|
||||
If allowMenusAndBars is true, things like the menu and dock (on mac) are still
|
||||
allowed to pop up when the mouse moves onto them. If this is false, it'll try
|
||||
to hide as much on-screen paraphenalia as possible.
|
||||
*/
|
||||
void setKioskModeComponent (Component* componentToUse,
|
||||
bool allowMenusAndBars = true);
|
||||
|
||||
/** Returns the component that is currently being used in kiosk-mode.
|
||||
|
||||
This is the component that was last set by setKioskModeComponent(). If none
|
||||
has been set, this returns nullptr.
|
||||
*/
|
||||
Component* getKioskModeComponent() const noexcept { return kioskModeComponent; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of components that are currently active as top-level
|
||||
desktop windows.
|
||||
|
||||
@see getComponent, Component::addToDesktop
|
||||
*/
|
||||
int getNumComponents() const noexcept;
|
||||
|
||||
/** Returns one of the top-level desktop window components.
|
||||
|
||||
The index is from 0 to getNumComponents() - 1. This could return 0 if the
|
||||
index is out-of-range.
|
||||
|
||||
@see getNumComponents, Component::addToDesktop
|
||||
*/
|
||||
Component* getComponent (int index) const noexcept;
|
||||
|
||||
/** Finds the component at a given screen location.
|
||||
|
||||
This will drill down into top-level windows to find the child component at
|
||||
the given position.
|
||||
|
||||
Returns nullptr if the coordinates are inside a non-Juce window.
|
||||
*/
|
||||
Component* findComponentAt (Point<int> screenPosition) const;
|
||||
|
||||
/** The Desktop object has a ComponentAnimator instance which can be used for performing
|
||||
your animations.
|
||||
|
||||
Having a single shared ComponentAnimator object makes it more efficient when multiple
|
||||
components are being moved around simultaneously. It's also more convenient than having
|
||||
to manage your own instance of one.
|
||||
|
||||
@see ComponentAnimator
|
||||
*/
|
||||
ComponentAnimator& getAnimator() noexcept { return animator; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current default look-and-feel for components which don't have one
|
||||
explicitly set.
|
||||
@see setDefaultLookAndFeel
|
||||
*/
|
||||
LookAndFeel& getDefaultLookAndFeel() noexcept;
|
||||
|
||||
/** Changes the default look-and-feel.
|
||||
@param newDefaultLookAndFeel the new look-and-feel object to use - if this is
|
||||
set to nullptr, it will revert to using the system's
|
||||
default one. The object passed-in must be deleted by the
|
||||
caller when it's no longer needed.
|
||||
@see getDefaultLookAndFeel
|
||||
*/
|
||||
void setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel);
|
||||
|
||||
//==============================================================================
|
||||
/** Provides access to the array of mouse sources, for iteration.
|
||||
In a traditional single-mouse system, there might be only one MouseInputSource. On a
|
||||
multi-touch system, there could be one input source per potential finger. The number
|
||||
of mouse sources returned here may increase dynamically as the program runs.
|
||||
To find out how many mouse events are currently happening, use getNumDraggingMouseSources().
|
||||
*/
|
||||
const Array<MouseInputSource>& getMouseSources() const noexcept;
|
||||
|
||||
/** Returns the number of MouseInputSource objects the system has at its disposal.
|
||||
In a traditional single-mouse system, there might be only one MouseInputSource. On a
|
||||
multi-touch system, there could be one input source per potential finger. The number
|
||||
of mouse sources returned here may increase dynamically as the program runs.
|
||||
To find out how many mouse events are currently happening, use getNumDraggingMouseSources().
|
||||
@see getMouseSource
|
||||
*/
|
||||
int getNumMouseSources() const noexcept;
|
||||
|
||||
/** Returns one of the system's MouseInputSource objects.
|
||||
The index should be from 0 to getNumMouseSources() - 1. Out-of-range indexes will return
|
||||
a null pointer.
|
||||
In a traditional single-mouse system, there might be only one object. On a multi-touch
|
||||
system, there could be one input source per potential finger.
|
||||
*/
|
||||
MouseInputSource* getMouseSource (int index) const noexcept;
|
||||
|
||||
/** Returns the main mouse input device that the system is using.
|
||||
@see getNumMouseSources()
|
||||
*/
|
||||
MouseInputSource getMainMouseSource() const noexcept;
|
||||
|
||||
/** Returns the number of mouse-sources that are currently being dragged.
|
||||
In a traditional single-mouse system, this will be 0 or 1, depending on whether a
|
||||
juce component has the button down on it. In a multi-touch system, this could
|
||||
be any number from 0 to the number of simultaneous touches that can be detected.
|
||||
*/
|
||||
int getNumDraggingMouseSources() const noexcept;
|
||||
|
||||
/** Returns one of the mouse sources that's currently being dragged.
|
||||
The index should be between 0 and getNumDraggingMouseSources() - 1. If the index is
|
||||
out of range, or if no mice or fingers are down, this will return a null pointer.
|
||||
*/
|
||||
MouseInputSource* getDraggingMouseSource (int index) const noexcept;
|
||||
|
||||
/** Ensures that a non-stop stream of mouse-drag events will be sent during the
|
||||
current mouse-drag operation.
|
||||
|
||||
This allows you to make sure that mouseDrag() events are sent continuously, even
|
||||
when the mouse isn't moving. This can be useful for things like auto-scrolling
|
||||
components when the mouse is near an edge.
|
||||
|
||||
Call this method during a mouseDown() or mouseDrag() callback, specifying the
|
||||
minimum interval between consecutive mouse drag callbacks. The callbacks
|
||||
will continue until the mouse is released, and then the interval will be reset,
|
||||
so you need to make sure it's called every time you begin a drag event.
|
||||
Passing an interval of 0 or less will cancel the auto-repeat.
|
||||
|
||||
@see mouseDrag
|
||||
*/
|
||||
void beginDragAutoRepeat (int millisecondsBetweenCallbacks);
|
||||
|
||||
//==============================================================================
|
||||
/** In a tablet device which can be turned around, this is used to inidicate the orientation. */
|
||||
enum DisplayOrientation
|
||||
{
|
||||
upright = 1, /**< Indicates that the display is the normal way up. */
|
||||
upsideDown = 2, /**< Indicates that the display is upside-down. */
|
||||
rotatedClockwise = 4, /**< Indicates that the display is turned 90 degrees clockwise from its upright position. */
|
||||
rotatedAntiClockwise = 8, /**< Indicates that the display is turned 90 degrees anti-clockwise from its upright position. */
|
||||
|
||||
allOrientations = 1 + 2 + 4 + 8 /**< A combination of all the orientation values */
|
||||
};
|
||||
|
||||
/** In a tablet device which can be turned around, this returns the current orientation. */
|
||||
DisplayOrientation getCurrentOrientation() const;
|
||||
|
||||
/** Sets which orientations the display is allowed to auto-rotate to.
|
||||
|
||||
For devices that support rotating desktops, this lets you specify which of the orientations your app can use.
|
||||
|
||||
The parameter is a bitwise or-ed combination of the values in DisplayOrientation, and must contain at least one
|
||||
set bit.
|
||||
*/
|
||||
void setOrientationsEnabled (int allowedOrientations);
|
||||
|
||||
/** Returns whether the display is allowed to auto-rotate to the given orientation.
|
||||
Each orientation can be enabled using setOrientationEnabled(). By default, all orientations are allowed.
|
||||
*/
|
||||
bool isOrientationEnabled (DisplayOrientation orientation) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
class JUCE_API Displays
|
||||
{
|
||||
public:
|
||||
/** Contains details about a display device. */
|
||||
struct Display
|
||||
{
|
||||
/** This is the bounds of the area of this display which isn't covered by
|
||||
OS-dependent objects like the taskbar, menu bar, etc. */
|
||||
Rectangle<int> userArea;
|
||||
|
||||
/** This is the total physical area of this display, including any taskbars, etc */
|
||||
Rectangle<int> totalArea;
|
||||
|
||||
/** This is the scale-factor of this display.
|
||||
If you create a component with size 1x1, this scale factor indicates the actual
|
||||
size of the component in terms of physical pixels.
|
||||
For higher-resolution displays, it may be a value greater than 1.0
|
||||
*/
|
||||
double scale;
|
||||
|
||||
/** The DPI of the display.
|
||||
This is the number of physical pixels per inch. To get the number of logical
|
||||
pixels per inch, divide this by the Display::scale value.
|
||||
*/
|
||||
double dpi;
|
||||
|
||||
/** This will be true if this is the user's main screen. */
|
||||
bool isMain;
|
||||
};
|
||||
|
||||
/** Returns the display which acts as user's main screen. */
|
||||
const Display& getMainDisplay() const noexcept;
|
||||
|
||||
/** Returns the display which contains a particular point.
|
||||
If the point lies outside all the displays, the nearest one will be returned.
|
||||
*/
|
||||
const Display& getDisplayContaining (Point<int> position) const noexcept;
|
||||
|
||||
/** Returns a RectangleList made up of all the displays. */
|
||||
RectangleList<int> getRectangleList (bool userAreasOnly) const;
|
||||
|
||||
/** Returns the smallest bounding box which contains all the displays. */
|
||||
Rectangle<int> getTotalBounds (bool userAreasOnly) const;
|
||||
|
||||
/** The list of displays. */
|
||||
Array<Display> displays;
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/** @internal */
|
||||
void refresh();
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class Desktop;
|
||||
friend struct ContainerDeletePolicy<Displays>;
|
||||
Displays (Desktop&);
|
||||
~Displays();
|
||||
|
||||
void init (Desktop&);
|
||||
void findDisplays (float masterScale);
|
||||
};
|
||||
|
||||
const Displays& getDisplays() const noexcept { return *displays; }
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a global scale factor to be used for all desktop windows.
|
||||
Setting this will also scale the monitor sizes that are returned by getDisplays().
|
||||
*/
|
||||
void setGlobalScaleFactor (float newScaleFactor) noexcept;
|
||||
|
||||
/** Returns the current global scale factor, as set by setGlobalScaleFactor().
|
||||
@see setGlobalScaleFactor
|
||||
*/
|
||||
float getGlobalScaleFactor() const noexcept { return masterScaleFactor; }
|
||||
|
||||
//==============================================================================
|
||||
/** True if the OS supports semitransparent windows */
|
||||
static bool canUseSemiTransparentWindows() noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static Desktop* instance;
|
||||
|
||||
friend class Component;
|
||||
friend class ComponentPeer;
|
||||
friend class MouseInputSourceInternal;
|
||||
friend class DeletedAtShutdown;
|
||||
friend class TopLevelWindowManager;
|
||||
|
||||
ScopedPointer<MouseInputSource::SourceList> mouseSources;
|
||||
|
||||
ListenerList<MouseListener> mouseListeners;
|
||||
ListenerList<FocusChangeListener> focusListeners;
|
||||
|
||||
Array<Component*> desktopComponents;
|
||||
Array<ComponentPeer*> peers;
|
||||
|
||||
ScopedPointer<Displays> displays;
|
||||
|
||||
Point<float> lastFakeMouseMove;
|
||||
void sendMouseMove();
|
||||
|
||||
int mouseClickCounter, mouseWheelCounter;
|
||||
void incrementMouseClickCounter() noexcept;
|
||||
void incrementMouseWheelCounter() noexcept;
|
||||
|
||||
ScopedPointer<LookAndFeel> defaultLookAndFeel;
|
||||
WeakReference<LookAndFeel> currentLookAndFeel;
|
||||
|
||||
Component* kioskModeComponent;
|
||||
Rectangle<int> kioskComponentOriginalBounds;
|
||||
bool kioskModeReentrant;
|
||||
|
||||
int allowedOrientations;
|
||||
float masterScaleFactor;
|
||||
|
||||
ComponentAnimator animator;
|
||||
|
||||
void timerCallback() override;
|
||||
void resetTimer();
|
||||
ListenerList<MouseListener>& getMouseListeners();
|
||||
|
||||
void addDesktopComponent (Component*);
|
||||
void removeDesktopComponent (Component*);
|
||||
void componentBroughtToFront (Component*);
|
||||
|
||||
void setKioskComponent (Component*, bool shouldBeEnabled, bool allowMenusAndBars);
|
||||
|
||||
void triggerFocusCallback();
|
||||
void handleAsyncUpdate() override;
|
||||
|
||||
static Point<float> getMousePositionFloat();
|
||||
|
||||
static double getDefaultMasterScale();
|
||||
|
||||
Desktop();
|
||||
~Desktop();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Desktop)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_DESKTOP_H_INCLUDED
|
||||
|
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class ModalComponentManager::ModalItem : public ComponentMovementWatcher
|
||||
{
|
||||
public:
|
||||
ModalItem (Component* const comp, const bool autoDelete_)
|
||||
: ComponentMovementWatcher (comp),
|
||||
component (comp), returnValue (0),
|
||||
isActive (true), autoDelete (autoDelete_)
|
||||
{
|
||||
jassert (comp != nullptr);
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool, bool) override {}
|
||||
|
||||
void componentPeerChanged() override
|
||||
{
|
||||
if (! component->isShowing())
|
||||
cancel();
|
||||
}
|
||||
|
||||
void componentVisibilityChanged() override
|
||||
{
|
||||
if (! component->isShowing())
|
||||
cancel();
|
||||
}
|
||||
|
||||
void componentBeingDeleted (Component& comp) override
|
||||
{
|
||||
ComponentMovementWatcher::componentBeingDeleted (comp);
|
||||
|
||||
if (component == &comp || comp.isParentOf (component))
|
||||
{
|
||||
autoDelete = false;
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
if (isActive)
|
||||
{
|
||||
isActive = false;
|
||||
|
||||
if (ModalComponentManager* mcm = ModalComponentManager::getInstanceWithoutCreating())
|
||||
mcm->triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
Component* component;
|
||||
OwnedArray<Callback> callbacks;
|
||||
int returnValue;
|
||||
bool isActive, autoDelete;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (ModalItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ModalComponentManager::ModalComponentManager()
|
||||
{
|
||||
}
|
||||
|
||||
ModalComponentManager::~ModalComponentManager()
|
||||
{
|
||||
stack.clear();
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
juce_ImplementSingleton_SingleThreaded (ModalComponentManager);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void ModalComponentManager::startModal (Component* component, bool autoDelete)
|
||||
{
|
||||
if (component != nullptr)
|
||||
stack.add (new ModalItem (component, autoDelete));
|
||||
}
|
||||
|
||||
void ModalComponentManager::attachCallback (Component* component, Callback* callback)
|
||||
{
|
||||
if (callback != nullptr)
|
||||
{
|
||||
ScopedPointer<Callback> callbackDeleter (callback);
|
||||
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
ModalItem* const item = stack.getUnchecked(i);
|
||||
|
||||
if (item->component == component)
|
||||
{
|
||||
item->callbacks.add (callback);
|
||||
callbackDeleter.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModalComponentManager::endModal (Component* component)
|
||||
{
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
ModalItem* const item = stack.getUnchecked(i);
|
||||
|
||||
if (item->component == component)
|
||||
item->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void ModalComponentManager::endModal (Component* component, int returnValue)
|
||||
{
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
ModalItem* const item = stack.getUnchecked(i);
|
||||
|
||||
if (item->component == component)
|
||||
{
|
||||
item->returnValue = returnValue;
|
||||
item->cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ModalComponentManager::getNumModalComponents() const
|
||||
{
|
||||
int n = 0;
|
||||
for (int i = 0; i < stack.size(); ++i)
|
||||
if (stack.getUnchecked(i)->isActive)
|
||||
++n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
Component* ModalComponentManager::getModalComponent (const int index) const
|
||||
{
|
||||
int n = 0;
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
const ModalItem* const item = stack.getUnchecked(i);
|
||||
if (item->isActive)
|
||||
if (n++ == index)
|
||||
return item->component;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ModalComponentManager::isModal (Component* const comp) const
|
||||
{
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
const ModalItem* const item = stack.getUnchecked(i);
|
||||
if (item->isActive && item->component == comp)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModalComponentManager::isFrontModalComponent (Component* const comp) const
|
||||
{
|
||||
return comp == getModalComponent (0);
|
||||
}
|
||||
|
||||
void ModalComponentManager::handleAsyncUpdate()
|
||||
{
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
const ModalItem* const item = stack.getUnchecked(i);
|
||||
|
||||
if (! item->isActive)
|
||||
{
|
||||
ScopedPointer<ModalItem> deleter (stack.removeAndReturn (i));
|
||||
Component::SafePointer<Component> compToDelete (item->autoDelete ? item->component : nullptr);
|
||||
|
||||
for (int j = item->callbacks.size(); --j >= 0;)
|
||||
item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue);
|
||||
|
||||
compToDelete.deleteAndZero();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModalComponentManager::bringModalComponentsToFront (bool topOneShouldGrabFocus)
|
||||
{
|
||||
ComponentPeer* lastOne = nullptr;
|
||||
|
||||
for (int i = 0; i < getNumModalComponents(); ++i)
|
||||
{
|
||||
Component* const c = getModalComponent (i);
|
||||
|
||||
if (c == nullptr)
|
||||
break;
|
||||
|
||||
ComponentPeer* peer = c->getPeer();
|
||||
|
||||
if (peer != nullptr && peer != lastOne)
|
||||
{
|
||||
if (lastOne == nullptr)
|
||||
{
|
||||
peer->toFront (topOneShouldGrabFocus);
|
||||
|
||||
if (topOneShouldGrabFocus)
|
||||
peer->grabFocus();
|
||||
}
|
||||
else
|
||||
peer->toBehind (lastOne);
|
||||
|
||||
lastOne = peer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ModalComponentManager::cancelAllModalComponents()
|
||||
{
|
||||
const int numModal = getNumModalComponents();
|
||||
|
||||
for (int i = numModal; --i >= 0;)
|
||||
if (Component* const c = getModalComponent(i))
|
||||
c->exitModalState (0);
|
||||
|
||||
return numModal > 0;
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
class ModalComponentManager::ReturnValueRetriever : public ModalComponentManager::Callback
|
||||
{
|
||||
public:
|
||||
ReturnValueRetriever (int& v, bool& done) : value (v), finished (done) {}
|
||||
|
||||
void modalStateFinished (int returnValue)
|
||||
{
|
||||
finished = true;
|
||||
value = returnValue;
|
||||
}
|
||||
|
||||
private:
|
||||
int& value;
|
||||
bool& finished;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ReturnValueRetriever)
|
||||
};
|
||||
|
||||
int ModalComponentManager::runEventLoopForCurrentComponent()
|
||||
{
|
||||
// This can only be run from the message thread!
|
||||
jassert (MessageManager::getInstance()->isThisTheMessageThread());
|
||||
|
||||
int returnValue = 0;
|
||||
|
||||
if (Component* currentlyModal = getModalComponent (0))
|
||||
{
|
||||
FocusRestorer focusRestorer;
|
||||
|
||||
bool finished = false;
|
||||
attachCallback (currentlyModal, new ReturnValueRetriever (returnValue, finished));
|
||||
|
||||
JUCE_TRY
|
||||
{
|
||||
while (! finished)
|
||||
{
|
||||
if (! MessageManager::getInstance()->runDispatchLoopUntil (20))
|
||||
break;
|
||||
}
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_MODALCOMPONENTMANAGER_H_INCLUDED
|
||||
#define JUCE_MODALCOMPONENTMANAGER_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Manages the system's stack of modal components.
|
||||
|
||||
Normally you'll just use the Component methods to invoke modal states in components,
|
||||
and won't have to deal with this class directly, but this is the singleton object that's
|
||||
used internally to manage the stack.
|
||||
|
||||
@see Component::enterModalState, Component::exitModalState, Component::isCurrentlyModal,
|
||||
Component::getCurrentlyModalComponent, Component::isCurrentlyBlockedByAnotherModalComponent
|
||||
*/
|
||||
class JUCE_API ModalComponentManager : private AsyncUpdater,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Receives callbacks when a modal component is dismissed.
|
||||
|
||||
You can register a callback using Component::enterModalState() or
|
||||
ModalComponentManager::attachCallback().
|
||||
|
||||
For some quick ways of creating callback objects, see the ModalCallbackFunction class.
|
||||
@see ModalCallbackFunction
|
||||
*/
|
||||
class Callback
|
||||
{
|
||||
public:
|
||||
/** */
|
||||
Callback() {}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~Callback() {}
|
||||
|
||||
/** Called to indicate that a modal component has been dismissed.
|
||||
|
||||
You can register a callback using Component::enterModalState() or
|
||||
ModalComponentManager::attachCallback().
|
||||
|
||||
The returnValue parameter is the value that was passed to Component::exitModalState()
|
||||
when the component was dismissed.
|
||||
|
||||
The callback object will be deleted shortly after this method is called.
|
||||
*/
|
||||
virtual void modalStateFinished (int returnValue) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of components currently being shown modally.
|
||||
@see getModalComponent
|
||||
*/
|
||||
int getNumModalComponents() const;
|
||||
|
||||
/** Returns one of the components being shown modally.
|
||||
An index of 0 is the most recently-shown, topmost component.
|
||||
*/
|
||||
Component* getModalComponent (int index) const;
|
||||
|
||||
/** Returns true if the specified component is in a modal state. */
|
||||
bool isModal (Component* component) const;
|
||||
|
||||
/** Returns true if the specified component is currently the topmost modal component. */
|
||||
bool isFrontModalComponent (Component* component) const;
|
||||
|
||||
/** Adds a new callback that will be called when the specified modal component is dismissed.
|
||||
|
||||
If the component is modal, then when it is dismissed, either by being hidden, or by calling
|
||||
Component::exitModalState(), then the Callback::modalStateFinished() method will be
|
||||
called.
|
||||
|
||||
Each component can have any number of callbacks associated with it, and this one is added
|
||||
to that list.
|
||||
|
||||
The object that is passed in will be deleted by the manager when it's no longer needed. If
|
||||
the given component is not currently modal, the callback object is deleted immediately and
|
||||
no action is taken.
|
||||
*/
|
||||
void attachCallback (Component* component, Callback* callback);
|
||||
|
||||
/** Brings any modal components to the front. */
|
||||
void bringModalComponentsToFront (bool topOneShouldGrabFocus = true);
|
||||
|
||||
/** Calls exitModalState (0) on any components that are currently modal.
|
||||
@returns true if any components were modal; false if nothing needed cancelling
|
||||
*/
|
||||
bool cancelAllModalComponents();
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Runs the event loop until the currently topmost modal component is dismissed, and
|
||||
returns the exit code for that component.
|
||||
*/
|
||||
int runEventLoopForCurrentComponent();
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
juce_DeclareSingleton_SingleThreaded_Minimal (ModalComponentManager);
|
||||
|
||||
protected:
|
||||
/** Creates a ModalComponentManager.
|
||||
You shouldn't ever call the constructor - it's a singleton, so use ModalComponentManager::getInstance()
|
||||
*/
|
||||
ModalComponentManager();
|
||||
|
||||
/** Destructor. */
|
||||
~ModalComponentManager();
|
||||
|
||||
/** @internal */
|
||||
void handleAsyncUpdate() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class ModalItem;
|
||||
class ReturnValueRetriever;
|
||||
|
||||
friend class Component;
|
||||
friend struct ContainerDeletePolicy<ModalItem>;
|
||||
OwnedArray<ModalItem> stack;
|
||||
|
||||
void startModal (Component*, bool autoDelete);
|
||||
void endModal (Component*, int returnValue);
|
||||
void endModal (Component*);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ModalComponentManager)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class provides some handy utility methods for creating ModalComponentManager::Callback
|
||||
objects that will invoke a static function with some parameters when a modal component is dismissed.
|
||||
*/
|
||||
class ModalCallbackFunction
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** This is a utility function to create a ModalComponentManager::Callback that will
|
||||
call a static function with a parameter.
|
||||
|
||||
The function that you supply must take two parameters - the first being an int, which is
|
||||
the result code that was used when the modal component was dismissed, and the second
|
||||
can be a custom type. Note that this custom value will be copied and stored, so it must
|
||||
be a primitive type or a class that provides copy-by-value semantics.
|
||||
|
||||
E.g. @code
|
||||
static void myCallbackFunction (int modalResult, double customValue)
|
||||
{
|
||||
if (modalResult == 1)
|
||||
doSomethingWith (customValue);
|
||||
}
|
||||
|
||||
Component* someKindOfComp;
|
||||
...
|
||||
someKindOfComp->enterModalState (ModalCallbackFunction::create (myCallbackFunction, 3.0));
|
||||
@endcode
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <typename ParamType>
|
||||
static ModalComponentManager::Callback* create (void (*functionToCall) (int, ParamType),
|
||||
ParamType parameterValue)
|
||||
{
|
||||
return new FunctionCaller1 <ParamType> (functionToCall, parameterValue);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** This is a utility function to create a ModalComponentManager::Callback that will
|
||||
call a static function with two custom parameters.
|
||||
|
||||
The function that you supply must take three parameters - the first being an int, which is
|
||||
the result code that was used when the modal component was dismissed, and the next two are
|
||||
your custom types. Note that these custom values will be copied and stored, so they must
|
||||
be primitive types or classes that provide copy-by-value semantics.
|
||||
|
||||
E.g. @code
|
||||
static void myCallbackFunction (int modalResult, double customValue1, String customValue2)
|
||||
{
|
||||
if (modalResult == 1)
|
||||
doSomethingWith (customValue1, customValue2);
|
||||
}
|
||||
|
||||
Component* someKindOfComp;
|
||||
...
|
||||
someKindOfComp->enterModalState (ModalCallbackFunction::create (myCallbackFunction, 3.0, String ("xyz")));
|
||||
@endcode
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <typename ParamType1, typename ParamType2>
|
||||
static ModalComponentManager::Callback* withParam (void (*functionToCall) (int, ParamType1, ParamType2),
|
||||
ParamType1 parameterValue1,
|
||||
ParamType2 parameterValue2)
|
||||
{
|
||||
return new FunctionCaller2 <ParamType1, ParamType2> (functionToCall, parameterValue1, parameterValue2);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** This is a utility function to create a ModalComponentManager::Callback that will
|
||||
call a static function with a component.
|
||||
|
||||
The function that you supply must take two parameters - the first being an int, which is
|
||||
the result code that was used when the modal component was dismissed, and the second
|
||||
can be a Component class. The component will be stored as a WeakReference, so that if it gets
|
||||
deleted before this callback is invoked, the pointer that is passed to the function will be null.
|
||||
|
||||
E.g. @code
|
||||
static void myCallbackFunction (int modalResult, Slider* mySlider)
|
||||
{
|
||||
if (modalResult == 1 && mySlider != nullptr) // (must check that mySlider isn't null in case it was deleted..)
|
||||
mySlider->setValue (0.0);
|
||||
}
|
||||
|
||||
Component* someKindOfComp;
|
||||
Slider* mySlider;
|
||||
...
|
||||
someKindOfComp->enterModalState (ModalCallbackFunction::forComponent (myCallbackFunction, mySlider));
|
||||
@endcode
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <class ComponentType>
|
||||
static ModalComponentManager::Callback* forComponent (void (*functionToCall) (int, ComponentType*),
|
||||
ComponentType* component)
|
||||
{
|
||||
return new ComponentCaller1 <ComponentType> (functionToCall, component);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a ModalComponentManager::Callback that will call a static function with a component.
|
||||
|
||||
The function that you supply must take three parameters - the first being an int, which is
|
||||
the result code that was used when the modal component was dismissed, the second being a Component
|
||||
class, and the third being a custom type (which must be a primitive type or have copy-by-value semantics).
|
||||
The component will be stored as a WeakReference, so that if it gets deleted before this callback is
|
||||
invoked, the pointer that is passed into the function will be null.
|
||||
|
||||
E.g. @code
|
||||
static void myCallbackFunction (int modalResult, Slider* mySlider, String customParam)
|
||||
{
|
||||
if (modalResult == 1 && mySlider != nullptr) // (must check that mySlider isn't null in case it was deleted..)
|
||||
mySlider->setName (customParam);
|
||||
}
|
||||
|
||||
Component* someKindOfComp;
|
||||
Slider* mySlider;
|
||||
...
|
||||
someKindOfComp->enterModalState (ModalCallbackFunction::forComponent (myCallbackFunction, mySlider, String ("hello")));
|
||||
@endcode
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <class ComponentType, typename ParamType>
|
||||
static ModalComponentManager::Callback* forComponent (void (*functionToCall) (int, ComponentType*, ParamType),
|
||||
ComponentType* component,
|
||||
ParamType param)
|
||||
{
|
||||
return new ComponentCaller2 <ComponentType, ParamType> (functionToCall, component, param);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename ParamType>
|
||||
class FunctionCaller1 : public ModalComponentManager::Callback
|
||||
{
|
||||
public:
|
||||
typedef void (*FunctionType) (int, ParamType);
|
||||
|
||||
FunctionCaller1 (FunctionType& f, ParamType& p1)
|
||||
: function (f), param (p1) {}
|
||||
|
||||
void modalStateFinished (int returnValue) { function (returnValue, param); }
|
||||
|
||||
private:
|
||||
const FunctionType function;
|
||||
ParamType param;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller1)
|
||||
};
|
||||
|
||||
template <typename ParamType1, typename ParamType2>
|
||||
class FunctionCaller2 : public ModalComponentManager::Callback
|
||||
{
|
||||
public:
|
||||
typedef void (*FunctionType) (int, ParamType1, ParamType2);
|
||||
|
||||
FunctionCaller2 (FunctionType& f, ParamType1& p1, ParamType2& p2)
|
||||
: function (f), param1 (p1), param2 (p2) {}
|
||||
|
||||
void modalStateFinished (int returnValue) { function (returnValue, param1, param2); }
|
||||
|
||||
private:
|
||||
const FunctionType function;
|
||||
ParamType1 param1;
|
||||
ParamType2 param2;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller2)
|
||||
};
|
||||
|
||||
template <typename ComponentType>
|
||||
class ComponentCaller1 : public ModalComponentManager::Callback
|
||||
{
|
||||
public:
|
||||
typedef void (*FunctionType) (int, ComponentType*);
|
||||
|
||||
ComponentCaller1 (FunctionType& f, ComponentType* c)
|
||||
: function (f), comp (c) {}
|
||||
|
||||
void modalStateFinished (int returnValue)
|
||||
{
|
||||
function (returnValue, static_cast <ComponentType*> (comp.get()));
|
||||
}
|
||||
|
||||
private:
|
||||
const FunctionType function;
|
||||
WeakReference<Component> comp;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller1)
|
||||
};
|
||||
|
||||
template <typename ComponentType, typename ParamType1>
|
||||
class ComponentCaller2 : public ModalComponentManager::Callback
|
||||
{
|
||||
public:
|
||||
typedef void (*FunctionType) (int, ComponentType*, ParamType1);
|
||||
|
||||
ComponentCaller2 (FunctionType& f, ComponentType* c, ParamType1 p1)
|
||||
: function (f), comp (c), param1 (p1) {}
|
||||
|
||||
void modalStateFinished (int returnValue)
|
||||
{
|
||||
function (returnValue, static_cast <ComponentType*> (comp.get()), param1);
|
||||
}
|
||||
|
||||
private:
|
||||
const FunctionType function;
|
||||
WeakReference<Component> comp;
|
||||
ParamType1 param1;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller2)
|
||||
};
|
||||
|
||||
ModalCallbackFunction();
|
||||
~ModalCallbackFunction();
|
||||
JUCE_DECLARE_NON_COPYABLE (ModalCallbackFunction)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_MODALCOMPONENTMANAGER_H_INCLUDED
|
||||
Loading…
Add table
Add a link
Reference in a new issue