migrating to the latest JUCE version
This commit is contained in:
@ -1,72 +1,72 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API CachedComponentImage
|
||||
{
|
||||
public:
|
||||
CachedComponentImage() = default;
|
||||
virtual ~CachedComponentImage() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** 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;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API CachedComponentImage
|
||||
{
|
||||
public:
|
||||
CachedComponentImage() = default;
|
||||
virtual ~CachedComponentImage() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** 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;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,38 +1,38 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
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&) {}
|
||||
void ComponentListener::componentEnablementChanged (Component&) {}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
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&) {}
|
||||
void ComponentListener::componentEnablementChanged (Component&) {}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,122 +1,122 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ComponentListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~ComponentListener() = default;
|
||||
|
||||
/** 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.
|
||||
|
||||
@param component the component that had its name 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!
|
||||
|
||||
@param component the component that was deleted
|
||||
*/
|
||||
virtual void componentBeingDeleted (Component& component);
|
||||
|
||||
/* Called when the component's enablement is changed.
|
||||
|
||||
@param component the component that had its enablement changed
|
||||
@see Component::setEnabled, Component::isEnabled, Component::enablementChanged
|
||||
*/
|
||||
virtual void componentEnablementChanged (Component& component);
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ComponentListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~ComponentListener() = default;
|
||||
|
||||
/** 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.
|
||||
|
||||
@param component the component that had its name 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!
|
||||
|
||||
@param component the component that was deleted
|
||||
*/
|
||||
virtual void componentBeingDeleted (Component& component);
|
||||
|
||||
/* Called when the component's enablement is changed.
|
||||
|
||||
@param component the component that had its enablement changed
|
||||
@see Component::setEnabled, Component::isEnabled, Component::enablementChanged
|
||||
*/
|
||||
virtual void componentEnablementChanged (Component& component);
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,72 +1,72 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Base class for traversing components.
|
||||
|
||||
If you need custom focus or keyboard focus traversal for a component you can
|
||||
create a subclass of ComponentTraverser and return it from
|
||||
Component::createFocusTraverser() or Component::createKeyboardFocusTraverser().
|
||||
|
||||
@see Component::createFocusTraverser, Component::createKeyboardFocusTraverser
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ComponentTraverser
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~ComponentTraverser() = default;
|
||||
|
||||
/** Returns the component that should be used as the traversal entry point
|
||||
within the given parent component.
|
||||
|
||||
This must return nullptr if there is no default component.
|
||||
*/
|
||||
virtual Component* getDefaultComponent (Component* parentComponent) = 0;
|
||||
|
||||
/** Returns the component that comes after the specified one when moving "forwards".
|
||||
|
||||
This must return nullptr if there is no next component.
|
||||
*/
|
||||
virtual Component* getNextComponent (Component* current) = 0;
|
||||
|
||||
/** Returns the component that comes after the specified one when moving "backwards".
|
||||
|
||||
This must return nullptr if there is no previous component.
|
||||
*/
|
||||
virtual Component* getPreviousComponent (Component* current) = 0;
|
||||
|
||||
/** Returns all of the traversable components within the given parent component in
|
||||
traversal order.
|
||||
*/
|
||||
virtual std::vector<Component*> getAllComponents (Component* parentComponent) = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Base class for traversing components.
|
||||
|
||||
If you need custom focus or keyboard focus traversal for a component you can
|
||||
create a subclass of ComponentTraverser and return it from
|
||||
Component::createFocusTraverser() or Component::createKeyboardFocusTraverser().
|
||||
|
||||
@see Component::createFocusTraverser, Component::createKeyboardFocusTraverser
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ComponentTraverser
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~ComponentTraverser() = default;
|
||||
|
||||
/** Returns the component that should be used as the traversal entry point
|
||||
within the given parent component.
|
||||
|
||||
This must return nullptr if there is no default component.
|
||||
*/
|
||||
virtual Component* getDefaultComponent (Component* parentComponent) = 0;
|
||||
|
||||
/** Returns the component that comes after the specified one when moving "forwards".
|
||||
|
||||
This must return nullptr if there is no next component.
|
||||
*/
|
||||
virtual Component* getNextComponent (Component* current) = 0;
|
||||
|
||||
/** Returns the component that comes after the specified one when moving "backwards".
|
||||
|
||||
This must return nullptr if there is no previous component.
|
||||
*/
|
||||
virtual Component* getPreviousComponent (Component* current) = 0;
|
||||
|
||||
/** Returns all of the traversable components within the given parent component in
|
||||
traversal order.
|
||||
*/
|
||||
virtual std::vector<Component*> getAllComponents (Component* parentComponent) = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,360 +1,360 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace FocusHelpers
|
||||
{
|
||||
static int getOrder (const Component* c)
|
||||
{
|
||||
auto order = c->getExplicitFocusOrder();
|
||||
return order > 0 ? order : std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
template <typename FocusContainerFn>
|
||||
static void findAllComponents (Component* parent,
|
||||
std::vector<Component*>& components,
|
||||
FocusContainerFn isFocusContainer)
|
||||
{
|
||||
if (parent == nullptr || parent->getNumChildComponents() == 0)
|
||||
return;
|
||||
|
||||
std::vector<Component*> localComponents;
|
||||
|
||||
for (auto* c : parent->getChildren())
|
||||
if (c->isVisible() && c->isEnabled())
|
||||
localComponents.push_back (c);
|
||||
|
||||
const auto compareComponents = [&] (const Component* a, const Component* b)
|
||||
{
|
||||
const auto getComponentOrderAttributes = [] (const Component* c)
|
||||
{
|
||||
return std::make_tuple (getOrder (c),
|
||||
c->isAlwaysOnTop() ? 0 : 1,
|
||||
c->getY(),
|
||||
c->getX());
|
||||
};
|
||||
|
||||
return getComponentOrderAttributes (a) < getComponentOrderAttributes (b);
|
||||
};
|
||||
|
||||
// This will sort so that they are ordered in terms of explicit focus,
|
||||
// always on top, left-to-right, and then top-to-bottom.
|
||||
std::stable_sort (localComponents.begin(), localComponents.end(), compareComponents);
|
||||
|
||||
for (auto* c : localComponents)
|
||||
{
|
||||
components.push_back (c);
|
||||
|
||||
if (! (c->*isFocusContainer)())
|
||||
findAllComponents (c, components, isFocusContainer);
|
||||
}
|
||||
}
|
||||
|
||||
enum class NavigationDirection { forwards, backwards };
|
||||
|
||||
template <typename FocusContainerFn>
|
||||
static Component* navigateFocus (Component* current,
|
||||
Component* focusContainer,
|
||||
NavigationDirection direction,
|
||||
FocusContainerFn isFocusContainer)
|
||||
{
|
||||
if (focusContainer != nullptr)
|
||||
{
|
||||
std::vector<Component*> components;
|
||||
findAllComponents (focusContainer, components, isFocusContainer);
|
||||
|
||||
const auto iter = std::find (components.cbegin(), components.cend(), current);
|
||||
|
||||
if (iter == components.cend())
|
||||
return nullptr;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case NavigationDirection::forwards:
|
||||
if (iter != std::prev (components.cend()))
|
||||
return *std::next (iter);
|
||||
|
||||
break;
|
||||
|
||||
case NavigationDirection::backwards:
|
||||
if (iter != components.cbegin())
|
||||
return *std::prev (iter);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Component* FocusTraverser::getNextComponent (Component* current)
|
||||
{
|
||||
jassert (current != nullptr);
|
||||
|
||||
return FocusHelpers::navigateFocus (current,
|
||||
current->findFocusContainer(),
|
||||
FocusHelpers::NavigationDirection::forwards,
|
||||
&Component::isFocusContainer);
|
||||
}
|
||||
|
||||
Component* FocusTraverser::getPreviousComponent (Component* current)
|
||||
{
|
||||
jassert (current != nullptr);
|
||||
|
||||
return FocusHelpers::navigateFocus (current,
|
||||
current->findFocusContainer(),
|
||||
FocusHelpers::NavigationDirection::backwards,
|
||||
&Component::isFocusContainer);
|
||||
}
|
||||
|
||||
Component* FocusTraverser::getDefaultComponent (Component* parentComponent)
|
||||
{
|
||||
if (parentComponent != nullptr)
|
||||
{
|
||||
std::vector<Component*> components;
|
||||
FocusHelpers::findAllComponents (parentComponent,
|
||||
components,
|
||||
&Component::isFocusContainer);
|
||||
|
||||
if (! components.empty())
|
||||
return components.front();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<Component*> FocusTraverser::getAllComponents (Component* parentComponent)
|
||||
{
|
||||
std::vector<Component*> components;
|
||||
FocusHelpers::findAllComponents (parentComponent,
|
||||
components,
|
||||
&Component::isFocusContainer);
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
struct FocusTraverserTests : public UnitTest
|
||||
{
|
||||
FocusTraverserTests()
|
||||
: UnitTest ("FocusTraverser", UnitTestCategories::gui)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
ScopedJuceInitialiser_GUI libraryInitialiser;
|
||||
const MessageManagerLock mml;
|
||||
|
||||
beginTest ("Basic traversal");
|
||||
{
|
||||
TestComponent parent;
|
||||
|
||||
expect (traverser.getDefaultComponent (&parent) == &parent.children.front());
|
||||
|
||||
for (auto iter = parent.children.begin(); iter != parent.children.end(); ++iter)
|
||||
expect (traverser.getNextComponent (&(*iter)) == (iter == std::prev (parent.children.cend()) ? nullptr
|
||||
: &(*std::next (iter))));
|
||||
|
||||
for (auto iter = parent.children.rbegin(); iter != parent.children.rend(); ++iter)
|
||||
expect (traverser.getPreviousComponent (&(*iter)) == (iter == std::prev (parent.children.rend()) ? nullptr
|
||||
: &(*std::next (iter))));
|
||||
auto allComponents = traverser.getAllComponents (&parent);
|
||||
|
||||
expect (std::equal (allComponents.cbegin(), allComponents.cend(), parent.children.cbegin(),
|
||||
[] (const Component* c1, const Component& c2) { return c1 == &c2; }));
|
||||
}
|
||||
|
||||
beginTest ("Disabled components are ignored");
|
||||
{
|
||||
checkIgnored ([] (Component& c) { c.setEnabled (false); });
|
||||
}
|
||||
|
||||
beginTest ("Invisible components are ignored");
|
||||
{
|
||||
checkIgnored ([] (Component& c) { c.setVisible (false); });
|
||||
}
|
||||
|
||||
beginTest ("Explicit focus order comes before unspecified");
|
||||
{
|
||||
TestComponent parent;
|
||||
|
||||
auto& explicitFocusComponent = parent.children[2];
|
||||
|
||||
explicitFocusComponent.setExplicitFocusOrder (1);
|
||||
expect (traverser.getDefaultComponent (&parent) == &explicitFocusComponent);
|
||||
|
||||
expect (traverser.getAllComponents (&parent).front() == &explicitFocusComponent);
|
||||
}
|
||||
|
||||
beginTest ("Explicit focus order comparison");
|
||||
{
|
||||
checkComponentProperties ([this] (Component& child) { child.setExplicitFocusOrder (getRandom().nextInt ({ 1, 100 })); },
|
||||
[] (const Component& c1, const Component& c2) { return c1.getExplicitFocusOrder()
|
||||
<= c2.getExplicitFocusOrder(); });
|
||||
}
|
||||
|
||||
beginTest ("Left to right");
|
||||
{
|
||||
checkComponentProperties ([this] (Component& child) { child.setTopLeftPosition (getRandom().nextInt ({ 0, 100 }), 0); },
|
||||
[] (const Component& c1, const Component& c2) { return c1.getX() <= c2.getX(); });
|
||||
}
|
||||
|
||||
beginTest ("Top to bottom");
|
||||
{
|
||||
checkComponentProperties ([this] (Component& child) { child.setTopLeftPosition (0, getRandom().nextInt ({ 0, 100 })); },
|
||||
[] (const Component& c1, const Component& c2) { return c1.getY() <= c2.getY(); });
|
||||
}
|
||||
|
||||
beginTest ("Focus containers have their own focus");
|
||||
{
|
||||
Component root;
|
||||
|
||||
TestComponent container;
|
||||
container.setFocusContainerType (Component::FocusContainerType::focusContainer);
|
||||
|
||||
root.addAndMakeVisible (container);
|
||||
|
||||
expect (traverser.getDefaultComponent (&root) == &container);
|
||||
expect (traverser.getNextComponent (&container) == nullptr);
|
||||
expect (traverser.getPreviousComponent (&container) == nullptr);
|
||||
|
||||
expect (traverser.getDefaultComponent (&container) == &container.children.front());
|
||||
|
||||
for (auto iter = container.children.begin(); iter != container.children.end(); ++iter)
|
||||
expect (traverser.getNextComponent (&(*iter)) == (iter == std::prev (container.children.cend()) ? nullptr
|
||||
: &(*std::next (iter))));
|
||||
|
||||
for (auto iter = container.children.rbegin(); iter != container.children.rend(); ++iter)
|
||||
expect (traverser.getPreviousComponent (&(*iter)) == (iter == std::prev (container.children.rend()) ? nullptr
|
||||
: &(*std::next (iter))));
|
||||
|
||||
expect (traverser.getAllComponents (&root).size() == 1);
|
||||
|
||||
auto allContainerComponents = traverser.getAllComponents (&container);
|
||||
|
||||
expect (std::equal (allContainerComponents.cbegin(), allContainerComponents.cend(), container.children.cbegin(),
|
||||
[] (const Component* c1, const Component& c2) { return c1 == &c2; }));
|
||||
}
|
||||
|
||||
beginTest ("Non-focus containers pass-through focus");
|
||||
{
|
||||
Component root;
|
||||
|
||||
TestComponent container;
|
||||
container.setFocusContainerType (Component::FocusContainerType::none);
|
||||
|
||||
root.addAndMakeVisible (container);
|
||||
|
||||
expect (traverser.getDefaultComponent (&root) == &container);
|
||||
expect (traverser.getNextComponent (&container) == &container.children.front());
|
||||
expect (traverser.getPreviousComponent (&container) == nullptr);
|
||||
|
||||
expect (traverser.getDefaultComponent (&container) == &container.children.front());
|
||||
|
||||
for (auto iter = container.children.begin(); iter != container.children.end(); ++iter)
|
||||
expect (traverser.getNextComponent (&(*iter)) == (iter == std::prev (container.children.cend()) ? nullptr
|
||||
: &(*std::next (iter))));
|
||||
|
||||
for (auto iter = container.children.rbegin(); iter != container.children.rend(); ++iter)
|
||||
expect (traverser.getPreviousComponent (&(*iter)) == (iter == std::prev (container.children.rend()) ? &container
|
||||
: &(*std::next (iter))));
|
||||
|
||||
expect (traverser.getAllComponents (&root).size() == container.children.size() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct TestComponent : public Component
|
||||
{
|
||||
TestComponent()
|
||||
{
|
||||
for (auto& child : children)
|
||||
addAndMakeVisible (child);
|
||||
}
|
||||
|
||||
std::array<Component, 10> children;
|
||||
};
|
||||
|
||||
void checkComponentProperties (std::function<void (Component&)>&& childFn,
|
||||
std::function<bool (const Component&, const Component&)>&& testProperty)
|
||||
{
|
||||
TestComponent parent;
|
||||
|
||||
for (auto& child : parent.children)
|
||||
childFn (child);
|
||||
|
||||
auto* comp = traverser.getDefaultComponent (&parent);
|
||||
|
||||
for (const auto& child : parent.children)
|
||||
if (&child != comp)
|
||||
expect (testProperty (*comp, child));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto* next = traverser.getNextComponent (comp);
|
||||
|
||||
if (next == nullptr)
|
||||
break;
|
||||
|
||||
expect (testProperty (*comp, *next));
|
||||
comp = next;
|
||||
}
|
||||
}
|
||||
|
||||
void checkIgnored (const std::function<void(Component&)>& makeIgnored)
|
||||
{
|
||||
TestComponent parent;
|
||||
|
||||
auto iter = parent.children.begin();
|
||||
|
||||
makeIgnored (*iter);
|
||||
expect (traverser.getDefaultComponent (&parent) == std::addressof (*std::next (iter)));
|
||||
|
||||
iter += 5;
|
||||
makeIgnored (*iter);
|
||||
expect (traverser.getNextComponent (std::addressof (*std::prev (iter))) == std::addressof (*std::next (iter)));
|
||||
expect (traverser.getPreviousComponent (std::addressof (*std::next (iter))) == std::addressof (*std::prev (iter)));
|
||||
|
||||
auto allComponents = traverser.getAllComponents (&parent);
|
||||
|
||||
expect (std::find (allComponents.cbegin(), allComponents.cend(), &parent.children.front()) == allComponents.cend());
|
||||
expect (std::find (allComponents.cbegin(), allComponents.cend(), std::addressof (*iter)) == allComponents.cend());
|
||||
}
|
||||
|
||||
FocusTraverser traverser;
|
||||
};
|
||||
|
||||
static FocusTraverserTests focusTraverserTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace FocusHelpers
|
||||
{
|
||||
static int getOrder (const Component* c)
|
||||
{
|
||||
auto order = c->getExplicitFocusOrder();
|
||||
return order > 0 ? order : std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
template <typename FocusContainerFn>
|
||||
static void findAllComponents (Component* parent,
|
||||
std::vector<Component*>& components,
|
||||
FocusContainerFn isFocusContainer)
|
||||
{
|
||||
if (parent == nullptr || parent->getNumChildComponents() == 0)
|
||||
return;
|
||||
|
||||
std::vector<Component*> localComponents;
|
||||
|
||||
for (auto* c : parent->getChildren())
|
||||
if (c->isVisible() && c->isEnabled())
|
||||
localComponents.push_back (c);
|
||||
|
||||
const auto compareComponents = [&] (const Component* a, const Component* b)
|
||||
{
|
||||
const auto getComponentOrderAttributes = [] (const Component* c)
|
||||
{
|
||||
return std::make_tuple (getOrder (c),
|
||||
c->isAlwaysOnTop() ? 0 : 1,
|
||||
c->getY(),
|
||||
c->getX());
|
||||
};
|
||||
|
||||
return getComponentOrderAttributes (a) < getComponentOrderAttributes (b);
|
||||
};
|
||||
|
||||
// This will sort so that they are ordered in terms of explicit focus,
|
||||
// always on top, left-to-right, and then top-to-bottom.
|
||||
std::stable_sort (localComponents.begin(), localComponents.end(), compareComponents);
|
||||
|
||||
for (auto* c : localComponents)
|
||||
{
|
||||
components.push_back (c);
|
||||
|
||||
if (! (c->*isFocusContainer)())
|
||||
findAllComponents (c, components, isFocusContainer);
|
||||
}
|
||||
}
|
||||
|
||||
enum class NavigationDirection { forwards, backwards };
|
||||
|
||||
template <typename FocusContainerFn>
|
||||
static Component* navigateFocus (Component* current,
|
||||
Component* focusContainer,
|
||||
NavigationDirection direction,
|
||||
FocusContainerFn isFocusContainer)
|
||||
{
|
||||
if (focusContainer != nullptr)
|
||||
{
|
||||
std::vector<Component*> components;
|
||||
findAllComponents (focusContainer, components, isFocusContainer);
|
||||
|
||||
const auto iter = std::find (components.cbegin(), components.cend(), current);
|
||||
|
||||
if (iter == components.cend())
|
||||
return nullptr;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case NavigationDirection::forwards:
|
||||
if (iter != std::prev (components.cend()))
|
||||
return *std::next (iter);
|
||||
|
||||
break;
|
||||
|
||||
case NavigationDirection::backwards:
|
||||
if (iter != components.cbegin())
|
||||
return *std::prev (iter);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Component* FocusTraverser::getNextComponent (Component* current)
|
||||
{
|
||||
jassert (current != nullptr);
|
||||
|
||||
return FocusHelpers::navigateFocus (current,
|
||||
current->findFocusContainer(),
|
||||
FocusHelpers::NavigationDirection::forwards,
|
||||
&Component::isFocusContainer);
|
||||
}
|
||||
|
||||
Component* FocusTraverser::getPreviousComponent (Component* current)
|
||||
{
|
||||
jassert (current != nullptr);
|
||||
|
||||
return FocusHelpers::navigateFocus (current,
|
||||
current->findFocusContainer(),
|
||||
FocusHelpers::NavigationDirection::backwards,
|
||||
&Component::isFocusContainer);
|
||||
}
|
||||
|
||||
Component* FocusTraverser::getDefaultComponent (Component* parentComponent)
|
||||
{
|
||||
if (parentComponent != nullptr)
|
||||
{
|
||||
std::vector<Component*> components;
|
||||
FocusHelpers::findAllComponents (parentComponent,
|
||||
components,
|
||||
&Component::isFocusContainer);
|
||||
|
||||
if (! components.empty())
|
||||
return components.front();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<Component*> FocusTraverser::getAllComponents (Component* parentComponent)
|
||||
{
|
||||
std::vector<Component*> components;
|
||||
FocusHelpers::findAllComponents (parentComponent,
|
||||
components,
|
||||
&Component::isFocusContainer);
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
struct FocusTraverserTests : public UnitTest
|
||||
{
|
||||
FocusTraverserTests()
|
||||
: UnitTest ("FocusTraverser", UnitTestCategories::gui)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
ScopedJuceInitialiser_GUI libraryInitialiser;
|
||||
const MessageManagerLock mml;
|
||||
|
||||
beginTest ("Basic traversal");
|
||||
{
|
||||
TestComponent parent;
|
||||
|
||||
expect (traverser.getDefaultComponent (&parent) == &parent.children.front());
|
||||
|
||||
for (auto iter = parent.children.begin(); iter != parent.children.end(); ++iter)
|
||||
expect (traverser.getNextComponent (&(*iter)) == (iter == std::prev (parent.children.cend()) ? nullptr
|
||||
: &(*std::next (iter))));
|
||||
|
||||
for (auto iter = parent.children.rbegin(); iter != parent.children.rend(); ++iter)
|
||||
expect (traverser.getPreviousComponent (&(*iter)) == (iter == std::prev (parent.children.rend()) ? nullptr
|
||||
: &(*std::next (iter))));
|
||||
auto allComponents = traverser.getAllComponents (&parent);
|
||||
|
||||
expect (std::equal (allComponents.cbegin(), allComponents.cend(), parent.children.cbegin(),
|
||||
[] (const Component* c1, const Component& c2) { return c1 == &c2; }));
|
||||
}
|
||||
|
||||
beginTest ("Disabled components are ignored");
|
||||
{
|
||||
checkIgnored ([] (Component& c) { c.setEnabled (false); });
|
||||
}
|
||||
|
||||
beginTest ("Invisible components are ignored");
|
||||
{
|
||||
checkIgnored ([] (Component& c) { c.setVisible (false); });
|
||||
}
|
||||
|
||||
beginTest ("Explicit focus order comes before unspecified");
|
||||
{
|
||||
TestComponent parent;
|
||||
|
||||
auto& explicitFocusComponent = parent.children[2];
|
||||
|
||||
explicitFocusComponent.setExplicitFocusOrder (1);
|
||||
expect (traverser.getDefaultComponent (&parent) == &explicitFocusComponent);
|
||||
|
||||
expect (traverser.getAllComponents (&parent).front() == &explicitFocusComponent);
|
||||
}
|
||||
|
||||
beginTest ("Explicit focus order comparison");
|
||||
{
|
||||
checkComponentProperties ([this] (Component& child) { child.setExplicitFocusOrder (getRandom().nextInt ({ 1, 100 })); },
|
||||
[] (const Component& c1, const Component& c2) { return c1.getExplicitFocusOrder()
|
||||
<= c2.getExplicitFocusOrder(); });
|
||||
}
|
||||
|
||||
beginTest ("Left to right");
|
||||
{
|
||||
checkComponentProperties ([this] (Component& child) { child.setTopLeftPosition (getRandom().nextInt ({ 0, 100 }), 0); },
|
||||
[] (const Component& c1, const Component& c2) { return c1.getX() <= c2.getX(); });
|
||||
}
|
||||
|
||||
beginTest ("Top to bottom");
|
||||
{
|
||||
checkComponentProperties ([this] (Component& child) { child.setTopLeftPosition (0, getRandom().nextInt ({ 0, 100 })); },
|
||||
[] (const Component& c1, const Component& c2) { return c1.getY() <= c2.getY(); });
|
||||
}
|
||||
|
||||
beginTest ("Focus containers have their own focus");
|
||||
{
|
||||
Component root;
|
||||
|
||||
TestComponent container;
|
||||
container.setFocusContainerType (Component::FocusContainerType::focusContainer);
|
||||
|
||||
root.addAndMakeVisible (container);
|
||||
|
||||
expect (traverser.getDefaultComponent (&root) == &container);
|
||||
expect (traverser.getNextComponent (&container) == nullptr);
|
||||
expect (traverser.getPreviousComponent (&container) == nullptr);
|
||||
|
||||
expect (traverser.getDefaultComponent (&container) == &container.children.front());
|
||||
|
||||
for (auto iter = container.children.begin(); iter != container.children.end(); ++iter)
|
||||
expect (traverser.getNextComponent (&(*iter)) == (iter == std::prev (container.children.cend()) ? nullptr
|
||||
: &(*std::next (iter))));
|
||||
|
||||
for (auto iter = container.children.rbegin(); iter != container.children.rend(); ++iter)
|
||||
expect (traverser.getPreviousComponent (&(*iter)) == (iter == std::prev (container.children.rend()) ? nullptr
|
||||
: &(*std::next (iter))));
|
||||
|
||||
expect (traverser.getAllComponents (&root).size() == 1);
|
||||
|
||||
auto allContainerComponents = traverser.getAllComponents (&container);
|
||||
|
||||
expect (std::equal (allContainerComponents.cbegin(), allContainerComponents.cend(), container.children.cbegin(),
|
||||
[] (const Component* c1, const Component& c2) { return c1 == &c2; }));
|
||||
}
|
||||
|
||||
beginTest ("Non-focus containers pass-through focus");
|
||||
{
|
||||
Component root;
|
||||
|
||||
TestComponent container;
|
||||
container.setFocusContainerType (Component::FocusContainerType::none);
|
||||
|
||||
root.addAndMakeVisible (container);
|
||||
|
||||
expect (traverser.getDefaultComponent (&root) == &container);
|
||||
expect (traverser.getNextComponent (&container) == &container.children.front());
|
||||
expect (traverser.getPreviousComponent (&container) == nullptr);
|
||||
|
||||
expect (traverser.getDefaultComponent (&container) == &container.children.front());
|
||||
|
||||
for (auto iter = container.children.begin(); iter != container.children.end(); ++iter)
|
||||
expect (traverser.getNextComponent (&(*iter)) == (iter == std::prev (container.children.cend()) ? nullptr
|
||||
: &(*std::next (iter))));
|
||||
|
||||
for (auto iter = container.children.rbegin(); iter != container.children.rend(); ++iter)
|
||||
expect (traverser.getPreviousComponent (&(*iter)) == (iter == std::prev (container.children.rend()) ? &container
|
||||
: &(*std::next (iter))));
|
||||
|
||||
expect (traverser.getAllComponents (&root).size() == container.children.size() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct TestComponent : public Component
|
||||
{
|
||||
TestComponent()
|
||||
{
|
||||
for (auto& child : children)
|
||||
addAndMakeVisible (child);
|
||||
}
|
||||
|
||||
std::array<Component, 10> children;
|
||||
};
|
||||
|
||||
void checkComponentProperties (std::function<void (Component&)>&& childFn,
|
||||
std::function<bool (const Component&, const Component&)>&& testProperty)
|
||||
{
|
||||
TestComponent parent;
|
||||
|
||||
for (auto& child : parent.children)
|
||||
childFn (child);
|
||||
|
||||
auto* comp = traverser.getDefaultComponent (&parent);
|
||||
|
||||
for (const auto& child : parent.children)
|
||||
if (&child != comp)
|
||||
expect (testProperty (*comp, child));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto* next = traverser.getNextComponent (comp);
|
||||
|
||||
if (next == nullptr)
|
||||
break;
|
||||
|
||||
expect (testProperty (*comp, *next));
|
||||
comp = next;
|
||||
}
|
||||
}
|
||||
|
||||
void checkIgnored (const std::function<void(Component&)>& makeIgnored)
|
||||
{
|
||||
TestComponent parent;
|
||||
|
||||
auto iter = parent.children.begin();
|
||||
|
||||
makeIgnored (*iter);
|
||||
expect (traverser.getDefaultComponent (&parent) == std::addressof (*std::next (iter)));
|
||||
|
||||
iter += 5;
|
||||
makeIgnored (*iter);
|
||||
expect (traverser.getNextComponent (std::addressof (*std::prev (iter))) == std::addressof (*std::next (iter)));
|
||||
expect (traverser.getPreviousComponent (std::addressof (*std::next (iter))) == std::addressof (*std::prev (iter)));
|
||||
|
||||
auto allComponents = traverser.getAllComponents (&parent);
|
||||
|
||||
expect (std::find (allComponents.cbegin(), allComponents.cend(), &parent.children.front()) == allComponents.cend());
|
||||
expect (std::find (allComponents.cbegin(), allComponents.cend(), std::addressof (*iter)) == allComponents.cend());
|
||||
}
|
||||
|
||||
FocusTraverser traverser;
|
||||
};
|
||||
|
||||
static FocusTraverserTests focusTraverserTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,93 +1,93 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Controls the order in which focus moves between components.
|
||||
|
||||
The algorithm used by this class to work out the order of traversal is as
|
||||
follows:
|
||||
- Only visible and enabled components are considered focusable.
|
||||
- If two components both have an explicit focus order specified then the
|
||||
one with the lowest number comes first (see the
|
||||
Component::setExplicitFocusOrder() method).
|
||||
- Any component with an explicit focus order greater than 0 comes before ones
|
||||
that don't have an order specified.
|
||||
- Components with their 'always on top' flag set come before those without.
|
||||
- Any unspecified components are traversed in a left-to-right, then
|
||||
top-to-bottom order.
|
||||
|
||||
If you need focus traversal in a more customised way you can create a
|
||||
ComponentTraverser subclass that uses your own algorithm and return it
|
||||
from Component::createFocusTraverser().
|
||||
|
||||
@see ComponentTraverser, Component::createFocusTraverser
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FocusTraverser : public ComponentTraverser
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
~FocusTraverser() override = default;
|
||||
|
||||
/** Returns the component that should receive focus by default within the given
|
||||
parent component.
|
||||
|
||||
The default implementation will just return the foremost visible and enabled
|
||||
child component, and will return nullptr if there is no suitable component.
|
||||
*/
|
||||
Component* getDefaultComponent (Component* parentComponent) override;
|
||||
|
||||
/** Returns the component that should be given focus after the specified one when
|
||||
moving "forwards".
|
||||
|
||||
The default implementation will return the next visible and enabled component
|
||||
which is to the right of or below this one, and will return nullptr if there
|
||||
is no suitable component.
|
||||
*/
|
||||
Component* getNextComponent (Component* current) override;
|
||||
|
||||
/** Returns the component that should be given focus after the specified one when
|
||||
moving "backwards".
|
||||
|
||||
The default implementation will return the previous visible and enabled component
|
||||
which is to the left of or above this one, and will return nullptr if there
|
||||
is no suitable component.
|
||||
*/
|
||||
Component* getPreviousComponent (Component* current) override;
|
||||
|
||||
/** Returns all of the components that can receive focus within the given parent
|
||||
component in traversal order.
|
||||
|
||||
The default implementation will return all visible and enabled child components.
|
||||
*/
|
||||
std::vector<Component*> getAllComponents (Component* parentComponent) override;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Controls the order in which focus moves between components.
|
||||
|
||||
The algorithm used by this class to work out the order of traversal is as
|
||||
follows:
|
||||
- Only visible and enabled components are considered focusable.
|
||||
- If two components both have an explicit focus order specified then the
|
||||
one with the lowest number comes first (see the
|
||||
Component::setExplicitFocusOrder() method).
|
||||
- Any component with an explicit focus order greater than 0 comes before ones
|
||||
that don't have an order specified.
|
||||
- Components with their 'always on top' flag set come before those without.
|
||||
- Any unspecified components are traversed in a left-to-right, then
|
||||
top-to-bottom order.
|
||||
|
||||
If you need focus traversal in a more customised way you can create a
|
||||
ComponentTraverser subclass that uses your own algorithm and return it
|
||||
from Component::createFocusTraverser().
|
||||
|
||||
@see ComponentTraverser, Component::createFocusTraverser
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FocusTraverser : public ComponentTraverser
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
~FocusTraverser() override = default;
|
||||
|
||||
/** Returns the component that should receive focus by default within the given
|
||||
parent component.
|
||||
|
||||
The default implementation will just return the foremost visible and enabled
|
||||
child component, and will return nullptr if there is no suitable component.
|
||||
*/
|
||||
Component* getDefaultComponent (Component* parentComponent) override;
|
||||
|
||||
/** Returns the component that should be given focus after the specified one when
|
||||
moving "forwards".
|
||||
|
||||
The default implementation will return the next visible and enabled component
|
||||
which is to the right of or below this one, and will return nullptr if there
|
||||
is no suitable component.
|
||||
*/
|
||||
Component* getNextComponent (Component* current) override;
|
||||
|
||||
/** Returns the component that should be given focus after the specified one when
|
||||
moving "backwards".
|
||||
|
||||
The default implementation will return the previous visible and enabled component
|
||||
which is to the left of or above this one, and will return nullptr if there
|
||||
is no suitable component.
|
||||
*/
|
||||
Component* getPreviousComponent (Component* current) override;
|
||||
|
||||
/** Returns all of the components that can receive focus within the given parent
|
||||
component in traversal order.
|
||||
|
||||
The default implementation will return all visible and enabled child components.
|
||||
*/
|
||||
std::vector<Component*> getAllComponents (Component* parentComponent) override;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,292 +1,292 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct ModalComponentManager::ModalItem : public ComponentMovementWatcher
|
||||
{
|
||||
ModalItem (Component* comp, bool shouldAutoDelete)
|
||||
: ComponentMovementWatcher (comp),
|
||||
component (comp), autoDelete (shouldAutoDelete)
|
||||
{
|
||||
jassert (comp != nullptr);
|
||||
}
|
||||
|
||||
~ModalItem() override
|
||||
{
|
||||
if (autoDelete)
|
||||
std::unique_ptr<Component> componentDeleter (component);
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool, bool) override {}
|
||||
|
||||
using ComponentMovementWatcher::componentMovedOrResized;
|
||||
|
||||
void componentPeerChanged() override
|
||||
{
|
||||
componentVisibilityChanged();
|
||||
}
|
||||
|
||||
void componentVisibilityChanged() override
|
||||
{
|
||||
if (! component->isShowing())
|
||||
cancel();
|
||||
}
|
||||
|
||||
using ComponentMovementWatcher::componentVisibilityChanged;
|
||||
|
||||
void componentBeingDeleted (Component& comp) override
|
||||
{
|
||||
ComponentMovementWatcher::componentBeingDeleted (comp);
|
||||
|
||||
if (component == &comp || comp.isParentOf (component))
|
||||
{
|
||||
autoDelete = false;
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
if (isActive)
|
||||
{
|
||||
isActive = false;
|
||||
|
||||
if (auto* mcm = ModalComponentManager::getInstanceWithoutCreating())
|
||||
mcm->triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
Component* component;
|
||||
OwnedArray<Callback> callbacks;
|
||||
int returnValue = 0;
|
||||
bool isActive = true, autoDelete;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ModalItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ModalComponentManager::ModalComponentManager()
|
||||
{
|
||||
}
|
||||
|
||||
ModalComponentManager::~ModalComponentManager()
|
||||
{
|
||||
stack.clear();
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (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)
|
||||
{
|
||||
std::unique_ptr<Callback> callbackDeleter (callback);
|
||||
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
auto* 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;)
|
||||
{
|
||||
auto* item = stack.getUnchecked (i);
|
||||
|
||||
if (item->component == component)
|
||||
item->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void ModalComponentManager::endModal (Component* component, int returnValue)
|
||||
{
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
auto* item = stack.getUnchecked (i);
|
||||
|
||||
if (item->component == component)
|
||||
{
|
||||
item->returnValue = returnValue;
|
||||
item->cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ModalComponentManager::getNumModalComponents() const
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
for (auto* item : stack)
|
||||
if (item->isActive)
|
||||
++n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
Component* ModalComponentManager::getModalComponent (int index) const
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
auto* item = stack.getUnchecked (i);
|
||||
|
||||
if (item->isActive)
|
||||
if (n++ == index)
|
||||
return item->component;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ModalComponentManager::isModal (const Component* comp) const
|
||||
{
|
||||
for (auto* item : stack)
|
||||
if (item->isActive && item->component == comp)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModalComponentManager::isFrontModalComponent (const Component* comp) const
|
||||
{
|
||||
return comp == getModalComponent (0);
|
||||
}
|
||||
|
||||
void ModalComponentManager::handleAsyncUpdate()
|
||||
{
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
auto* item = stack.getUnchecked (i);
|
||||
|
||||
if (! item->isActive)
|
||||
{
|
||||
std::unique_ptr<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)
|
||||
{
|
||||
auto* c = getModalComponent (i);
|
||||
|
||||
if (c == nullptr)
|
||||
break;
|
||||
|
||||
if (auto* peer = c->getPeer())
|
||||
{
|
||||
if (peer != lastOne)
|
||||
{
|
||||
if (lastOne == nullptr)
|
||||
{
|
||||
peer->toFront (topOneShouldGrabFocus);
|
||||
|
||||
if (topOneShouldGrabFocus)
|
||||
peer->grabFocus();
|
||||
}
|
||||
else
|
||||
{
|
||||
peer->toBehind (lastOne);
|
||||
}
|
||||
|
||||
lastOne = peer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ModalComponentManager::cancelAllModalComponents()
|
||||
{
|
||||
auto numModal = getNumModalComponents();
|
||||
|
||||
for (int i = numModal; --i >= 0;)
|
||||
if (auto* c = getModalComponent (i))
|
||||
c->exitModalState (0);
|
||||
|
||||
return numModal > 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
int ModalComponentManager::runEventLoopForCurrentComponent()
|
||||
{
|
||||
// This can only be run from the message thread!
|
||||
JUCE_ASSERT_MESSAGE_THREAD
|
||||
|
||||
int returnValue = 0;
|
||||
|
||||
if (auto* currentlyModal = getModalComponent (0))
|
||||
{
|
||||
FocusRestorer focusRestorer;
|
||||
bool finished = false;
|
||||
|
||||
attachCallback (currentlyModal, ModalCallbackFunction::create ([&] (int r) { returnValue = r; finished = true; }));
|
||||
|
||||
JUCE_TRY
|
||||
{
|
||||
while (! finished)
|
||||
{
|
||||
if (! MessageManager::getInstance()->runDispatchLoopUntil (20))
|
||||
break;
|
||||
}
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct ModalComponentManager::ModalItem : public ComponentMovementWatcher
|
||||
{
|
||||
ModalItem (Component* comp, bool shouldAutoDelete)
|
||||
: ComponentMovementWatcher (comp),
|
||||
component (comp), autoDelete (shouldAutoDelete)
|
||||
{
|
||||
jassert (comp != nullptr);
|
||||
}
|
||||
|
||||
~ModalItem() override
|
||||
{
|
||||
if (autoDelete)
|
||||
std::unique_ptr<Component> componentDeleter (component);
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool, bool) override {}
|
||||
|
||||
using ComponentMovementWatcher::componentMovedOrResized;
|
||||
|
||||
void componentPeerChanged() override
|
||||
{
|
||||
componentVisibilityChanged();
|
||||
}
|
||||
|
||||
void componentVisibilityChanged() override
|
||||
{
|
||||
if (! component->isShowing())
|
||||
cancel();
|
||||
}
|
||||
|
||||
using ComponentMovementWatcher::componentVisibilityChanged;
|
||||
|
||||
void componentBeingDeleted (Component& comp) override
|
||||
{
|
||||
ComponentMovementWatcher::componentBeingDeleted (comp);
|
||||
|
||||
if (component == &comp || comp.isParentOf (component))
|
||||
{
|
||||
autoDelete = false;
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
if (isActive)
|
||||
{
|
||||
isActive = false;
|
||||
|
||||
if (auto* mcm = ModalComponentManager::getInstanceWithoutCreating())
|
||||
mcm->triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
Component* component;
|
||||
OwnedArray<Callback> callbacks;
|
||||
int returnValue = 0;
|
||||
bool isActive = true, autoDelete;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ModalItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ModalComponentManager::ModalComponentManager()
|
||||
{
|
||||
}
|
||||
|
||||
ModalComponentManager::~ModalComponentManager()
|
||||
{
|
||||
stack.clear();
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (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)
|
||||
{
|
||||
std::unique_ptr<Callback> callbackDeleter (callback);
|
||||
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
auto* 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;)
|
||||
{
|
||||
auto* item = stack.getUnchecked (i);
|
||||
|
||||
if (item->component == component)
|
||||
item->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void ModalComponentManager::endModal (Component* component, int returnValue)
|
||||
{
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
auto* item = stack.getUnchecked (i);
|
||||
|
||||
if (item->component == component)
|
||||
{
|
||||
item->returnValue = returnValue;
|
||||
item->cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ModalComponentManager::getNumModalComponents() const
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
for (auto* item : stack)
|
||||
if (item->isActive)
|
||||
++n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
Component* ModalComponentManager::getModalComponent (int index) const
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
auto* item = stack.getUnchecked (i);
|
||||
|
||||
if (item->isActive)
|
||||
if (n++ == index)
|
||||
return item->component;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ModalComponentManager::isModal (const Component* comp) const
|
||||
{
|
||||
for (auto* item : stack)
|
||||
if (item->isActive && item->component == comp)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModalComponentManager::isFrontModalComponent (const Component* comp) const
|
||||
{
|
||||
return comp == getModalComponent (0);
|
||||
}
|
||||
|
||||
void ModalComponentManager::handleAsyncUpdate()
|
||||
{
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
auto* item = stack.getUnchecked (i);
|
||||
|
||||
if (! item->isActive)
|
||||
{
|
||||
std::unique_ptr<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)
|
||||
{
|
||||
auto* c = getModalComponent (i);
|
||||
|
||||
if (c == nullptr)
|
||||
break;
|
||||
|
||||
if (auto* peer = c->getPeer())
|
||||
{
|
||||
if (peer != lastOne)
|
||||
{
|
||||
if (lastOne == nullptr)
|
||||
{
|
||||
peer->toFront (topOneShouldGrabFocus);
|
||||
|
||||
if (topOneShouldGrabFocus)
|
||||
peer->grabFocus();
|
||||
}
|
||||
else
|
||||
{
|
||||
peer->toBehind (lastOne);
|
||||
}
|
||||
|
||||
lastOne = peer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ModalComponentManager::cancelAllModalComponents()
|
||||
{
|
||||
auto numModal = getNumModalComponents();
|
||||
|
||||
for (int i = numModal; --i >= 0;)
|
||||
if (auto* c = getModalComponent (i))
|
||||
c->exitModalState (0);
|
||||
|
||||
return numModal > 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
int ModalComponentManager::runEventLoopForCurrentComponent()
|
||||
{
|
||||
// This can only be run from the message thread!
|
||||
JUCE_ASSERT_MESSAGE_THREAD
|
||||
|
||||
int returnValue = 0;
|
||||
|
||||
if (auto* currentlyModal = getModalComponent (0))
|
||||
{
|
||||
FocusRestorer focusRestorer;
|
||||
bool finished = false;
|
||||
|
||||
attachCallback (currentlyModal, ModalCallbackFunction::create ([&] (int r) { returnValue = r; finished = true; }));
|
||||
|
||||
JUCE_TRY
|
||||
{
|
||||
while (! finished)
|
||||
{
|
||||
if (! MessageManager::getInstance()->runDispatchLoopUntil (20))
|
||||
break;
|
||||
}
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,324 +1,324 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
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 JUCE_API Callback
|
||||
{
|
||||
public:
|
||||
/** */
|
||||
Callback() = default;
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~Callback() = default;
|
||||
|
||||
/** 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;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (ModalComponentManager)
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** 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 (const Component* component) const;
|
||||
|
||||
/** Returns true if the specified component is currently the topmost modal component. */
|
||||
bool isFrontModalComponent (const 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
|
||||
|
||||
protected:
|
||||
/** Creates a ModalComponentManager.
|
||||
You shouldn't ever call the constructor - it's a singleton, so use ModalComponentManager::getInstance()
|
||||
*/
|
||||
ModalComponentManager();
|
||||
|
||||
/** Destructor. */
|
||||
~ModalComponentManager() override;
|
||||
|
||||
/** @internal */
|
||||
void handleAsyncUpdate() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
friend class Component;
|
||||
|
||||
struct 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.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ModalCallbackFunction
|
||||
{
|
||||
public:
|
||||
/** This is a utility function to create a ModalComponentManager::Callback that will
|
||||
call a callable object.
|
||||
|
||||
The function that you supply must take an integer parameter, which is the result code that
|
||||
was returned when the modal component was dismissed.
|
||||
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <typename CallbackFn>
|
||||
static ModalComponentManager::Callback* create (CallbackFn&& fn)
|
||||
{
|
||||
struct Callable : public ModalComponentManager::Callback
|
||||
{
|
||||
explicit Callable (CallbackFn&& f) : fn (std::forward<CallbackFn> (f)) {}
|
||||
void modalStateFinished (int result) override { NullCheckedInvocation::invoke (std::move (fn), result); }
|
||||
|
||||
std::remove_reference_t<CallbackFn> fn;
|
||||
};
|
||||
|
||||
return new Callable (std::forward<CallbackFn> (fn));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** 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 (true, ModalCallbackFunction::create (myCallbackFunction, 3.0));
|
||||
@endcode
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <typename ParamType>
|
||||
static ModalComponentManager::Callback* create (void (*functionToCall) (int, ParamType),
|
||||
ParamType parameterValue)
|
||||
{
|
||||
return create ([functionToCall, parameterValue] (int r)
|
||||
{
|
||||
functionToCall (r, 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 (true, 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 create ([functionToCall, parameterValue1, parameterValue2] (int r)
|
||||
{
|
||||
functionToCall (r, 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 (true, ModalCallbackFunction::forComponent (myCallbackFunction, mySlider));
|
||||
@endcode
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <class ComponentType>
|
||||
static ModalComponentManager::Callback* forComponent (void (*functionToCall) (int, ComponentType*),
|
||||
ComponentType* component)
|
||||
{
|
||||
return create ([functionToCall, comp = WeakReference<Component> { component }] (int r)
|
||||
{
|
||||
functionToCall (r, static_cast<ComponentType*> (comp.get()));
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** 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 (true, 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 create ([functionToCall, param, comp = WeakReference<Component> { component }] (int r)
|
||||
{
|
||||
functionToCall (r, static_cast<ComponentType*> (comp.get()), param);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
ModalCallbackFunction() = delete;
|
||||
~ModalCallbackFunction() = delete;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
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 JUCE_API Callback
|
||||
{
|
||||
public:
|
||||
/** */
|
||||
Callback() = default;
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~Callback() = default;
|
||||
|
||||
/** 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;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (ModalComponentManager)
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** 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 (const Component* component) const;
|
||||
|
||||
/** Returns true if the specified component is currently the topmost modal component. */
|
||||
bool isFrontModalComponent (const 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
|
||||
|
||||
protected:
|
||||
/** Creates a ModalComponentManager.
|
||||
You shouldn't ever call the constructor - it's a singleton, so use ModalComponentManager::getInstance()
|
||||
*/
|
||||
ModalComponentManager();
|
||||
|
||||
/** Destructor. */
|
||||
~ModalComponentManager() override;
|
||||
|
||||
/** @internal */
|
||||
void handleAsyncUpdate() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
friend class Component;
|
||||
|
||||
struct 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.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ModalCallbackFunction
|
||||
{
|
||||
public:
|
||||
/** This is a utility function to create a ModalComponentManager::Callback that will
|
||||
call a callable object.
|
||||
|
||||
The function that you supply must take an integer parameter, which is the result code that
|
||||
was returned when the modal component was dismissed.
|
||||
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <typename CallbackFn>
|
||||
static ModalComponentManager::Callback* create (CallbackFn&& fn)
|
||||
{
|
||||
struct Callable : public ModalComponentManager::Callback
|
||||
{
|
||||
explicit Callable (CallbackFn&& f) : fn (std::forward<CallbackFn> (f)) {}
|
||||
void modalStateFinished (int result) override { NullCheckedInvocation::invoke (std::move (fn), result); }
|
||||
|
||||
std::remove_reference_t<CallbackFn> fn;
|
||||
};
|
||||
|
||||
return new Callable (std::forward<CallbackFn> (fn));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** 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 (true, ModalCallbackFunction::create (myCallbackFunction, 3.0));
|
||||
@endcode
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <typename ParamType>
|
||||
static ModalComponentManager::Callback* create (void (*functionToCall) (int, ParamType),
|
||||
ParamType parameterValue)
|
||||
{
|
||||
return create ([functionToCall, parameterValue] (int r)
|
||||
{
|
||||
functionToCall (r, 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 (true, 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 create ([functionToCall, parameterValue1, parameterValue2] (int r)
|
||||
{
|
||||
functionToCall (r, 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 (true, ModalCallbackFunction::forComponent (myCallbackFunction, mySlider));
|
||||
@endcode
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <class ComponentType>
|
||||
static ModalComponentManager::Callback* forComponent (void (*functionToCall) (int, ComponentType*),
|
||||
ComponentType* component)
|
||||
{
|
||||
return create ([functionToCall, comp = WeakReference<Component> { component }] (int r)
|
||||
{
|
||||
functionToCall (r, static_cast<ComponentType*> (comp.get()));
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** 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 (true, 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 create ([functionToCall, param, comp = WeakReference<Component> { component }] (int r)
|
||||
{
|
||||
functionToCall (r, static_cast<ComponentType*> (comp.get()), param);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
ModalCallbackFunction() = delete;
|
||||
~ModalCallbackFunction() = delete;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
Reference in New Issue
Block a user