git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
119
deps/juce/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityActions.h
vendored
Normal file
119
deps/juce/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityActions.h
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/** An action that can be performed by an accessible UI element.
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
enum class AccessibilityActionType
|
||||
{
|
||||
/** Represents a "press" action.
|
||||
|
||||
This will be called when the user "clicks" the UI element using an
|
||||
accessibility client.
|
||||
*/
|
||||
press,
|
||||
|
||||
/** Represents a "toggle" action.
|
||||
|
||||
This will be called when the user toggles the state of a UI element,
|
||||
for example a toggle button or the selection of a list item.
|
||||
*/
|
||||
toggle,
|
||||
|
||||
/** Indicates that the UI element has received focus.
|
||||
|
||||
This will be called when a UI element receives focus from an accessibility
|
||||
client, or keyboard focus from the application.
|
||||
*/
|
||||
focus,
|
||||
|
||||
/** Represents the user showing a contextual menu for a UI element.
|
||||
|
||||
This will be called for UI elements which expand and collapse to
|
||||
show contextual information or menus, or show a popup.
|
||||
*/
|
||||
showMenu
|
||||
};
|
||||
|
||||
/** A simple wrapper for building a collection of supported accessibility actions
|
||||
and corresponding callbacks for a UI element.
|
||||
|
||||
Pass one of these when constructing an `AccessibilityHandler` to enable users
|
||||
to interact with a UI element via the supported actions.
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
class JUCE_API AccessibilityActions
|
||||
{
|
||||
public:
|
||||
/** Constructor.
|
||||
|
||||
Creates a default AccessibilityActions object with no action callbacks.
|
||||
*/
|
||||
AccessibilityActions() = default;
|
||||
|
||||
/** Adds an action.
|
||||
|
||||
When the user performs this action with an accessibility client
|
||||
`actionCallback` will be called.
|
||||
|
||||
Returns a reference to itself so that several calls can be chained.
|
||||
*/
|
||||
AccessibilityActions& addAction (AccessibilityActionType type,
|
||||
std::function<void()> actionCallback)
|
||||
{
|
||||
actionMap[type] = std::move (actionCallback);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns true if the specified action is supported. */
|
||||
bool contains (AccessibilityActionType type) const
|
||||
{
|
||||
return actionMap.find (type) != actionMap.end();
|
||||
}
|
||||
|
||||
/** If an action has been registered for the provided action type, invokes the
|
||||
action and returns true. Otherwise, returns false.
|
||||
*/
|
||||
bool invoke (AccessibilityActionType type) const
|
||||
{
|
||||
auto iter = actionMap.find (type);
|
||||
|
||||
if (iter == actionMap.end())
|
||||
return false;
|
||||
|
||||
iter->second();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<AccessibilityActionType, std::function<void()>> actionMap;
|
||||
};
|
||||
|
||||
} // namespace juce
|
81
deps/juce/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityEvent.h
vendored
Normal file
81
deps/juce/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityEvent.h
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/** A list of events that can be notified to any subscribed accessibility clients.
|
||||
|
||||
To post a notification, call `AccessibilityHandler::notifyAccessibilityEvent`
|
||||
on the associated handler with the appropriate `AccessibilityEvent` type and
|
||||
listening clients will be notified.
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
enum class AccessibilityEvent
|
||||
{
|
||||
/** Indicates that the UI element's value has changed.
|
||||
|
||||
This should be called on the handler that implements `AccessibilityValueInterface`
|
||||
for the UI element that has changed.
|
||||
*/
|
||||
valueChanged,
|
||||
|
||||
/** Indicates that the title of the UI element has changed.
|
||||
|
||||
This should be called on the handler whose title has changed.
|
||||
*/
|
||||
titleChanged,
|
||||
|
||||
/** Indicates that the structure of the UI elements has changed in a
|
||||
significant way.
|
||||
|
||||
This should be called on the top-level handler whose structure has changed.
|
||||
*/
|
||||
structureChanged,
|
||||
|
||||
/** Indicates that the selection of a text element has changed.
|
||||
|
||||
This should be called on the handler that implements `AccessibilityTextInterface`
|
||||
for the text element that has changed.
|
||||
*/
|
||||
textSelectionChanged,
|
||||
|
||||
/** Indicates that the visible text of a text element has changed.
|
||||
|
||||
This should be called on the handler that implements `AccessibilityTextInterface`
|
||||
for the text element that has changed.
|
||||
*/
|
||||
textChanged,
|
||||
|
||||
/** Indicates that the selection of rows in a list or table has changed.
|
||||
|
||||
This should be called on the handler that implements `AccessibilityTableInterface`
|
||||
for the UI element that has changed.
|
||||
*/
|
||||
rowSelectionChanged
|
||||
};
|
||||
|
||||
}
|
71
deps/juce/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityRole.h
vendored
Normal file
71
deps/juce/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityRole.h
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/** The list of available roles for an AccessibilityHandler object.
|
||||
|
||||
When creating a custom AccessibilityHandler you should select the role that
|
||||
best describes the UI element being represented.
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
enum class AccessibilityRole
|
||||
{
|
||||
button,
|
||||
toggleButton,
|
||||
radioButton,
|
||||
comboBox,
|
||||
image,
|
||||
slider,
|
||||
label,
|
||||
staticText,
|
||||
editableText,
|
||||
menuItem,
|
||||
menuBar,
|
||||
popupMenu,
|
||||
table,
|
||||
tableHeader,
|
||||
column,
|
||||
row,
|
||||
cell,
|
||||
hyperlink,
|
||||
list,
|
||||
listItem,
|
||||
tree,
|
||||
treeItem,
|
||||
progressBar,
|
||||
group,
|
||||
dialogWindow,
|
||||
window,
|
||||
scrollBar,
|
||||
tooltip,
|
||||
splashScreen,
|
||||
ignored,
|
||||
unspecified
|
||||
};
|
||||
|
||||
}
|
61
deps/juce/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityCellInterface.h
vendored
Normal file
61
deps/juce/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityCellInterface.h
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/** An abstract interface which represents a UI element that supports a cell interface.
|
||||
|
||||
This typically represents a single cell inside of a UI element which implements an
|
||||
AccessibilityTableInterface.
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
class JUCE_API AccessibilityCellInterface
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~AccessibilityCellInterface() = default;
|
||||
|
||||
/** Returns the column index of the cell in the table. */
|
||||
virtual int getColumnIndex() const = 0;
|
||||
|
||||
/** Returns the number of columns occupied by the cell in the table. */
|
||||
virtual int getColumnSpan() const = 0;
|
||||
|
||||
/** Returns the row index of the cell in the table. */
|
||||
virtual int getRowIndex() const = 0;
|
||||
|
||||
/** Returns the number of rows occupied by the cell in the table. */
|
||||
virtual int getRowSpan() const = 0;
|
||||
|
||||
/** Returns the indentation level for the cell. */
|
||||
virtual int getDisclosureLevel() const = 0;
|
||||
|
||||
/** Returns the AccessibilityHandler of the table which contains the cell. */
|
||||
virtual const AccessibilityHandler* getTableHandler() const = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
54
deps/juce/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityTableInterface.h
vendored
Normal file
54
deps/juce/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityTableInterface.h
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/** An abstract interface which represents a UI element that supports a table interface.
|
||||
|
||||
Examples of UI elements which typically support a table interface are lists, tables,
|
||||
and trees.
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
class JUCE_API AccessibilityTableInterface
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~AccessibilityTableInterface() = default;
|
||||
|
||||
/** Returns the total number of rows in the table. */
|
||||
virtual int getNumRows() const = 0;
|
||||
|
||||
/** Returns the total number of columns in the table. */
|
||||
virtual int getNumColumns() const = 0;
|
||||
|
||||
/** Returns the AccessibilityHandler for one of the cells in the table, or
|
||||
nullptr if there is no cell at the specified position.
|
||||
*/
|
||||
virtual const AccessibilityHandler* getCellHandler (int row, int column) const = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
81
deps/juce/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityTextInterface.h
vendored
Normal file
81
deps/juce/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityTextInterface.h
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/** An abstract interface which represents a UI element that supports a text interface.
|
||||
|
||||
A UI element can use this interface to provide extended textual information which
|
||||
cannot be conveyed using just the title, description, and help text properties of
|
||||
AccessibilityHandler. This is typically for text that an accessibility client might
|
||||
want to read line-by-line, or provide text selection and input for.
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
class JUCE_API AccessibilityTextInterface
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~AccessibilityTextInterface() = default;
|
||||
|
||||
/** Returns true if the text being displayed is protected and should not be
|
||||
exposed to the user, for example a password entry field.
|
||||
*/
|
||||
virtual bool isDisplayingProtectedText() const = 0;
|
||||
|
||||
/** Returns true if the text being displayed is read-only or false if editable. */
|
||||
virtual bool isReadOnly() const = 0;
|
||||
|
||||
/** Returns the total number of characters in the text element. */
|
||||
virtual int getTotalNumCharacters() const = 0;
|
||||
|
||||
/** Returns the range of characters that are currently selected, or an empty
|
||||
range if nothing is selected.
|
||||
*/
|
||||
virtual Range<int> getSelection() const = 0;
|
||||
|
||||
/** Selects a section of the text. */
|
||||
virtual void setSelection (Range<int> newRange) = 0;
|
||||
|
||||
/** Gets the current text insertion position, if supported. */
|
||||
virtual int getTextInsertionOffset() const = 0;
|
||||
|
||||
/** Returns a section of text. */
|
||||
virtual String getText (Range<int> range) const = 0;
|
||||
|
||||
/** Replaces the text with a new string. */
|
||||
virtual void setText (const String& newText) = 0;
|
||||
|
||||
/** Returns the bounding box in screen coordinates for a range of text.
|
||||
As the range may span multiple lines, this method returns a RectangleList.
|
||||
*/
|
||||
virtual RectangleList<int> getTextBounds (Range<int> textRange) const = 0;
|
||||
|
||||
/** Returns the index of the character at a given position in screen coordinates. */
|
||||
virtual int getOffsetAtPoint (Point<int> point) const = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
222
deps/juce/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityValueInterface.h
vendored
Normal file
222
deps/juce/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityValueInterface.h
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/** An abstract interface representing the value of an accessibility element.
|
||||
|
||||
Values should be used when information needs to be conveyed which cannot
|
||||
be represented by the accessibility element's label alone. For example, a
|
||||
gain slider with the label "Gain" needs to also provide a value for its
|
||||
position whereas a "Save" button does not.
|
||||
|
||||
This class allows for full control over the value text/numeric conversion,
|
||||
ranged, and read-only properties but in most cases you'll want to use one
|
||||
of the derived classes below which handle some of this for you.
|
||||
|
||||
@see AccessibilityTextValueInterface, AccessibilityNumericValueInterface,
|
||||
AccessibilityRangedNumericValueInterface
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
class JUCE_API AccessibilityValueInterface
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~AccessibilityValueInterface() = default;
|
||||
|
||||
/** Returns true if the value is read-only and cannot be modified by an
|
||||
accessibility client.
|
||||
|
||||
@see setValue, setValueAsString
|
||||
*/
|
||||
virtual bool isReadOnly() const = 0;
|
||||
|
||||
/** Returns the current value as a double. */
|
||||
virtual double getCurrentValue() const = 0;
|
||||
|
||||
/** Returns the current value as a String. */
|
||||
virtual String getCurrentValueAsString() const = 0;
|
||||
|
||||
/** Sets the current value to a new double value. */
|
||||
virtual void setValue (double newValue) = 0;
|
||||
|
||||
/** Sets the current value to a new String value. */
|
||||
virtual void setValueAsString (const String& newValue) = 0;
|
||||
|
||||
/** Represents the range of this value, if supported.
|
||||
|
||||
Return one of these from the `getRange()` method, providing a minimum,
|
||||
maximum, and interval value for the range to indicate that this is a
|
||||
ranged value.
|
||||
|
||||
The default state is an "invalid" range, indicating that the accessibility
|
||||
element does not support ranged values.
|
||||
|
||||
@see AccessibilityRangedNumericValueInterface
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
class JUCE_API AccessibleValueRange
|
||||
{
|
||||
public:
|
||||
/** Constructor.
|
||||
|
||||
Creates a default, "invalid" range that can be returned from
|
||||
`AccessibilityValueInterface::getRange()` to indicate that the value
|
||||
interface does not support ranged values.
|
||||
*/
|
||||
AccessibleValueRange() = default;
|
||||
|
||||
/** The minimum and maximum values for this range, inclusive. */
|
||||
struct JUCE_API MinAndMax { double min, max; };
|
||||
|
||||
/** Constructor.
|
||||
|
||||
Creates a valid AccessibleValueRange with the provided minimum, maximum,
|
||||
and interval values.
|
||||
*/
|
||||
AccessibleValueRange (MinAndMax valueRange, double interval)
|
||||
: valid (true),
|
||||
range (valueRange),
|
||||
stepSize (interval)
|
||||
{
|
||||
jassert (range.min < range.max);
|
||||
}
|
||||
|
||||
/** Returns true if this represents a valid range. */
|
||||
bool isValid() const noexcept { return valid; }
|
||||
|
||||
/** Returns the minimum value for this range. */
|
||||
double getMinimumValue() const noexcept { return range.min; }
|
||||
|
||||
/** Returns the maxiumum value for this range. */
|
||||
double getMaximumValue() const noexcept { return range.max; }
|
||||
|
||||
/** Returns the interval for this range. */
|
||||
double getInterval() const noexcept { return stepSize; }
|
||||
|
||||
private:
|
||||
bool valid = false;
|
||||
MinAndMax range {};
|
||||
double stepSize = 0.0;
|
||||
};
|
||||
|
||||
/** If this is a ranged value, this should return a valid AccessibleValueRange
|
||||
object representing the supported numerical range.
|
||||
*/
|
||||
virtual AccessibleValueRange getRange() const = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** A value interface that represents a text value.
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
class JUCE_API AccessibilityTextValueInterface : public AccessibilityValueInterface
|
||||
{
|
||||
public:
|
||||
/** Returns true if the value is read-only and cannot be modified by an
|
||||
accessibility client.
|
||||
|
||||
@see setValueAsString
|
||||
*/
|
||||
bool isReadOnly() const override = 0;
|
||||
|
||||
/** Returns the current value. */
|
||||
String getCurrentValueAsString() const override = 0;
|
||||
|
||||
/** Sets the current value to a new value. */
|
||||
void setValueAsString (const String& newValue) override = 0;
|
||||
|
||||
/** @internal */
|
||||
double getCurrentValue() const final { return getCurrentValueAsString().getDoubleValue(); }
|
||||
/** @internal */
|
||||
void setValue (double newValue) final { setValueAsString (String (newValue)); }
|
||||
/** @internal */
|
||||
AccessibleValueRange getRange() const final { return {}; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** A value interface that represents a non-ranged numeric value.
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
class JUCE_API AccessibilityNumericValueInterface : public AccessibilityValueInterface
|
||||
{
|
||||
public:
|
||||
/** Returns true if the value is read-only and cannot be modified by an
|
||||
accessibility client.
|
||||
|
||||
@see setValue
|
||||
*/
|
||||
bool isReadOnly() const override = 0;
|
||||
|
||||
/** Returns the current value. */
|
||||
double getCurrentValue() const override = 0;
|
||||
|
||||
/** Sets the current value to a new value. */
|
||||
void setValue (double newValue) override = 0;
|
||||
|
||||
/** @internal */
|
||||
String getCurrentValueAsString() const final { return String (getCurrentValue()); }
|
||||
/** @internal */
|
||||
void setValueAsString (const String& newValue) final { setValue (newValue.getDoubleValue()); }
|
||||
/** @internal */
|
||||
AccessibleValueRange getRange() const final { return {}; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** A value interface that represents a ranged numeric value.
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
class JUCE_API AccessibilityRangedNumericValueInterface : public AccessibilityValueInterface
|
||||
{
|
||||
public:
|
||||
/** Returns true if the value is read-only and cannot be modified by an
|
||||
accessibility client.
|
||||
|
||||
@see setValueAsString
|
||||
*/
|
||||
bool isReadOnly() const override = 0;
|
||||
|
||||
/** Returns the current value. */
|
||||
double getCurrentValue() const override = 0;
|
||||
|
||||
/** Sets the current value to a new value. */
|
||||
void setValue (double newValue) override = 0;
|
||||
|
||||
/** Returns the range. */
|
||||
AccessibleValueRange getRange() const override = 0;
|
||||
|
||||
/** @internal */
|
||||
String getCurrentValueAsString() const final { return String (getCurrentValue()); }
|
||||
/** @internal */
|
||||
void setValueAsString (const String& newValue) final { setValue (newValue.getDoubleValue()); }
|
||||
};
|
||||
|
||||
} // namespace juce
|
340
deps/juce/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp
vendored
Normal file
340
deps/juce/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp
vendored
Normal file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
AccessibilityHandler* AccessibilityHandler::currentlyFocusedHandler = nullptr;
|
||||
|
||||
enum class InternalAccessibilityEvent
|
||||
{
|
||||
elementCreated,
|
||||
elementDestroyed,
|
||||
elementMovedOrResized,
|
||||
focusChanged,
|
||||
windowOpened,
|
||||
windowClosed
|
||||
};
|
||||
|
||||
void notifyAccessibilityEventInternal (const AccessibilityHandler&, InternalAccessibilityEvent);
|
||||
|
||||
inline String getAccessibleApplicationOrPluginName()
|
||||
{
|
||||
#if defined (JucePlugin_Name)
|
||||
return JucePlugin_Name;
|
||||
#else
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
return app->getApplicationName();
|
||||
|
||||
return "JUCE Application";
|
||||
#endif
|
||||
}
|
||||
|
||||
AccessibilityHandler::AccessibilityHandler (Component& comp,
|
||||
AccessibilityRole accessibilityRole,
|
||||
AccessibilityActions accessibilityActions,
|
||||
Interfaces interfacesIn)
|
||||
: component (comp),
|
||||
typeIndex (typeid (component)),
|
||||
role (accessibilityRole),
|
||||
actions (std::move (accessibilityActions)),
|
||||
interfaces (std::move (interfacesIn)),
|
||||
nativeImpl (createNativeImpl (*this))
|
||||
{
|
||||
notifyAccessibilityEventInternal (*this, InternalAccessibilityEvent::elementCreated);
|
||||
}
|
||||
|
||||
AccessibilityHandler::~AccessibilityHandler()
|
||||
{
|
||||
giveAwayFocus();
|
||||
notifyAccessibilityEventInternal (*this, InternalAccessibilityEvent::elementDestroyed);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
AccessibleState AccessibilityHandler::getCurrentState() const
|
||||
{
|
||||
if (component.isCurrentlyBlockedByAnotherModalComponent()
|
||||
&& Component::getCurrentlyModalComponent()->isVisible())
|
||||
return {};
|
||||
|
||||
auto state = AccessibleState().withFocusable();
|
||||
|
||||
return hasFocus (false) ? state.withFocused() : state;
|
||||
}
|
||||
|
||||
bool AccessibilityHandler::isIgnored() const
|
||||
{
|
||||
return role == AccessibilityRole::ignored || getCurrentState().isIgnored();
|
||||
}
|
||||
|
||||
static bool isComponentVisibleWithinWindow (const Component& comp)
|
||||
{
|
||||
if (auto* peer = comp.getPeer())
|
||||
return ! peer->getAreaCoveredBy (comp).getIntersection (peer->getComponent().getLocalBounds()).isEmpty();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isComponentVisibleWithinParent (Component* comp)
|
||||
{
|
||||
if (auto* parent = comp->getParentComponent())
|
||||
{
|
||||
if (comp->getBoundsInParent().getIntersection (parent->getLocalBounds()).isEmpty())
|
||||
return false;
|
||||
|
||||
return isComponentVisibleWithinParent (parent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AccessibilityHandler::isVisibleWithinParent() const
|
||||
{
|
||||
return getCurrentState().isAccessibleOffscreen()
|
||||
|| (isComponentVisibleWithinParent (&component) && isComponentVisibleWithinWindow (component));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const AccessibilityActions& AccessibilityHandler::getActions() const noexcept
|
||||
{
|
||||
return actions;
|
||||
}
|
||||
|
||||
AccessibilityValueInterface* AccessibilityHandler::getValueInterface() const
|
||||
{
|
||||
return interfaces.value.get();
|
||||
}
|
||||
|
||||
AccessibilityTableInterface* AccessibilityHandler::getTableInterface() const
|
||||
{
|
||||
return interfaces.table.get();
|
||||
}
|
||||
|
||||
AccessibilityCellInterface* AccessibilityHandler::getCellInterface() const
|
||||
{
|
||||
return interfaces.cell.get();
|
||||
}
|
||||
|
||||
AccessibilityTextInterface* AccessibilityHandler::getTextInterface() const
|
||||
{
|
||||
return interfaces.text.get();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static AccessibilityHandler* findEnclosingHandler (Component* comp)
|
||||
{
|
||||
if (comp != nullptr)
|
||||
{
|
||||
if (auto* handler = comp->getAccessibilityHandler())
|
||||
return handler;
|
||||
|
||||
return findEnclosingHandler (comp->getParentComponent());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static AccessibilityHandler* getUnignoredAncestor (AccessibilityHandler* handler)
|
||||
{
|
||||
while (handler != nullptr
|
||||
&& (handler->isIgnored() || ! handler->isVisibleWithinParent())
|
||||
&& handler->getParent() != nullptr)
|
||||
{
|
||||
handler = handler->getParent();
|
||||
}
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
static AccessibilityHandler* findFirstUnignoredChild (const std::vector<AccessibilityHandler*>& handlers)
|
||||
{
|
||||
if (! handlers.empty())
|
||||
{
|
||||
const auto iter = std::find_if (handlers.cbegin(), handlers.cend(),
|
||||
[] (const AccessibilityHandler* handler) { return ! handler->isIgnored() && handler->isVisibleWithinParent(); });
|
||||
|
||||
if (iter != handlers.cend())
|
||||
return *iter;
|
||||
|
||||
for (auto* handler : handlers)
|
||||
if (auto* unignored = findFirstUnignoredChild (handler->getChildren()))
|
||||
return unignored;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static AccessibilityHandler* getFirstUnignoredDescendant (AccessibilityHandler* handler)
|
||||
{
|
||||
if (handler != nullptr && (handler->isIgnored() || ! handler->isVisibleWithinParent()))
|
||||
return findFirstUnignoredChild (handler->getChildren());
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
AccessibilityHandler* AccessibilityHandler::getParent() const
|
||||
{
|
||||
if (auto* focusContainer = component.findFocusContainer())
|
||||
return getUnignoredAncestor (findEnclosingHandler (focusContainer));
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<AccessibilityHandler*> AccessibilityHandler::getChildren() const
|
||||
{
|
||||
if (! component.isFocusContainer() && component.getParentComponent() != nullptr)
|
||||
return {};
|
||||
|
||||
const auto addChildComponentHandler = [this] (Component* focusableComponent,
|
||||
std::vector<AccessibilityHandler*>& childHandlers)
|
||||
{
|
||||
if (focusableComponent == nullptr)
|
||||
return;
|
||||
|
||||
if (auto* handler = findEnclosingHandler (focusableComponent))
|
||||
{
|
||||
if (! handler->getCurrentState().isFocusable() || ! isParentOf (handler))
|
||||
return;
|
||||
|
||||
if (auto* unignored = getFirstUnignoredDescendant (handler))
|
||||
if (std::find (childHandlers.cbegin(), childHandlers.cend(), unignored) == childHandlers.cend())
|
||||
childHandlers.push_back (unignored);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<AccessibilityHandler*> children;
|
||||
|
||||
if (auto traverser = component.createFocusTraverser())
|
||||
{
|
||||
addChildComponentHandler (traverser->getDefaultComponent (&component), children);
|
||||
|
||||
for (auto* focusableChild : traverser->getAllComponents (&component))
|
||||
addChildComponentHandler (focusableChild, children);
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
bool AccessibilityHandler::isParentOf (const AccessibilityHandler* possibleChild) const noexcept
|
||||
{
|
||||
while (possibleChild != nullptr)
|
||||
{
|
||||
possibleChild = possibleChild->getParent();
|
||||
|
||||
if (possibleChild == this)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AccessibilityHandler* AccessibilityHandler::getChildAt (Point<int> screenPoint)
|
||||
{
|
||||
if (auto* comp = Desktop::getInstance().findComponentAt (screenPoint))
|
||||
{
|
||||
if (auto* handler = getUnignoredAncestor (findEnclosingHandler (comp)))
|
||||
if (isParentOf (handler))
|
||||
return handler;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AccessibilityHandler* AccessibilityHandler::getChildFocus()
|
||||
{
|
||||
return hasFocus (true) ? getUnignoredAncestor (currentlyFocusedHandler)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
bool AccessibilityHandler::hasFocus (bool trueIfChildFocused) const
|
||||
{
|
||||
return currentlyFocusedHandler != nullptr
|
||||
&& (currentlyFocusedHandler == this
|
||||
|| (trueIfChildFocused && isParentOf (currentlyFocusedHandler)));
|
||||
}
|
||||
|
||||
void AccessibilityHandler::grabFocus()
|
||||
{
|
||||
if (! hasFocus (false))
|
||||
grabFocusInternal (true);
|
||||
}
|
||||
|
||||
void AccessibilityHandler::giveAwayFocus() const
|
||||
{
|
||||
if (hasFocus (true))
|
||||
giveAwayFocusInternal();
|
||||
}
|
||||
|
||||
void AccessibilityHandler::grabFocusInternal (bool canTryParent)
|
||||
{
|
||||
if (getCurrentState().isFocusable() && ! isIgnored())
|
||||
{
|
||||
takeFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isParentOf (currentlyFocusedHandler))
|
||||
return;
|
||||
|
||||
if (auto traverser = component.createFocusTraverser())
|
||||
{
|
||||
if (auto* defaultComp = traverser->getDefaultComponent (&component))
|
||||
{
|
||||
if (auto* handler = getUnignoredAncestor (findEnclosingHandler (defaultComp)))
|
||||
{
|
||||
if (isParentOf (handler))
|
||||
{
|
||||
handler->grabFocusInternal (false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canTryParent)
|
||||
if (auto* parent = getParent())
|
||||
parent->grabFocusInternal (true);
|
||||
}
|
||||
|
||||
void AccessibilityHandler::giveAwayFocusInternal() const
|
||||
{
|
||||
currentlyFocusedHandler = nullptr;
|
||||
notifyAccessibilityEventInternal (*this, InternalAccessibilityEvent::focusChanged);
|
||||
}
|
||||
|
||||
void AccessibilityHandler::takeFocus()
|
||||
{
|
||||
currentlyFocusedHandler = this;
|
||||
notifyAccessibilityEventInternal (*this, InternalAccessibilityEvent::focusChanged);
|
||||
|
||||
if ((component.isShowing() || component.isOnDesktop())
|
||||
&& component.getWantsKeyboardFocus()
|
||||
&& ! component.hasKeyboardFocus (true))
|
||||
{
|
||||
component.grabKeyboardFocus();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
325
deps/juce/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.h
vendored
Normal file
325
deps/juce/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.h
vendored
Normal file
@ -0,0 +1,325 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
class AccessibilityNativeHandle;
|
||||
|
||||
/** Base class for accessible Components.
|
||||
|
||||
This class wraps a Component and provides methods that allow an accessibility client,
|
||||
such as VoiceOver on macOS, or Narrator on Windows, to control it.
|
||||
|
||||
It handles hierarchical navigation, properties, state, and various interfaces.
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
class JUCE_API AccessibilityHandler
|
||||
{
|
||||
public:
|
||||
/** Utility struct which holds one or more accessibility interfaces.
|
||||
|
||||
The main purpose of this class is to provide convenience constructors from each
|
||||
of the four types of accessibility interface.
|
||||
*/
|
||||
struct JUCE_API Interfaces
|
||||
{
|
||||
Interfaces() = default;
|
||||
|
||||
Interfaces (std::unique_ptr<AccessibilityValueInterface> ptr) : value (std::move (ptr)) {}
|
||||
Interfaces (std::unique_ptr<AccessibilityTextInterface> ptr) : text (std::move (ptr)) {}
|
||||
Interfaces (std::unique_ptr<AccessibilityTableInterface> ptr) : table (std::move (ptr)) {}
|
||||
Interfaces (std::unique_ptr<AccessibilityCellInterface> ptr) : cell (std::move (ptr)) {}
|
||||
|
||||
Interfaces (std::unique_ptr<AccessibilityValueInterface> valueIn,
|
||||
std::unique_ptr<AccessibilityTextInterface> textIn,
|
||||
std::unique_ptr<AccessibilityTableInterface> tableIn,
|
||||
std::unique_ptr<AccessibilityCellInterface> cellIn)
|
||||
: value (std::move (valueIn)),
|
||||
text (std::move (textIn)),
|
||||
table (std::move (tableIn)),
|
||||
cell (std::move (cellIn))
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<AccessibilityValueInterface> value;
|
||||
std::unique_ptr<AccessibilityTextInterface> text;
|
||||
std::unique_ptr<AccessibilityTableInterface> table;
|
||||
std::unique_ptr<AccessibilityCellInterface> cell;
|
||||
};
|
||||
|
||||
/** Constructor.
|
||||
|
||||
This will create a AccessibilityHandler which wraps the provided Component and makes
|
||||
it visible to accessibility clients. You must also specify a role for the UI element
|
||||
from the `AccessibilityRole` list which best describes it.
|
||||
|
||||
To enable users to interact with the UI element you should provide the set of supported
|
||||
actions and their associated callbacks via the `accessibilityActions` parameter.
|
||||
|
||||
For UI elements that support more complex interaction the value, text, table, and cell
|
||||
interfaces should be implemented as required and passed as the final argument of this
|
||||
constructor. See the documentation of these classes for more information about the
|
||||
types of control they represent and which methods need to be implemented.
|
||||
*/
|
||||
AccessibilityHandler (Component& componentToWrap,
|
||||
AccessibilityRole accessibilityRole,
|
||||
AccessibilityActions actions = {},
|
||||
Interfaces interfaces = {});
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~AccessibilityHandler();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the Component that this handler represents. */
|
||||
const Component& getComponent() const noexcept { return component; }
|
||||
|
||||
/** Returns the Component that this handler represents. */
|
||||
Component& getComponent() noexcept { return component; }
|
||||
|
||||
//==============================================================================
|
||||
/** The type of UI element that this accessibility handler represents.
|
||||
|
||||
@see AccessibilityRole
|
||||
*/
|
||||
AccessibilityRole getRole() const noexcept { return role; }
|
||||
|
||||
/** The title of the UI element.
|
||||
|
||||
This will be read out by the system and should be concise, preferably matching
|
||||
the visible title of the UI element (if any). For example, this might be the
|
||||
text of a button or a simple label.
|
||||
|
||||
The default implementation will call `Component::getTitle()`, but you can override
|
||||
this to return a different string if required.
|
||||
|
||||
If neither a name nor a description is provided then the UI element may be
|
||||
ignored by accessibility clients.
|
||||
|
||||
This must be a localised string.
|
||||
*/
|
||||
virtual String getTitle() const { return component.getTitle(); }
|
||||
|
||||
/** A short description of the UI element.
|
||||
|
||||
This may be read out by the system. It should not include the type of the UI
|
||||
element and should ideally be a single word, for example "Open" for a button
|
||||
that opens a window.
|
||||
|
||||
The default implementation will call `Component::getDescription()`, but you
|
||||
can override this to return a different string if required.
|
||||
|
||||
If neither a name nor a description is provided then the UI element may be
|
||||
ignored by accessibility clients.
|
||||
|
||||
This must be a localised string.
|
||||
*/
|
||||
virtual String getDescription() const { return component.getDescription(); }
|
||||
|
||||
/** Some help text for the UI element (if required).
|
||||
|
||||
This may be read out by the system. This string functions in a similar way to
|
||||
a tooltip, for example "Click to open window." for a button which opens a window.
|
||||
|
||||
The default implementation will call `Component::getHelpText()`, but you can
|
||||
override this to return a different string if required.
|
||||
|
||||
This must be a localised string.
|
||||
*/
|
||||
virtual String getHelp() const { return component.getHelpText(); }
|
||||
|
||||
/** Returns the current state of the UI element.
|
||||
|
||||
The default implementation of this method will set the focusable flag and, if
|
||||
this UI element is currently focused, will also set the focused flag.
|
||||
*/
|
||||
virtual AccessibleState getCurrentState() const;
|
||||
|
||||
/** Returns true if this UI element should be ignored by accessibility clients. */
|
||||
bool isIgnored() const;
|
||||
|
||||
/** Returns true if this UI element is visible within its parent.
|
||||
|
||||
This will always return true for UI elements with the AccessibleState::accessibleOffscreen
|
||||
flag set.
|
||||
*/
|
||||
bool isVisibleWithinParent() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the set of actions that the UI element supports and the associated
|
||||
callbacks.
|
||||
*/
|
||||
const AccessibilityActions& getActions() const noexcept;
|
||||
|
||||
/** Returns the value interface for this UI element, or nullptr if it is not supported.
|
||||
|
||||
@see AccessibilityValueInterface
|
||||
*/
|
||||
AccessibilityValueInterface* getValueInterface() const;
|
||||
|
||||
/** Returns the table interface for this UI element, or nullptr if it is not supported.
|
||||
|
||||
@see AccessibilityTableInterface
|
||||
*/
|
||||
AccessibilityTableInterface* getTableInterface() const;
|
||||
|
||||
/** Returns the cell interface for this UI element, or nullptr if it is not supported.
|
||||
|
||||
@see AccessibilityCellInterface
|
||||
*/
|
||||
AccessibilityCellInterface* getCellInterface() const;
|
||||
|
||||
/** Returns the text interface for this UI element, or nullptr if it is not supported.
|
||||
|
||||
@see AccessibilityTextInterface
|
||||
*/
|
||||
AccessibilityTextInterface* getTextInterface() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the first unignored parent of this UI element in the accessibility hierarchy,
|
||||
or nullptr if this is a root element without a parent.
|
||||
*/
|
||||
AccessibilityHandler* getParent() const;
|
||||
|
||||
/** Returns the unignored children of this UI element in the accessibility hierarchy. */
|
||||
std::vector<AccessibilityHandler*> getChildren() const;
|
||||
|
||||
/** Checks whether a given UI element is a child of this one in the accessibility
|
||||
hierarchy.
|
||||
*/
|
||||
bool isParentOf (const AccessibilityHandler* possibleChild) const noexcept;
|
||||
|
||||
/** Returns the deepest child of this UI element in the accessibility hierarchy that
|
||||
contains the given screen point, or nullptr if there is no child at this point.
|
||||
*/
|
||||
AccessibilityHandler* getChildAt (Point<int> screenPoint);
|
||||
|
||||
/** Returns the deepest UI element which currently has focus.
|
||||
|
||||
This can be a child of this UI element or, if no child is focused,
|
||||
this element itself.
|
||||
|
||||
Note that this can be different to the value of the Component with keyboard
|
||||
focus returned by Component::getCurrentlyFocusedComponent().
|
||||
|
||||
@see hasFocus
|
||||
*/
|
||||
AccessibilityHandler* getChildFocus();
|
||||
|
||||
/** Returns true if this UI element has the focus.
|
||||
|
||||
@param trueIfChildFocused if this is true, this method will also return true
|
||||
if any child of this UI element in the accessibility
|
||||
hierarchy has focus
|
||||
*/
|
||||
bool hasFocus (bool trueIfChildFocused) const;
|
||||
|
||||
/** Tries to give focus to this UI element.
|
||||
|
||||
If the UI element is focusable and not ignored this will update the currently focused
|
||||
element, try to give keyboard focus to the Component it represents, and notify any
|
||||
listening accessibility clients that the current focus has changed.
|
||||
|
||||
@see hasFocus, giveAwayFocus
|
||||
*/
|
||||
void grabFocus();
|
||||
|
||||
/** If this UI element or any of its children in the accessibility hierarchy currently
|
||||
have focus, this will defocus it.
|
||||
|
||||
This will also give away the keyboard focus from the Component it represents, and
|
||||
notify any listening accessibility clients that the current focus has changed.
|
||||
|
||||
@see hasFocus, grabFocus
|
||||
*/
|
||||
void giveAwayFocus() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Used to send a notification to any observing accessibility clients that something
|
||||
has changed in the UI element.
|
||||
|
||||
@see AccessibilityEvent
|
||||
*/
|
||||
void notifyAccessibilityEvent (AccessibilityEvent event) const;
|
||||
|
||||
/** A priority level that can help an accessibility client determine how to handle
|
||||
an announcement request.
|
||||
|
||||
Exactly what this controls is platform-specific, but generally a low priority
|
||||
announcement will be read when the screen reader is free, whereas a high priority
|
||||
announcement will interrupt the current speech.
|
||||
*/
|
||||
enum class AnnouncementPriority
|
||||
{
|
||||
low,
|
||||
medium,
|
||||
high
|
||||
};
|
||||
|
||||
/** Posts an announcement to be made to the user.
|
||||
|
||||
@param announcementString a localised string containing the announcement to be read out
|
||||
@param priority the appropriate priority level for the announcement
|
||||
*/
|
||||
static void postAnnouncement (const String& announcementString, AnnouncementPriority priority);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
AccessibilityNativeHandle* getNativeImplementation() const;
|
||||
/** @internal */
|
||||
std::type_index getTypeIndex() const { return typeIndex; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
friend class AccessibilityNativeHandle;
|
||||
|
||||
//==============================================================================
|
||||
void grabFocusInternal (bool);
|
||||
void giveAwayFocusInternal() const;
|
||||
void takeFocus();
|
||||
|
||||
static AccessibilityHandler* currentlyFocusedHandler;
|
||||
|
||||
//==============================================================================
|
||||
Component& component;
|
||||
std::type_index typeIndex;
|
||||
|
||||
const AccessibilityRole role;
|
||||
AccessibilityActions actions;
|
||||
|
||||
Interfaces interfaces;
|
||||
|
||||
//==============================================================================
|
||||
class AccessibilityNativeImpl;
|
||||
std::unique_ptr<AccessibilityNativeImpl> nativeImpl;
|
||||
|
||||
static std::unique_ptr<AccessibilityNativeImpl> createNativeImpl (AccessibilityHandler&);
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityHandler)
|
||||
};
|
||||
|
||||
} // namespace juce
|
227
deps/juce/modules/juce_gui_basics/accessibility/juce_AccessibilityState.h
vendored
Normal file
227
deps/juce/modules/juce_gui_basics/accessibility/juce_AccessibilityState.h
vendored
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/** Represents the state of an accessible UI element.
|
||||
|
||||
An instance of this class is returned by `AccessibilityHandler::getCurrentState()`
|
||||
to convey its current state to an accessibility client.
|
||||
|
||||
@see AccessibilityHandler
|
||||
|
||||
@tags{Accessibility}
|
||||
*/
|
||||
class JUCE_API AccessibleState
|
||||
{
|
||||
public:
|
||||
/** Constructor.
|
||||
|
||||
Represents a "default" state with no flags set. To set a flag, use one of the
|
||||
`withX()` methods - these can be chained together to set multiple flags.
|
||||
*/
|
||||
AccessibleState() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the checkable flag and returns the new state.
|
||||
|
||||
@see isCheckable
|
||||
*/
|
||||
AccessibleState withCheckable() const noexcept { return withFlag (Flags::checkable); }
|
||||
|
||||
/** Sets the checked flag and returns the new state.
|
||||
|
||||
@see isChecked
|
||||
*/
|
||||
AccessibleState withChecked() const noexcept { return withFlag (Flags::checked); }
|
||||
|
||||
/** Sets the collapsed flag and returns the new state.
|
||||
|
||||
@see isCollapsed
|
||||
*/
|
||||
AccessibleState withCollapsed() const noexcept { return withFlag (Flags::collapsed); }
|
||||
|
||||
/** Sets the expandable flag and returns the new state.
|
||||
|
||||
@see isExpandable
|
||||
*/
|
||||
AccessibleState withExpandable() const noexcept { return withFlag (Flags::expandable); }
|
||||
|
||||
/** Sets the expanded flag and returns the new state.
|
||||
|
||||
@see isExpanded
|
||||
*/
|
||||
AccessibleState withExpanded() const noexcept { return withFlag (Flags::expanded); }
|
||||
|
||||
/** Sets the focusable flag and returns the new state.
|
||||
|
||||
@see isFocusable
|
||||
*/
|
||||
AccessibleState withFocusable() const noexcept { return withFlag (Flags::focusable); }
|
||||
|
||||
/** Sets the focused flag and returns the new state.
|
||||
|
||||
@see isFocused
|
||||
*/
|
||||
AccessibleState withFocused() const noexcept { return withFlag (Flags::focused); }
|
||||
|
||||
/** Sets the ignored flag and returns the new state.
|
||||
|
||||
@see isIgnored
|
||||
*/
|
||||
AccessibleState withIgnored() const noexcept { return withFlag (Flags::ignored); }
|
||||
|
||||
/** Sets the selectable flag and returns the new state.
|
||||
|
||||
@see isSelectable
|
||||
*/
|
||||
AccessibleState withSelectable() const noexcept { return withFlag (Flags::selectable); }
|
||||
|
||||
/** Sets the multiSelectable flag and returns the new state.
|
||||
|
||||
@see isMultiSelectable
|
||||
*/
|
||||
AccessibleState withMultiSelectable() const noexcept { return withFlag (Flags::multiSelectable); }
|
||||
|
||||
/** Sets the selected flag and returns the new state.
|
||||
|
||||
@see isSelected
|
||||
*/
|
||||
AccessibleState withSelected() const noexcept { return withFlag (Flags::selected); }
|
||||
|
||||
/** Sets the accessible offscreen flag and returns the new state.
|
||||
|
||||
@see isSelected
|
||||
*/
|
||||
AccessibleState withAccessibleOffscreen() const noexcept { return withFlag (Flags::accessibleOffscreen); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the UI element is checkable.
|
||||
|
||||
@see withCheckable
|
||||
*/
|
||||
bool isCheckable() const noexcept { return isFlagSet (Flags::checkable); }
|
||||
|
||||
/** Returns true if the UI element is checked.
|
||||
|
||||
@see withChecked
|
||||
*/
|
||||
bool isChecked() const noexcept { return isFlagSet (Flags::checked); }
|
||||
|
||||
/** Returns true if the UI element is collapsed.
|
||||
|
||||
@see withCollapsed
|
||||
*/
|
||||
bool isCollapsed() const noexcept { return isFlagSet (Flags::collapsed); }
|
||||
|
||||
/** Returns true if the UI element is expandable.
|
||||
|
||||
@see withExpandable
|
||||
*/
|
||||
bool isExpandable() const noexcept { return isFlagSet (Flags::expandable); }
|
||||
|
||||
/** Returns true if the UI element is expanded.
|
||||
|
||||
@see withExpanded
|
||||
*/
|
||||
bool isExpanded() const noexcept { return isFlagSet (Flags::expanded); }
|
||||
|
||||
/** Returns true if the UI element is focusable.
|
||||
|
||||
@see withFocusable
|
||||
*/
|
||||
bool isFocusable() const noexcept { return isFlagSet (Flags::focusable); }
|
||||
|
||||
/** Returns true if the UI element is focused.
|
||||
|
||||
@see withFocused
|
||||
*/
|
||||
bool isFocused() const noexcept { return isFlagSet (Flags::focused); }
|
||||
|
||||
/** Returns true if the UI element is ignored.
|
||||
|
||||
@see withIgnored
|
||||
*/
|
||||
bool isIgnored() const noexcept { return isFlagSet (Flags::ignored); }
|
||||
|
||||
/** Returns true if the UI element supports multiple item selection.
|
||||
|
||||
@see withMultiSelectable
|
||||
*/
|
||||
bool isMultiSelectable() const noexcept { return isFlagSet (Flags::multiSelectable); }
|
||||
|
||||
/** Returns true if the UI element is selectable.
|
||||
|
||||
@see withSelectable
|
||||
*/
|
||||
bool isSelectable() const noexcept { return isFlagSet (Flags::selectable); }
|
||||
|
||||
/** Returns true if the UI element is selected.
|
||||
|
||||
@see withSelected
|
||||
*/
|
||||
bool isSelected() const noexcept { return isFlagSet (Flags::selected); }
|
||||
|
||||
/** Returns true if the UI element is accessible offscreen.
|
||||
|
||||
@see withSelected
|
||||
*/
|
||||
bool isAccessibleOffscreen() const noexcept { return isFlagSet (Flags::accessibleOffscreen); }
|
||||
|
||||
private:
|
||||
enum Flags
|
||||
{
|
||||
checkable = (1 << 0),
|
||||
checked = (1 << 1),
|
||||
collapsed = (1 << 2),
|
||||
expandable = (1 << 3),
|
||||
expanded = (1 << 4),
|
||||
focusable = (1 << 5),
|
||||
focused = (1 << 6),
|
||||
ignored = (1 << 7),
|
||||
multiSelectable = (1 << 8),
|
||||
selectable = (1 << 9),
|
||||
selected = (1 << 10),
|
||||
accessibleOffscreen = (1 << 11)
|
||||
};
|
||||
|
||||
AccessibleState withFlag (int flag) const noexcept
|
||||
{
|
||||
auto copy = *this;
|
||||
copy.flags |= flag;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
bool isFlagSet (int flag) const noexcept
|
||||
{
|
||||
return (flags & flag) != 0;
|
||||
}
|
||||
|
||||
int flags = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
104
deps/juce/modules/juce_gui_basics/application/juce_Application.cpp
vendored
Normal file
104
deps/juce/modules/juce_gui_basics/application/juce_Application.cpp
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
JUCEApplication::JUCEApplication() {}
|
||||
JUCEApplication::~JUCEApplication() {}
|
||||
|
||||
//==============================================================================
|
||||
JUCEApplication* JUCE_CALLTYPE JUCEApplication::getInstance() noexcept
|
||||
{
|
||||
return dynamic_cast<JUCEApplication*> (JUCEApplicationBase::getInstance());
|
||||
}
|
||||
|
||||
bool JUCEApplication::moreThanOneInstanceAllowed() { return true; }
|
||||
void JUCEApplication::anotherInstanceStarted (const String&) {}
|
||||
|
||||
void JUCEApplication::suspended() {}
|
||||
void JUCEApplication::resumed() {}
|
||||
|
||||
void JUCEApplication::systemRequestedQuit() { quit(); }
|
||||
|
||||
void JUCEApplication::unhandledException (const std::exception*, const String&, int)
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* JUCEApplication::getNextCommandTarget()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void JUCEApplication::getAllCommands (Array<CommandID>& commands)
|
||||
{
|
||||
commands.add (StandardApplicationCommandIDs::quit);
|
||||
}
|
||||
|
||||
void JUCEApplication::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
|
||||
{
|
||||
if (commandID == StandardApplicationCommandIDs::quit)
|
||||
{
|
||||
result.setInfo (TRANS("Quit"),
|
||||
TRANS("Quits the application"),
|
||||
"Application", 0);
|
||||
|
||||
result.defaultKeypresses.add (KeyPress ('q', ModifierKeys::commandModifier, 0));
|
||||
}
|
||||
}
|
||||
|
||||
bool JUCEApplication::perform (const InvocationInfo& info)
|
||||
{
|
||||
if (info.commandID == StandardApplicationCommandIDs::quit)
|
||||
{
|
||||
systemRequestedQuit();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
extern void juce_initialiseMacMainMenu();
|
||||
#endif
|
||||
|
||||
bool JUCEApplication::initialiseApp()
|
||||
{
|
||||
if (JUCEApplicationBase::initialiseApp())
|
||||
{
|
||||
#if JUCE_MAC
|
||||
juce_initialiseMacMainMenu(); // (needs to get the app's name)
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
193
deps/juce/modules/juce_gui_basics/application/juce_Application.h
vendored
Normal file
193
deps/juce/modules/juce_gui_basics/application/juce_Application.h
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An instance of this class is used to specify initialisation and shutdown
|
||||
code for the application.
|
||||
|
||||
Any application that wants to run an event loop must declare a subclass of
|
||||
JUCEApplicationBase or JUCEApplication, and implement its various pure virtual
|
||||
methods.
|
||||
|
||||
It then needs to use the START_JUCE_APPLICATION macro somewhere in a CPP file
|
||||
to declare an instance of this class and generate suitable platform-specific
|
||||
boilerplate code to launch the app.
|
||||
|
||||
Note that this class is derived from JUCEApplicationBase, which contains most
|
||||
of the useful methods and functionality. This derived class is here simply as
|
||||
a convenient way to also inherit from an ApplicationCommandTarget, and to implement
|
||||
default versions of some of the pure virtual base class methods. But you can derive
|
||||
your app object directly from JUCEApplicationBase if you want to, and by doing so
|
||||
can avoid having a dependency on the juce_gui_basics module.
|
||||
|
||||
e.g. @code
|
||||
class MyJUCEApp : public JUCEApplication
|
||||
{
|
||||
public:
|
||||
MyJUCEApp() {}
|
||||
~MyJUCEApp() {}
|
||||
|
||||
void initialise (const String& commandLine) override
|
||||
{
|
||||
myMainWindow.reset (new MyApplicationWindow());
|
||||
myMainWindow->setBounds (100, 100, 400, 500);
|
||||
myMainWindow->setVisible (true);
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
myMainWindow = nullptr;
|
||||
}
|
||||
|
||||
const String getApplicationName() override
|
||||
{
|
||||
return "Super JUCE-o-matic";
|
||||
}
|
||||
|
||||
const String getApplicationVersion() override
|
||||
{
|
||||
return "1.0";
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<MyApplicationWindow> myMainWindow;
|
||||
};
|
||||
|
||||
// this generates boilerplate code to launch our app class:
|
||||
START_JUCE_APPLICATION (MyJUCEApp)
|
||||
@endcode
|
||||
|
||||
@see JUCEApplicationBase, START_JUCE_APPLICATION
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API JUCEApplication : public JUCEApplicationBase,
|
||||
public ApplicationCommandTarget
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructs a JUCE app object.
|
||||
|
||||
If subclasses implement a constructor or destructor, they shouldn't call any
|
||||
JUCE code in there - put your startup/shutdown code in initialise() and
|
||||
shutdown() instead.
|
||||
*/
|
||||
JUCEApplication();
|
||||
|
||||
/** Destructor.
|
||||
|
||||
If subclasses implement a constructor or destructor, they shouldn't call any
|
||||
JUCE code in there - put your startup/shutdown code in initialise() and
|
||||
shutdown() instead.
|
||||
*/
|
||||
~JUCEApplication() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the global instance of the application object being run. */
|
||||
static JUCEApplication* JUCE_CALLTYPE getInstance() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
#if DOXYGEN
|
||||
/** Returns the application's name. */
|
||||
virtual const String getApplicationName() = 0;
|
||||
|
||||
/** Returns the application's version number. */
|
||||
virtual const String getApplicationVersion() = 0;
|
||||
#endif
|
||||
|
||||
/** Checks whether multiple instances of the app are allowed.
|
||||
|
||||
If your application class returns true for this, more than one instance is
|
||||
permitted to run (except on OSX where the OS automatically stops you launching
|
||||
a second instance of an app without explicitly starting it from the command-line).
|
||||
|
||||
If it's false, the second instance won't start, but you will still get a
|
||||
callback to anotherInstanceStarted() to tell you about this - which
|
||||
gives you a chance to react to what the user was trying to do.
|
||||
*/
|
||||
bool moreThanOneInstanceAllowed() override;
|
||||
|
||||
/** Indicates that the user has tried to start up another instance of the app.
|
||||
This will get called even if moreThanOneInstanceAllowed() is false.
|
||||
*/
|
||||
void anotherInstanceStarted (const String& commandLine) override;
|
||||
|
||||
/** Called when the operating system is trying to close the application.
|
||||
|
||||
The default implementation of this method is to call quit(), but it may
|
||||
be overloaded to ignore the request or do some other special behaviour
|
||||
instead. For example, you might want to offer the user the chance to save
|
||||
their changes before quitting, and give them the chance to cancel.
|
||||
|
||||
If you want to send a quit signal to your app, this is the correct method
|
||||
to call, because it means that requests that come from the system get handled
|
||||
in the same way as those from your own application code. So e.g. you'd
|
||||
call this method from a "quit" item on a menu bar.
|
||||
*/
|
||||
void systemRequestedQuit() override;
|
||||
|
||||
/** This method is called when the application is being put into background mode
|
||||
by the operating system.
|
||||
*/
|
||||
void suspended() override;
|
||||
|
||||
/** This method is called when the application is being woken from background mode
|
||||
by the operating system.
|
||||
*/
|
||||
void resumed() override;
|
||||
|
||||
/** If any unhandled exceptions make it through to the message dispatch loop, this
|
||||
callback will be triggered, in case you want to log them or do some other
|
||||
type of error-handling.
|
||||
|
||||
If the type of exception is derived from the std::exception class, the pointer
|
||||
passed-in will be valid. If the exception is of unknown type, this pointer
|
||||
will be null.
|
||||
*/
|
||||
void unhandledException (const std::exception* e,
|
||||
const String& sourceFilename,
|
||||
int lineNumber) override;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
ApplicationCommandTarget* getNextCommandTarget() override;
|
||||
/** @internal */
|
||||
void getCommandInfo (CommandID, ApplicationCommandInfo&) override;
|
||||
/** @internal */
|
||||
void getAllCommands (Array<CommandID>&) override;
|
||||
/** @internal */
|
||||
bool perform (const InvocationInfo&) override;
|
||||
|
||||
private:
|
||||
bool initialiseApp() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (JUCEApplication)
|
||||
};
|
||||
|
||||
} // namespace juce
|
51
deps/juce/modules/juce_gui_basics/buttons/juce_ArrowButton.cpp
vendored
Normal file
51
deps/juce/modules/juce_gui_basics/buttons/juce_ArrowButton.cpp
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
ArrowButton::ArrowButton (const String& name, float arrowDirectionInRadians, Colour arrowColour)
|
||||
: Button (name), colour (arrowColour)
|
||||
{
|
||||
path.addTriangle (0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f);
|
||||
path.applyTransform (AffineTransform::rotation (MathConstants<float>::twoPi * arrowDirectionInRadians, 0.5f, 0.5f));
|
||||
}
|
||||
|
||||
ArrowButton::~ArrowButton() {}
|
||||
|
||||
void ArrowButton::paintButton (Graphics& g, bool /*shouldDrawButtonAsHighlighted*/, bool shouldDrawButtonAsDown)
|
||||
{
|
||||
Path p (path);
|
||||
|
||||
const float offset = shouldDrawButtonAsDown ? 1.0f : 0.0f;
|
||||
p.applyTransform (path.getTransformToScaleToFit (offset, offset, (float) getWidth() - 3.0f, (float) getHeight() - 3.0f, false));
|
||||
|
||||
DropShadow (Colours::black.withAlpha (0.3f), shouldDrawButtonAsDown ? 2 : 4, Point<int>()).drawForPath (g, p);
|
||||
|
||||
g.setColour (colour);
|
||||
g.fillPath (p);
|
||||
}
|
||||
|
||||
} // namespace juce
|
65
deps/juce/modules/juce_gui_basics/buttons/juce_ArrowButton.h
vendored
Normal file
65
deps/juce/modules/juce_gui_basics/buttons/juce_ArrowButton.h
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A button with an arrow in it.
|
||||
|
||||
@see Button
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ArrowButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an ArrowButton.
|
||||
|
||||
@param buttonName the name to give the button
|
||||
@param arrowDirection the direction the arrow should point in, where 0.0 is
|
||||
pointing right, 0.25 is down, 0.5 is left, 0.75 is up
|
||||
@param arrowColour the colour to use for the arrow
|
||||
*/
|
||||
ArrowButton (const String& buttonName,
|
||||
float arrowDirection,
|
||||
Colour arrowColour);
|
||||
|
||||
/** Destructor. */
|
||||
~ArrowButton() override;
|
||||
|
||||
/** @internal */
|
||||
void paintButton (Graphics&, bool, bool) override;
|
||||
|
||||
private:
|
||||
Colour colour;
|
||||
Path path;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ArrowButton)
|
||||
};
|
||||
|
||||
} // namespace juce
|
792
deps/juce/modules/juce_gui_basics/buttons/juce_Button.cpp
vendored
Normal file
792
deps/juce/modules/juce_gui_basics/buttons/juce_Button.cpp
vendored
Normal file
@ -0,0 +1,792 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 Button::CallbackHelper : public Timer,
|
||||
public ApplicationCommandManagerListener,
|
||||
public Value::Listener,
|
||||
public KeyListener
|
||||
{
|
||||
CallbackHelper (Button& b) : button (b) {}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
button.repeatTimerCallback();
|
||||
}
|
||||
|
||||
bool keyStateChanged (bool, Component*) override
|
||||
{
|
||||
return button.keyStateChangedCallback();
|
||||
}
|
||||
|
||||
void valueChanged (Value& value) override
|
||||
{
|
||||
if (value.refersToSameSourceAs (button.isOn))
|
||||
button.setToggleState (button.isOn.getValue(), dontSendNotification, sendNotification);
|
||||
}
|
||||
|
||||
bool keyPressed (const KeyPress&, Component*) override
|
||||
{
|
||||
// returning true will avoid forwarding events for keys that we're using as shortcuts
|
||||
return button.isShortcutPressed();
|
||||
}
|
||||
|
||||
void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info) override
|
||||
{
|
||||
if (info.commandID == button.commandID
|
||||
&& (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) == 0)
|
||||
button.flashButtonState();
|
||||
}
|
||||
|
||||
void applicationCommandListChanged() override
|
||||
{
|
||||
button.applicationCommandListChangeCallback();
|
||||
}
|
||||
|
||||
Button& button;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHelper)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
Button::Button (const String& name) : Component (name), text (name)
|
||||
{
|
||||
callbackHelper.reset (new CallbackHelper (*this));
|
||||
|
||||
setWantsKeyboardFocus (true);
|
||||
isOn.addListener (callbackHelper.get());
|
||||
}
|
||||
|
||||
Button::~Button()
|
||||
{
|
||||
clearShortcuts();
|
||||
|
||||
if (commandManagerToUse != nullptr)
|
||||
commandManagerToUse->removeListener (callbackHelper.get());
|
||||
|
||||
isOn.removeListener (callbackHelper.get());
|
||||
callbackHelper.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::setButtonText (const String& newText)
|
||||
{
|
||||
if (text != newText)
|
||||
{
|
||||
text = newText;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::setTooltip (const String& newTooltip)
|
||||
{
|
||||
SettableTooltipClient::setTooltip (newTooltip);
|
||||
generateTooltip = false;
|
||||
}
|
||||
|
||||
void Button::updateAutomaticTooltip (const ApplicationCommandInfo& info)
|
||||
{
|
||||
if (generateTooltip && commandManagerToUse != nullptr)
|
||||
{
|
||||
auto tt = info.description.isNotEmpty() ? info.description
|
||||
: info.shortName;
|
||||
|
||||
for (auto& kp : commandManagerToUse->getKeyMappings()->getKeyPressesAssignedToCommand (commandID))
|
||||
{
|
||||
auto key = kp.getTextDescription();
|
||||
|
||||
tt << " [";
|
||||
|
||||
if (key.length() == 1)
|
||||
tt << TRANS("shortcut") << ": '" << key << "']";
|
||||
else
|
||||
tt << key << ']';
|
||||
}
|
||||
|
||||
SettableTooltipClient::setTooltip (tt);
|
||||
}
|
||||
}
|
||||
|
||||
void Button::setConnectedEdges (int newFlags)
|
||||
{
|
||||
if (connectedEdgeFlags != newFlags)
|
||||
{
|
||||
connectedEdgeFlags = newFlags;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::setToggleState (bool shouldBeOn, NotificationType notification)
|
||||
{
|
||||
setToggleState (shouldBeOn, notification, notification);
|
||||
}
|
||||
|
||||
void Button::setToggleState (bool shouldBeOn, NotificationType clickNotification, NotificationType stateNotification)
|
||||
{
|
||||
if (shouldBeOn != lastToggleState)
|
||||
{
|
||||
WeakReference<Component> deletionWatcher (this);
|
||||
|
||||
if (shouldBeOn)
|
||||
{
|
||||
turnOffOtherButtonsInGroup (clickNotification, stateNotification);
|
||||
|
||||
if (deletionWatcher == nullptr)
|
||||
return;
|
||||
}
|
||||
|
||||
// This test is done so that if the value is void rather than explicitly set to
|
||||
// false, the value won't be changed unless the required value is true.
|
||||
if (getToggleState() != shouldBeOn)
|
||||
{
|
||||
isOn = shouldBeOn;
|
||||
|
||||
if (deletionWatcher == nullptr)
|
||||
return;
|
||||
}
|
||||
|
||||
lastToggleState = shouldBeOn;
|
||||
repaint();
|
||||
|
||||
if (clickNotification != dontSendNotification)
|
||||
{
|
||||
// async callbacks aren't possible here
|
||||
jassert (clickNotification != sendNotificationAsync);
|
||||
|
||||
sendClickMessage (ModifierKeys::currentModifiers);
|
||||
|
||||
if (deletionWatcher == nullptr)
|
||||
return;
|
||||
}
|
||||
|
||||
if (stateNotification != dontSendNotification)
|
||||
sendStateMessage();
|
||||
else
|
||||
buttonStateChanged();
|
||||
|
||||
if (auto* handler = getAccessibilityHandler())
|
||||
handler->notifyAccessibilityEvent (AccessibilityEvent::valueChanged);
|
||||
}
|
||||
}
|
||||
|
||||
void Button::setToggleState (bool shouldBeOn, bool sendChange)
|
||||
{
|
||||
setToggleState (shouldBeOn, sendChange ? sendNotification : dontSendNotification);
|
||||
}
|
||||
|
||||
void Button::setClickingTogglesState (bool shouldToggle) noexcept
|
||||
{
|
||||
clickTogglesState = shouldToggle;
|
||||
|
||||
// if you've got clickTogglesState turned on, you shouldn't also connect the button
|
||||
// up to be a command invoker. Instead, your command handler must flip the state of whatever
|
||||
// it is that this button represents, and the button will update its state to reflect this
|
||||
// in the applicationCommandListChanged() method.
|
||||
jassert (commandManagerToUse == nullptr || ! clickTogglesState);
|
||||
}
|
||||
|
||||
bool Button::getClickingTogglesState() const noexcept
|
||||
{
|
||||
return clickTogglesState;
|
||||
}
|
||||
|
||||
void Button::setRadioGroupId (int newGroupId, NotificationType notification)
|
||||
{
|
||||
if (radioGroupId != newGroupId)
|
||||
{
|
||||
radioGroupId = newGroupId;
|
||||
|
||||
if (lastToggleState)
|
||||
turnOffOtherButtonsInGroup (notification, notification);
|
||||
|
||||
invalidateAccessibilityHandler();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::turnOffOtherButtonsInGroup (NotificationType clickNotification, NotificationType stateNotification)
|
||||
{
|
||||
if (auto* p = getParentComponent())
|
||||
{
|
||||
if (radioGroupId != 0)
|
||||
{
|
||||
WeakReference<Component> deletionWatcher (this);
|
||||
|
||||
for (auto* c : p->getChildren())
|
||||
{
|
||||
if (c != this)
|
||||
{
|
||||
if (auto b = dynamic_cast<Button*> (c))
|
||||
{
|
||||
if (b->getRadioGroupId() == radioGroupId)
|
||||
{
|
||||
b->setToggleState (false, clickNotification, stateNotification);
|
||||
|
||||
if (deletionWatcher == nullptr)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::enablementChanged()
|
||||
{
|
||||
updateState();
|
||||
repaint();
|
||||
}
|
||||
|
||||
Button::ButtonState Button::updateState()
|
||||
{
|
||||
return updateState (isMouseOver (true), isMouseButtonDown());
|
||||
}
|
||||
|
||||
Button::ButtonState Button::updateState (bool over, bool down)
|
||||
{
|
||||
ButtonState newState = buttonNormal;
|
||||
|
||||
if (isEnabled() && isVisible() && ! isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
if ((down && (over || (triggerOnMouseDown && buttonState == buttonDown))) || isKeyDown)
|
||||
newState = buttonDown;
|
||||
else if (over)
|
||||
newState = buttonOver;
|
||||
}
|
||||
|
||||
setState (newState);
|
||||
return newState;
|
||||
}
|
||||
|
||||
void Button::setState (ButtonState newState)
|
||||
{
|
||||
if (buttonState != newState)
|
||||
{
|
||||
buttonState = newState;
|
||||
repaint();
|
||||
|
||||
if (buttonState == buttonDown)
|
||||
{
|
||||
buttonPressTime = Time::getApproximateMillisecondCounter();
|
||||
lastRepeatTime = 0;
|
||||
}
|
||||
|
||||
sendStateMessage();
|
||||
}
|
||||
}
|
||||
|
||||
bool Button::isDown() const noexcept { return buttonState == buttonDown; }
|
||||
bool Button::isOver() const noexcept { return buttonState != buttonNormal; }
|
||||
|
||||
void Button::buttonStateChanged() {}
|
||||
|
||||
uint32 Button::getMillisecondsSinceButtonDown() const noexcept
|
||||
{
|
||||
auto now = Time::getApproximateMillisecondCounter();
|
||||
return now > buttonPressTime ? now - buttonPressTime : 0;
|
||||
}
|
||||
|
||||
void Button::setTriggeredOnMouseDown (bool isTriggeredOnMouseDown) noexcept
|
||||
{
|
||||
triggerOnMouseDown = isTriggeredOnMouseDown;
|
||||
}
|
||||
|
||||
bool Button::getTriggeredOnMouseDown() const noexcept
|
||||
{
|
||||
return triggerOnMouseDown;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::clicked()
|
||||
{
|
||||
}
|
||||
|
||||
void Button::clicked (const ModifierKeys&)
|
||||
{
|
||||
clicked();
|
||||
}
|
||||
|
||||
enum { clickMessageId = 0x2f3f4f99 };
|
||||
|
||||
void Button::triggerClick()
|
||||
{
|
||||
postCommandMessage (clickMessageId);
|
||||
}
|
||||
|
||||
void Button::internalClickCallback (const ModifierKeys& modifiers)
|
||||
{
|
||||
if (clickTogglesState)
|
||||
{
|
||||
const bool shouldBeOn = (radioGroupId != 0 || ! lastToggleState);
|
||||
|
||||
if (shouldBeOn != getToggleState())
|
||||
{
|
||||
setToggleState (shouldBeOn, sendNotification);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sendClickMessage (modifiers);
|
||||
}
|
||||
|
||||
void Button::flashButtonState()
|
||||
{
|
||||
if (isEnabled())
|
||||
{
|
||||
needsToRelease = true;
|
||||
setState (buttonDown);
|
||||
callbackHelper->startTimer (100);
|
||||
}
|
||||
}
|
||||
|
||||
void Button::handleCommandMessage (int commandId)
|
||||
{
|
||||
if (commandId == clickMessageId)
|
||||
{
|
||||
if (isEnabled())
|
||||
{
|
||||
flashButtonState();
|
||||
internalClickCallback (ModifierKeys::currentModifiers);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Component::handleCommandMessage (commandId);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::addListener (Listener* l) { buttonListeners.add (l); }
|
||||
void Button::removeListener (Listener* l) { buttonListeners.remove (l); }
|
||||
|
||||
void Button::sendClickMessage (const ModifierKeys& modifiers)
|
||||
{
|
||||
Component::BailOutChecker checker (this);
|
||||
|
||||
if (commandManagerToUse != nullptr && commandID != 0)
|
||||
{
|
||||
ApplicationCommandTarget::InvocationInfo info (commandID);
|
||||
info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromButton;
|
||||
info.originatingComponent = this;
|
||||
|
||||
commandManagerToUse->invoke (info, true);
|
||||
}
|
||||
|
||||
clicked (modifiers);
|
||||
|
||||
if (checker.shouldBailOut())
|
||||
return;
|
||||
|
||||
buttonListeners.callChecked (checker, [this] (Listener& l) { l.buttonClicked (this); });
|
||||
|
||||
if (checker.shouldBailOut())
|
||||
return;
|
||||
|
||||
if (onClick != nullptr)
|
||||
onClick();
|
||||
}
|
||||
|
||||
void Button::sendStateMessage()
|
||||
{
|
||||
Component::BailOutChecker checker (this);
|
||||
|
||||
buttonStateChanged();
|
||||
|
||||
if (checker.shouldBailOut())
|
||||
return;
|
||||
|
||||
buttonListeners.callChecked (checker, [this] (Listener& l) { l.buttonStateChanged (this); });
|
||||
|
||||
if (checker.shouldBailOut())
|
||||
return;
|
||||
|
||||
if (onStateChange != nullptr)
|
||||
onStateChange();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::paint (Graphics& g)
|
||||
{
|
||||
if (needsToRelease && isEnabled())
|
||||
{
|
||||
needsToRelease = false;
|
||||
needsRepainting = true;
|
||||
}
|
||||
|
||||
paintButton (g, isOver(), isDown());
|
||||
lastStatePainted = buttonState;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::mouseEnter (const MouseEvent&) { updateState (true, false); }
|
||||
void Button::mouseExit (const MouseEvent&) { updateState (false, false); }
|
||||
|
||||
bool Button::isInDragToScrollViewport() const noexcept
|
||||
{
|
||||
if (auto* vp = findParentComponentOfClass<Viewport>())
|
||||
return vp->isScrollOnDragEnabled() && (vp->canScrollVertically() || vp->canScrollHorizontally());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Button::mouseDown (const MouseEvent& e)
|
||||
{
|
||||
isInDraggableViewport = isInDragToScrollViewport();
|
||||
isDraggingToScroll = false;
|
||||
updateState (true, true);
|
||||
|
||||
if (isDown())
|
||||
{
|
||||
if (autoRepeatDelay >= 0)
|
||||
callbackHelper->startTimer (autoRepeatDelay);
|
||||
|
||||
if (triggerOnMouseDown)
|
||||
internalClickCallback (e.mods);
|
||||
}
|
||||
}
|
||||
|
||||
void Button::mouseUp (const MouseEvent& e)
|
||||
{
|
||||
const auto wasDown = isDown();
|
||||
const auto wasOver = isOver();
|
||||
updateState (isMouseSourceOver (e), false);
|
||||
|
||||
if (wasDown && wasOver && ! triggerOnMouseDown && ! isDraggingToScroll)
|
||||
{
|
||||
if (lastStatePainted != buttonDown)
|
||||
flashButtonState();
|
||||
|
||||
WeakReference<Component> deletionWatcher (this);
|
||||
|
||||
internalClickCallback (e.mods);
|
||||
|
||||
if (deletionWatcher != nullptr)
|
||||
updateState (isMouseSourceOver (e), false);
|
||||
}
|
||||
}
|
||||
|
||||
void Button::mouseDrag (const MouseEvent& e)
|
||||
{
|
||||
auto oldState = buttonState;
|
||||
updateState (isMouseSourceOver (e), true);
|
||||
|
||||
if (autoRepeatDelay >= 0 && buttonState != oldState && isDown())
|
||||
callbackHelper->startTimer (autoRepeatSpeed);
|
||||
|
||||
if (isInDraggableViewport && ! isDraggingToScroll)
|
||||
if (auto* vp = findParentComponentOfClass<Viewport>())
|
||||
isDraggingToScroll = vp->isCurrentlyScrollingOnDrag();
|
||||
}
|
||||
|
||||
bool Button::isMouseSourceOver (const MouseEvent& e)
|
||||
{
|
||||
if (e.source.isTouch() || e.source.isPen())
|
||||
return getLocalBounds().toFloat().contains (e.position);
|
||||
|
||||
return isMouseOver();
|
||||
}
|
||||
|
||||
void Button::focusGained (FocusChangeType)
|
||||
{
|
||||
updateState();
|
||||
repaint();
|
||||
}
|
||||
|
||||
void Button::focusLost (FocusChangeType)
|
||||
{
|
||||
updateState();
|
||||
repaint();
|
||||
}
|
||||
|
||||
void Button::visibilityChanged()
|
||||
{
|
||||
needsToRelease = false;
|
||||
updateState();
|
||||
}
|
||||
|
||||
void Button::parentHierarchyChanged()
|
||||
{
|
||||
auto* newKeySource = shortcuts.isEmpty() ? nullptr : getTopLevelComponent();
|
||||
|
||||
if (newKeySource != keySource.get())
|
||||
{
|
||||
if (keySource != nullptr)
|
||||
keySource->removeKeyListener (callbackHelper.get());
|
||||
|
||||
keySource = newKeySource;
|
||||
|
||||
if (keySource != nullptr)
|
||||
keySource->addKeyListener (callbackHelper.get());
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::setCommandToTrigger (ApplicationCommandManager* newCommandManager,
|
||||
CommandID newCommandID, bool generateTip)
|
||||
{
|
||||
commandID = newCommandID;
|
||||
generateTooltip = generateTip;
|
||||
|
||||
if (commandManagerToUse != newCommandManager)
|
||||
{
|
||||
if (commandManagerToUse != nullptr)
|
||||
commandManagerToUse->removeListener (callbackHelper.get());
|
||||
|
||||
commandManagerToUse = newCommandManager;
|
||||
|
||||
if (commandManagerToUse != nullptr)
|
||||
commandManagerToUse->addListener (callbackHelper.get());
|
||||
|
||||
// if you've got clickTogglesState turned on, you shouldn't also connect the button
|
||||
// up to be a command invoker. Instead, your command handler must flip the state of whatever
|
||||
// it is that this button represents, and the button will update its state to reflect this
|
||||
// in the applicationCommandListChanged() method.
|
||||
jassert (commandManagerToUse == nullptr || ! clickTogglesState);
|
||||
}
|
||||
|
||||
if (commandManagerToUse != nullptr)
|
||||
applicationCommandListChangeCallback();
|
||||
else
|
||||
setEnabled (true);
|
||||
}
|
||||
|
||||
void Button::applicationCommandListChangeCallback()
|
||||
{
|
||||
if (commandManagerToUse != nullptr)
|
||||
{
|
||||
ApplicationCommandInfo info (0);
|
||||
|
||||
if (commandManagerToUse->getTargetForCommand (commandID, info) != nullptr)
|
||||
{
|
||||
updateAutomaticTooltip (info);
|
||||
setEnabled ((info.flags & ApplicationCommandInfo::isDisabled) == 0);
|
||||
setToggleState ((info.flags & ApplicationCommandInfo::isTicked) != 0, dontSendNotification);
|
||||
}
|
||||
else
|
||||
{
|
||||
setEnabled (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::addShortcut (const KeyPress& key)
|
||||
{
|
||||
if (key.isValid())
|
||||
{
|
||||
jassert (! isRegisteredForShortcut (key)); // already registered!
|
||||
|
||||
shortcuts.add (key);
|
||||
parentHierarchyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::clearShortcuts()
|
||||
{
|
||||
shortcuts.clear();
|
||||
parentHierarchyChanged();
|
||||
}
|
||||
|
||||
bool Button::isShortcutPressed() const
|
||||
{
|
||||
if (isShowing() && ! isCurrentlyBlockedByAnotherModalComponent())
|
||||
for (auto& s : shortcuts)
|
||||
if (s.isCurrentlyDown())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Button::isRegisteredForShortcut (const KeyPress& key) const
|
||||
{
|
||||
for (auto& s : shortcuts)
|
||||
if (key == s)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Button::keyStateChangedCallback()
|
||||
{
|
||||
if (! isEnabled())
|
||||
return false;
|
||||
|
||||
const bool wasDown = isKeyDown;
|
||||
isKeyDown = isShortcutPressed();
|
||||
|
||||
if (autoRepeatDelay >= 0 && (isKeyDown && ! wasDown))
|
||||
callbackHelper->startTimer (autoRepeatDelay);
|
||||
|
||||
updateState();
|
||||
|
||||
if (isEnabled() && wasDown && ! isKeyDown)
|
||||
{
|
||||
internalClickCallback (ModifierKeys::currentModifiers);
|
||||
|
||||
// (return immediately - this button may now have been deleted)
|
||||
return true;
|
||||
}
|
||||
|
||||
return wasDown || isKeyDown;
|
||||
}
|
||||
|
||||
bool Button::keyPressed (const KeyPress& key)
|
||||
{
|
||||
if (isEnabled() && key.isKeyCode (KeyPress::returnKey))
|
||||
{
|
||||
triggerClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::setRepeatSpeed (int initialDelayMillisecs,
|
||||
int repeatMillisecs,
|
||||
int minimumDelayInMillisecs) noexcept
|
||||
{
|
||||
autoRepeatDelay = initialDelayMillisecs;
|
||||
autoRepeatSpeed = repeatMillisecs;
|
||||
autoRepeatMinimumDelay = jmin (autoRepeatSpeed, minimumDelayInMillisecs);
|
||||
}
|
||||
|
||||
void Button::repeatTimerCallback()
|
||||
{
|
||||
if (needsRepainting)
|
||||
{
|
||||
callbackHelper->stopTimer();
|
||||
updateState();
|
||||
needsRepainting = false;
|
||||
}
|
||||
else if (autoRepeatSpeed > 0 && (isKeyDown || (updateState() == buttonDown)))
|
||||
{
|
||||
auto repeatSpeed = autoRepeatSpeed;
|
||||
|
||||
if (autoRepeatMinimumDelay >= 0)
|
||||
{
|
||||
auto timeHeldDown = jmin (1.0, getMillisecondsSinceButtonDown() / 4000.0);
|
||||
timeHeldDown *= timeHeldDown;
|
||||
|
||||
repeatSpeed = repeatSpeed + (int) (timeHeldDown * (autoRepeatMinimumDelay - repeatSpeed));
|
||||
}
|
||||
|
||||
repeatSpeed = jmax (1, repeatSpeed);
|
||||
|
||||
auto now = Time::getMillisecondCounter();
|
||||
|
||||
// if we've been blocked from repeating often enough, speed up the repeat timer to compensate..
|
||||
if (lastRepeatTime != 0 && (int) (now - lastRepeatTime) > repeatSpeed * 2)
|
||||
repeatSpeed = jmax (1, repeatSpeed / 2);
|
||||
|
||||
lastRepeatTime = now;
|
||||
callbackHelper->startTimer (repeatSpeed);
|
||||
|
||||
internalClickCallback (ModifierKeys::currentModifiers);
|
||||
}
|
||||
else if (! needsToRelease)
|
||||
{
|
||||
callbackHelper->stopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ButtonAccessibilityHandler : public AccessibilityHandler
|
||||
{
|
||||
public:
|
||||
explicit ButtonAccessibilityHandler (Button& buttonToWrap, AccessibilityRole roleIn)
|
||||
: AccessibilityHandler (buttonToWrap,
|
||||
isRadioButton (buttonToWrap) ? AccessibilityRole::radioButton : buttonToWrap.getClickingTogglesState() ? AccessibilityRole::toggleButton : roleIn,
|
||||
getAccessibilityActions (buttonToWrap, roleIn)),
|
||||
button (buttonToWrap)
|
||||
{
|
||||
}
|
||||
|
||||
AccessibleState getCurrentState() const override
|
||||
{
|
||||
auto state = AccessibilityHandler::getCurrentState();
|
||||
|
||||
if (isToggleButton (getRole()) || isRadioButton (button) || button.getClickingTogglesState())
|
||||
{
|
||||
state = state.withCheckable();
|
||||
|
||||
if (button.getToggleState())
|
||||
state = state.withChecked();
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
String getTitle() const override
|
||||
{
|
||||
auto title = AccessibilityHandler::getTitle();
|
||||
|
||||
if (title.isEmpty())
|
||||
return button.getButtonText();
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
String getHelp() const override { return button.getTooltip(); }
|
||||
|
||||
private:
|
||||
static bool isToggleButton (AccessibilityRole role) noexcept
|
||||
{
|
||||
return role == AccessibilityRole::toggleButton;
|
||||
}
|
||||
|
||||
static bool isRadioButton (const Button& button) noexcept
|
||||
{
|
||||
return button.getRadioGroupId() != 0;
|
||||
}
|
||||
|
||||
static AccessibilityActions getAccessibilityActions (Button& button, AccessibilityRole role)
|
||||
{
|
||||
auto actions = AccessibilityActions().addAction (AccessibilityActionType::press,
|
||||
[&button] { button.triggerClick(); });
|
||||
|
||||
if (isToggleButton (role) || button.getClickingTogglesState())
|
||||
actions = actions.addAction (AccessibilityActionType::toggle,
|
||||
[&button] { button.setToggleState (! button.getToggleState(), sendNotification); });
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
Button& button;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonAccessibilityHandler)
|
||||
};
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> Button::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<ButtonAccessibilityHandler> (*this, AccessibilityRole::button);
|
||||
}
|
||||
|
||||
} // namespace juce
|
528
deps/juce/modules/juce_gui_basics/buttons/juce_Button.h
vendored
Normal file
528
deps/juce/modules/juce_gui_basics/buttons/juce_Button.h
vendored
Normal file
@ -0,0 +1,528 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for buttons.
|
||||
|
||||
This contains all the logic for button behaviours such as enabling/disabling,
|
||||
responding to shortcut keystrokes, auto-repeating when held down, toggle-buttons
|
||||
and radio groups, etc.
|
||||
|
||||
@see TextButton, DrawableButton, ToggleButton
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API Button : public Component,
|
||||
public SettableTooltipClient
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates a button.
|
||||
|
||||
@param buttonName the text to put in the button (the component's name is also
|
||||
initially set to this string, but these can be changed later
|
||||
using the setName() and setButtonText() methods)
|
||||
*/
|
||||
explicit Button (const String& buttonName);
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
~Button() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the button's text.
|
||||
@see getButtonText
|
||||
*/
|
||||
void setButtonText (const String& newText);
|
||||
|
||||
/** Returns the text displayed in the button.
|
||||
@see setButtonText
|
||||
*/
|
||||
const String& getButtonText() const { return text; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the button is currently being held down.
|
||||
@see isOver
|
||||
*/
|
||||
bool isDown() const noexcept;
|
||||
|
||||
/** Returns true if the mouse is currently over the button.
|
||||
This will be also be true if the button is being held down.
|
||||
@see isDown
|
||||
*/
|
||||
bool isOver() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** A button has an on/off state associated with it, and this changes that.
|
||||
|
||||
By default buttons are 'off' and for simple buttons that you click to perform
|
||||
an action you won't change this. Toggle buttons, however will want to
|
||||
change their state when turned on or off.
|
||||
|
||||
@param shouldBeOn whether to set the button's toggle state to be on or
|
||||
off. If it's a member of a button group, this will
|
||||
always try to turn it on, and to turn off any other
|
||||
buttons in the group
|
||||
@param notification determines the behaviour if the value changes - this
|
||||
can invoke a synchronous call to clicked(), but
|
||||
sendNotificationAsync is not supported
|
||||
@see getToggleState, setRadioGroupId
|
||||
*/
|
||||
void setToggleState (bool shouldBeOn, NotificationType notification);
|
||||
|
||||
/** Returns true if the button is 'on'.
|
||||
|
||||
By default buttons are 'off' and for simple buttons that you click to perform
|
||||
an action you won't change this. Toggle buttons, however will want to
|
||||
change their state when turned on or off.
|
||||
|
||||
@see setToggleState
|
||||
*/
|
||||
bool getToggleState() const noexcept { return isOn.getValue(); }
|
||||
|
||||
/** Returns the Value object that represents the button's toggle state.
|
||||
|
||||
You can use this Value object to connect the button's state to external values or setters,
|
||||
either by taking a copy of the Value, or by using Value::referTo() to make it point to
|
||||
your own Value object.
|
||||
|
||||
@see getToggleState, Value
|
||||
*/
|
||||
Value& getToggleStateValue() noexcept { return isOn; }
|
||||
|
||||
/** This tells the button to automatically flip the toggle state when
|
||||
the button is clicked.
|
||||
|
||||
If set to true, then before the clicked() callback occurs, the toggle-state
|
||||
of the button is flipped.
|
||||
*/
|
||||
void setClickingTogglesState (bool shouldAutoToggleOnClick) noexcept;
|
||||
|
||||
/** Returns true if this button is set to be an automatic toggle-button.
|
||||
This returns the last value that was passed to setClickingTogglesState().
|
||||
*/
|
||||
bool getClickingTogglesState() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Enables the button to act as a member of a mutually-exclusive group
|
||||
of 'radio buttons'.
|
||||
|
||||
If the group ID is set to a non-zero number, then this button will
|
||||
act as part of a group of buttons with the same ID, only one of
|
||||
which can be 'on' at the same time. Note that when it's part of
|
||||
a group, clicking a toggle-button that's 'on' won't turn it off.
|
||||
|
||||
To find other buttons with the same ID, this button will search through
|
||||
its sibling components for ToggleButtons, so all the buttons for a
|
||||
particular group must be placed inside the same parent component.
|
||||
|
||||
Set the group ID back to zero if you want it to act as a normal toggle
|
||||
button again.
|
||||
|
||||
The notification argument lets you specify how other buttons should react
|
||||
to being turned on or off in response to this call.
|
||||
|
||||
@see getRadioGroupId
|
||||
*/
|
||||
void setRadioGroupId (int newGroupId, NotificationType notification = sendNotification);
|
||||
|
||||
/** Returns the ID of the group to which this button belongs.
|
||||
(See setRadioGroupId() for an explanation of this).
|
||||
*/
|
||||
int getRadioGroupId() const noexcept { return radioGroupId; }
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Used to receive callbacks when a button is clicked.
|
||||
|
||||
@see Button::addListener, Button::removeListener
|
||||
*/
|
||||
class JUCE_API Listener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Called when the button is clicked. */
|
||||
virtual void buttonClicked (Button*) = 0;
|
||||
|
||||
/** Called when the button's state changes. */
|
||||
virtual void buttonStateChanged (Button*) {}
|
||||
};
|
||||
|
||||
/** Registers a listener to receive events when this button's state changes.
|
||||
If the listener is already registered, this will not register it again.
|
||||
@see removeListener
|
||||
*/
|
||||
void addListener (Listener* newListener);
|
||||
|
||||
/** Removes a previously-registered button listener
|
||||
@see addListener
|
||||
*/
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** You can assign a lambda to this callback object to have it called when the button is clicked. */
|
||||
std::function<void()> onClick;
|
||||
|
||||
/** You can assign a lambda to this callback object to have it called when the button's state changes. */
|
||||
std::function<void()> onStateChange;
|
||||
|
||||
//==============================================================================
|
||||
/** Causes the button to act as if it's been clicked.
|
||||
|
||||
This will asynchronously make the button draw itself going down and up, and
|
||||
will then call back the clicked() method as if mouse was clicked on it.
|
||||
|
||||
@see clicked
|
||||
*/
|
||||
virtual void triggerClick();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a command ID for this button to automatically invoke when it's clicked.
|
||||
|
||||
When the button is pressed, it will use the given manager to trigger the
|
||||
command ID.
|
||||
|
||||
Obviously be careful that the ApplicationCommandManager doesn't get deleted
|
||||
before this button is. To disable the command triggering, call this method and
|
||||
pass nullptr as the command manager.
|
||||
|
||||
If generateTooltip is true, then the button's tooltip will be automatically
|
||||
generated based on the name of this command and its current shortcut key.
|
||||
|
||||
@see addShortcut, getCommandID
|
||||
*/
|
||||
void setCommandToTrigger (ApplicationCommandManager* commandManagerToUse,
|
||||
CommandID commandID,
|
||||
bool generateTooltip);
|
||||
|
||||
/** Returns the command ID that was set by setCommandToTrigger(). */
|
||||
CommandID getCommandID() const noexcept { return commandID; }
|
||||
|
||||
//==============================================================================
|
||||
/** Assigns a shortcut key to trigger the button.
|
||||
|
||||
The button registers itself with its top-level parent component for keypresses.
|
||||
|
||||
Note that a different way of linking buttons to keypresses is by using the
|
||||
setCommandToTrigger() method to invoke a command.
|
||||
|
||||
@see clearShortcuts
|
||||
*/
|
||||
void addShortcut (const KeyPress&);
|
||||
|
||||
/** Removes all key shortcuts that had been set for this button.
|
||||
@see addShortcut
|
||||
*/
|
||||
void clearShortcuts();
|
||||
|
||||
/** Returns true if the given keypress is a shortcut for this button.
|
||||
@see addShortcut
|
||||
*/
|
||||
bool isRegisteredForShortcut (const KeyPress&) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets an auto-repeat speed for the button when it is held down.
|
||||
|
||||
(Auto-repeat is disabled by default).
|
||||
|
||||
@param initialDelayInMillisecs how long to wait after the mouse is pressed before
|
||||
triggering the next click. If this is zero, auto-repeat
|
||||
is disabled
|
||||
@param repeatDelayInMillisecs the frequently subsequent repeated clicks should be
|
||||
triggered
|
||||
@param minimumDelayInMillisecs if this is greater than 0, the auto-repeat speed will
|
||||
get faster, the longer the button is held down, up to the
|
||||
minimum interval specified here
|
||||
*/
|
||||
void setRepeatSpeed (int initialDelayInMillisecs,
|
||||
int repeatDelayInMillisecs,
|
||||
int minimumDelayInMillisecs = -1) noexcept;
|
||||
|
||||
/** Sets whether the button click should happen when the mouse is pressed or released.
|
||||
|
||||
By default the button is only considered to have been clicked when the mouse is
|
||||
released, but setting this to true will make it call the clicked() method as soon
|
||||
as the button is pressed.
|
||||
|
||||
This is useful if the button is being used to show a pop-up menu, as it allows
|
||||
the click to be used as a drag onto the menu.
|
||||
*/
|
||||
void setTriggeredOnMouseDown (bool isTriggeredOnMouseDown) noexcept;
|
||||
|
||||
/** Returns whether the button click happens when the mouse is pressed or released.
|
||||
@see setTriggeredOnMouseDown
|
||||
*/
|
||||
bool getTriggeredOnMouseDown() const noexcept;
|
||||
|
||||
/** Returns the number of milliseconds since the last time the button
|
||||
went into the 'down' state.
|
||||
*/
|
||||
uint32 getMillisecondsSinceButtonDown() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the tooltip for this button.
|
||||
@see TooltipClient, TooltipWindow
|
||||
*/
|
||||
void setTooltip (const String& newTooltip) override;
|
||||
|
||||
//==============================================================================
|
||||
/** A combination of these flags are used by setConnectedEdges(). */
|
||||
enum ConnectedEdgeFlags
|
||||
{
|
||||
ConnectedOnLeft = 1,
|
||||
ConnectedOnRight = 2,
|
||||
ConnectedOnTop = 4,
|
||||
ConnectedOnBottom = 8
|
||||
};
|
||||
|
||||
/** Hints about which edges of the button might be connected to adjoining buttons.
|
||||
|
||||
The value passed in is a bitwise combination of any of the values in the
|
||||
ConnectedEdgeFlags enum.
|
||||
|
||||
E.g. if you are placing two buttons adjacent to each other, you could use this to
|
||||
indicate which edges are touching, and the LookAndFeel might choose to drawn them
|
||||
without rounded corners on the edges that connect. It's only a hint, so the
|
||||
LookAndFeel can choose to ignore it if it's not relevant for this type of
|
||||
button.
|
||||
*/
|
||||
void setConnectedEdges (int connectedEdgeFlags);
|
||||
|
||||
/** Returns the set of flags passed into setConnectedEdges(). */
|
||||
int getConnectedEdgeFlags() const noexcept { return connectedEdgeFlags; }
|
||||
|
||||
/** Indicates whether the button adjoins another one on its left edge.
|
||||
@see setConnectedEdges
|
||||
*/
|
||||
bool isConnectedOnLeft() const noexcept { return (connectedEdgeFlags & ConnectedOnLeft) != 0; }
|
||||
|
||||
/** Indicates whether the button adjoins another one on its right edge.
|
||||
@see setConnectedEdges
|
||||
*/
|
||||
bool isConnectedOnRight() const noexcept { return (connectedEdgeFlags & ConnectedOnRight) != 0; }
|
||||
|
||||
/** Indicates whether the button adjoins another one on its top edge.
|
||||
@see setConnectedEdges
|
||||
*/
|
||||
bool isConnectedOnTop() const noexcept { return (connectedEdgeFlags & ConnectedOnTop) != 0; }
|
||||
|
||||
/** Indicates whether the button adjoins another one on its bottom edge.
|
||||
@see setConnectedEdges
|
||||
*/
|
||||
bool isConnectedOnBottom() const noexcept { return (connectedEdgeFlags & ConnectedOnBottom) != 0; }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Used by setState(). */
|
||||
enum ButtonState
|
||||
{
|
||||
buttonNormal,
|
||||
buttonOver,
|
||||
buttonDown
|
||||
};
|
||||
|
||||
/** Can be used to force the button into a particular state.
|
||||
|
||||
This only changes the button's appearance, it won't trigger a click, or stop any mouse-clicks
|
||||
from happening.
|
||||
|
||||
The state that you set here will only last until it is automatically changed when the mouse
|
||||
enters or exits the button, or the mouse-button is pressed or released.
|
||||
*/
|
||||
void setState (ButtonState newState);
|
||||
|
||||
/** Returns the button's current over/down/up state. */
|
||||
ButtonState getState() const noexcept { return buttonState; }
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||
button-drawing functionality.
|
||||
*/
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual void drawButtonBackground (Graphics&, Button&, const Colour& backgroundColour,
|
||||
bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) = 0;
|
||||
|
||||
virtual Font getTextButtonFont (TextButton&, int buttonHeight) = 0;
|
||||
virtual int getTextButtonWidthToFitText (TextButton&, int buttonHeight) = 0;
|
||||
|
||||
/** Draws the text for a TextButton. */
|
||||
virtual void drawButtonText (Graphics&, TextButton&,
|
||||
bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) = 0;
|
||||
|
||||
/** Draws the contents of a standard ToggleButton. */
|
||||
virtual void drawToggleButton (Graphics&, ToggleButton&,
|
||||
bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) = 0;
|
||||
|
||||
virtual void changeToggleButtonWidthToFitText (ToggleButton&) = 0;
|
||||
|
||||
virtual void drawTickBox (Graphics&, Component&, float x, float y, float w, float h,
|
||||
bool ticked, bool isEnabled,
|
||||
bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) = 0;
|
||||
|
||||
virtual void drawDrawableButton (Graphics&, DrawableButton&,
|
||||
bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
[[deprecated ("This method's parameters have changed.")]]
|
||||
void setToggleState (bool, bool);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** This method is called when the button has been clicked.
|
||||
|
||||
Subclasses can override this to perform whatever actions they need to do.
|
||||
In general, you wouldn't use this method to receive clicks, but should get your callbacks
|
||||
by attaching a std::function to the onClick callback, or adding a Button::Listener.
|
||||
@see triggerClick, onClick
|
||||
*/
|
||||
virtual void clicked();
|
||||
|
||||
/** This method is called when the button has been clicked.
|
||||
|
||||
By default it just calls clicked(), but you might want to override it to handle
|
||||
things like clicking when a modifier key is pressed, etc.
|
||||
|
||||
@see ModifierKeys
|
||||
*/
|
||||
virtual void clicked (const ModifierKeys& modifiers);
|
||||
|
||||
/** Subclasses should override this to actually paint the button's contents.
|
||||
|
||||
It's better to use this than the paint method, because it gives you information
|
||||
about the over/down state of the button.
|
||||
|
||||
@param g the graphics context to use
|
||||
@param shouldDrawButtonAsHighlighted true if the button is either in the 'over' or 'down' state
|
||||
@param shouldDrawButtonAsDown true if the button should be drawn in the 'down' position
|
||||
*/
|
||||
virtual void paintButton (Graphics& g,
|
||||
bool shouldDrawButtonAsHighlighted,
|
||||
bool shouldDrawButtonAsDown) = 0;
|
||||
|
||||
/** Called when the button's up/down/over state changes.
|
||||
|
||||
Subclasses can override this if they need to do something special when the button
|
||||
goes up or down.
|
||||
|
||||
@see isDown, isOver
|
||||
*/
|
||||
virtual void buttonStateChanged();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
virtual void internalClickCallback (const ModifierKeys&);
|
||||
/** @internal */
|
||||
void handleCommandMessage (int commandId) override;
|
||||
/** @internal */
|
||||
void mouseEnter (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseExit (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDown (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseUp (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&) override;
|
||||
/** @internal */
|
||||
using Component::keyStateChanged;
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
void visibilityChanged() override;
|
||||
/** @internal */
|
||||
void focusGained (FocusChangeType) override;
|
||||
/** @internal */
|
||||
void focusLost (FocusChangeType) override;
|
||||
/** @internal */
|
||||
void enablementChanged() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<KeyPress> shortcuts;
|
||||
WeakReference<Component> keySource;
|
||||
String text;
|
||||
ListenerList<Listener> buttonListeners;
|
||||
|
||||
struct CallbackHelper;
|
||||
std::unique_ptr<CallbackHelper> callbackHelper;
|
||||
uint32 buttonPressTime = 0, lastRepeatTime = 0;
|
||||
ApplicationCommandManager* commandManagerToUse = nullptr;
|
||||
int autoRepeatDelay = -1, autoRepeatSpeed = 0, autoRepeatMinimumDelay = -1;
|
||||
int radioGroupId = 0, connectedEdgeFlags = 0;
|
||||
CommandID commandID = {};
|
||||
ButtonState buttonState = buttonNormal, lastStatePainted = buttonNormal;
|
||||
|
||||
Value isOn;
|
||||
bool lastToggleState = false;
|
||||
bool clickTogglesState = false;
|
||||
bool needsToRelease = false;
|
||||
bool needsRepainting = false;
|
||||
bool isKeyDown = false;
|
||||
bool triggerOnMouseDown = false;
|
||||
bool generateTooltip = false;
|
||||
bool isInDraggableViewport = false ,isDraggingToScroll = false;
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
|
||||
void repeatTimerCallback();
|
||||
bool keyStateChangedCallback();
|
||||
void applicationCommandListChangeCallback();
|
||||
void updateAutomaticTooltip (const ApplicationCommandInfo&);
|
||||
|
||||
ButtonState updateState();
|
||||
ButtonState updateState (bool isOver, bool isDown);
|
||||
bool isShortcutPressed() const;
|
||||
void turnOffOtherButtonsInGroup (NotificationType click, NotificationType state);
|
||||
|
||||
void flashButtonState();
|
||||
void sendClickMessage (const ModifierKeys&);
|
||||
void sendStateMessage();
|
||||
void setToggleState (bool shouldBeOn, NotificationType click, NotificationType state);
|
||||
|
||||
bool isMouseSourceOver (const MouseEvent& e);
|
||||
|
||||
bool isInDragToScrollViewport() const noexcept;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Button)
|
||||
};
|
||||
|
||||
|
||||
} // namespace juce
|
253
deps/juce/modules/juce_gui_basics/buttons/juce_DrawableButton.cpp
vendored
Normal file
253
deps/juce/modules/juce_gui_basics/buttons/juce_DrawableButton.cpp
vendored
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
DrawableButton::DrawableButton (const String& name, const DrawableButton::ButtonStyle buttonStyle)
|
||||
: Button (name), style (buttonStyle)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableButton::~DrawableButton()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static std::unique_ptr<Drawable> copyDrawableIfNotNull (const Drawable* const d)
|
||||
{
|
||||
if (d != nullptr)
|
||||
return d->createCopy();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void DrawableButton::setImages (const Drawable* normal,
|
||||
const Drawable* over,
|
||||
const Drawable* down,
|
||||
const Drawable* disabled,
|
||||
const Drawable* normalOn,
|
||||
const Drawable* overOn,
|
||||
const Drawable* downOn,
|
||||
const Drawable* disabledOn)
|
||||
{
|
||||
jassert (normal != nullptr); // you really need to give it at least a normal image..
|
||||
|
||||
normalImage = copyDrawableIfNotNull (normal);
|
||||
overImage = copyDrawableIfNotNull (over);
|
||||
downImage = copyDrawableIfNotNull (down);
|
||||
disabledImage = copyDrawableIfNotNull (disabled);
|
||||
normalImageOn = copyDrawableIfNotNull (normalOn);
|
||||
overImageOn = copyDrawableIfNotNull (overOn);
|
||||
downImageOn = copyDrawableIfNotNull (downOn);
|
||||
disabledImageOn = copyDrawableIfNotNull (disabledOn);
|
||||
|
||||
currentImage = nullptr;
|
||||
|
||||
buttonStateChanged();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableButton::setButtonStyle (const DrawableButton::ButtonStyle newStyle)
|
||||
{
|
||||
if (style != newStyle)
|
||||
{
|
||||
style = newStyle;
|
||||
buttonStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableButton::setEdgeIndent (const int numPixelsIndent)
|
||||
{
|
||||
edgeIndent = numPixelsIndent;
|
||||
repaint();
|
||||
resized();
|
||||
}
|
||||
|
||||
Rectangle<float> DrawableButton::getImageBounds() const
|
||||
{
|
||||
auto r = getLocalBounds();
|
||||
|
||||
if (style != ImageStretched)
|
||||
{
|
||||
auto indentX = jmin (edgeIndent, proportionOfWidth (0.3f));
|
||||
auto indentY = jmin (edgeIndent, proportionOfHeight (0.3f));
|
||||
|
||||
if (shouldDrawButtonBackground())
|
||||
{
|
||||
indentX = jmax (getWidth() / 4, indentX);
|
||||
indentY = jmax (getHeight() / 4, indentY);
|
||||
}
|
||||
else if (style == ImageAboveTextLabel)
|
||||
{
|
||||
r = r.withTrimmedBottom (jmin (16, proportionOfHeight (0.25f)));
|
||||
}
|
||||
else if (getStyle() == ImageBelowTextLabel)
|
||||
{
|
||||
r = r.withTrimmedTop (jmin (14, proportionOfHeight (0.25f)));
|
||||
}
|
||||
else if (getStyle() == ImageLeftOfTextLabel)
|
||||
{
|
||||
r = r.withTrimmedRight (proportionOfWidth (0.5f));
|
||||
}
|
||||
else if (getStyle() == ImageRightOfTextLabel)
|
||||
{
|
||||
r = r.withTrimmedLeft (proportionOfWidth (0.5f));
|
||||
}
|
||||
r = r.reduced (indentX, indentY);
|
||||
}
|
||||
|
||||
return r.toFloat();
|
||||
}
|
||||
|
||||
void DrawableButton::resized()
|
||||
{
|
||||
Button::resized();
|
||||
|
||||
if (currentImage != nullptr)
|
||||
{
|
||||
if (style != ImageRaw)
|
||||
{
|
||||
int transformFlags = 0;
|
||||
|
||||
if (style == ImageStretched)
|
||||
{
|
||||
transformFlags |= RectanglePlacement::stretchToFit;
|
||||
}
|
||||
else
|
||||
{
|
||||
transformFlags |= RectanglePlacement::centred;
|
||||
|
||||
if (style == ImageOnButtonBackgroundOriginalSize)
|
||||
transformFlags |= RectanglePlacement::doNotResize;
|
||||
}
|
||||
|
||||
currentImage->setTransformToFit (getImageBounds(), transformFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableButton::buttonStateChanged()
|
||||
{
|
||||
repaint();
|
||||
|
||||
Drawable* imageToDraw = nullptr;
|
||||
float opacity = 1.0f;
|
||||
|
||||
if (isEnabled())
|
||||
{
|
||||
imageToDraw = getCurrentImage();
|
||||
}
|
||||
else
|
||||
{
|
||||
imageToDraw = getToggleState() ? disabledImageOn.get()
|
||||
: disabledImage.get();
|
||||
|
||||
if (imageToDraw == nullptr)
|
||||
{
|
||||
opacity = 0.4f;
|
||||
imageToDraw = getNormalImage();
|
||||
}
|
||||
}
|
||||
|
||||
if (imageToDraw != currentImage)
|
||||
{
|
||||
removeChildComponent (currentImage);
|
||||
currentImage = imageToDraw;
|
||||
|
||||
if (currentImage != nullptr)
|
||||
{
|
||||
currentImage->setInterceptsMouseClicks (false, false);
|
||||
currentImage->setAccessible(false);
|
||||
addAndMakeVisible (currentImage);
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
if (currentImage != nullptr)
|
||||
currentImage->setAlpha (opacity);
|
||||
}
|
||||
|
||||
void DrawableButton::enablementChanged()
|
||||
{
|
||||
Button::enablementChanged();
|
||||
buttonStateChanged();
|
||||
}
|
||||
|
||||
void DrawableButton::colourChanged()
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
void DrawableButton::paintButton (Graphics& g,
|
||||
const bool shouldDrawButtonAsHighlighted,
|
||||
const bool shouldDrawButtonAsDown)
|
||||
{
|
||||
auto& lf = getLookAndFeel();
|
||||
|
||||
if (shouldDrawButtonBackground())
|
||||
lf.drawButtonBackground (g, *this,
|
||||
findColour (getToggleState() ? TextButton::buttonOnColourId
|
||||
: TextButton::buttonColourId),
|
||||
shouldDrawButtonAsHighlighted, shouldDrawButtonAsDown);
|
||||
else
|
||||
lf.drawDrawableButton (g, *this, shouldDrawButtonAsHighlighted, shouldDrawButtonAsDown);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Drawable* DrawableButton::getCurrentImage() const noexcept
|
||||
{
|
||||
if (isDown()) return getDownImage();
|
||||
if (isOver()) return getOverImage();
|
||||
|
||||
return getNormalImage();
|
||||
}
|
||||
|
||||
Drawable* DrawableButton::getNormalImage() const noexcept
|
||||
{
|
||||
return (getToggleState() && normalImageOn != nullptr) ? normalImageOn.get()
|
||||
: normalImage.get();
|
||||
}
|
||||
|
||||
Drawable* DrawableButton::getOverImage() const noexcept
|
||||
{
|
||||
if (getToggleState())
|
||||
{
|
||||
if (overImageOn != nullptr) return overImageOn.get();
|
||||
if (normalImageOn != nullptr) return normalImageOn.get();
|
||||
}
|
||||
|
||||
return overImage != nullptr ? overImage.get() : normalImage.get();
|
||||
}
|
||||
|
||||
Drawable* DrawableButton::getDownImage() const noexcept
|
||||
{
|
||||
if (auto* d = getToggleState() ? downImageOn.get() : downImage.get())
|
||||
return d;
|
||||
|
||||
return getOverImage();
|
||||
}
|
||||
|
||||
} // namespace juce
|
200
deps/juce/modules/juce_gui_basics/buttons/juce_DrawableButton.h
vendored
Normal file
200
deps/juce/modules/juce_gui_basics/buttons/juce_DrawableButton.h
vendored
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A button that displays a Drawable.
|
||||
|
||||
Up to three Drawable objects can be given to this button, to represent the
|
||||
'normal', 'over' and 'down' states.
|
||||
|
||||
@see Button
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DrawableButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
enum ButtonStyle
|
||||
{
|
||||
ImageFitted, /**< The button will just display the images, but will resize and centre them to fit inside it. */
|
||||
ImageRaw, /**< The button will just display the images in their normal size and position.
|
||||
This leaves it up to the caller to make sure the images are the correct size and position for the button. */
|
||||
ImageAboveTextLabel, /**< Draws the button as a text label across the bottom with the image resized and scaled to fit above it. */
|
||||
ImageOnButtonBackground, /**< Draws the button as a standard rounded-rectangle button with the image on top. The image will be resized
|
||||
to match the button's proportions.
|
||||
Note that if you use this style, the colour IDs that control the button colour are
|
||||
TextButton::buttonColourId and TextButton::buttonOnColourId. */
|
||||
ImageOnButtonBackgroundOriginalSize, /** Same as ImageOnButtonBackground, but keeps the original image size. */
|
||||
ImageStretched, /**< Fills the button with a stretched version of the image. */
|
||||
ImageBelowTextLabel, /**< Draws the button as a text label across the top with the image resized and scaled to fit below it. */
|
||||
ImageLeftOfTextLabel, /**< Draws the button as a text label on the right with the image resized and scaled to fit beside it. */
|
||||
ImageRightOfTextLabel /**< Draws the button as a text label on the left with the image resized and scaled to fit beside it. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a DrawableButton.
|
||||
|
||||
After creating one of these, use setImages() to specify the drawables to use.
|
||||
|
||||
@param buttonName the name to give the component
|
||||
@param buttonStyle the layout to use
|
||||
|
||||
@see ButtonStyle, setButtonStyle, setImages
|
||||
*/
|
||||
DrawableButton (const String& buttonName,
|
||||
ButtonStyle buttonStyle);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableButton() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets up the images to draw for the various button states.
|
||||
|
||||
The button will keep its own internal copies of these drawables.
|
||||
|
||||
@param normalImage the thing to draw for the button's 'normal' state. An internal copy
|
||||
will be made of the object passed-in if it is non-null.
|
||||
@param overImage the thing to draw for the button's 'over' state - if this is
|
||||
null, the button's normal image will be used when the mouse is
|
||||
over it. An internal copy will be made of the object passed-in
|
||||
if it is non-null.
|
||||
@param downImage the thing to draw for the button's 'down' state - if this is
|
||||
null, the 'over' image will be used instead (or the normal image
|
||||
as a last resort). An internal copy will be made of the object
|
||||
passed-in if it is non-null.
|
||||
@param disabledImage an image to draw when the button is disabled. If this is null,
|
||||
the normal image will be drawn with a reduced opacity instead.
|
||||
An internal copy will be made of the object passed-in if it is
|
||||
non-null.
|
||||
@param normalImageOn same as the normalImage, but this is used when the button's toggle
|
||||
state is 'on'. If this is nullptr, the normal image is used instead
|
||||
@param overImageOn same as the overImage, but this is used when the button's toggle
|
||||
state is 'on'. If this is nullptr, the normalImageOn is drawn instead
|
||||
@param downImageOn same as the downImage, but this is used when the button's toggle
|
||||
state is 'on'. If this is nullptr, the overImageOn is drawn instead
|
||||
@param disabledImageOn same as the disabledImage, but this is used when the button's toggle
|
||||
state is 'on'. If this is nullptr, the normal image will be drawn instead
|
||||
with a reduced opacity
|
||||
*/
|
||||
void setImages (const Drawable* normalImage,
|
||||
const Drawable* overImage = nullptr,
|
||||
const Drawable* downImage = nullptr,
|
||||
const Drawable* disabledImage = nullptr,
|
||||
const Drawable* normalImageOn = nullptr,
|
||||
const Drawable* overImageOn = nullptr,
|
||||
const Drawable* downImageOn = nullptr,
|
||||
const Drawable* disabledImageOn = nullptr);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the button's style.
|
||||
@see ButtonStyle
|
||||
*/
|
||||
void setButtonStyle (ButtonStyle newStyle);
|
||||
|
||||
/** Returns the current style. */
|
||||
ButtonStyle getStyle() const noexcept { return style; }
|
||||
|
||||
//==============================================================================
|
||||
/** Gives the button an optional amount of space around the edge of the drawable.
|
||||
By default there's a gap of about 3 pixels.
|
||||
*/
|
||||
void setEdgeIndent (int numPixelsIndent);
|
||||
|
||||
/** Returns the current edge indent size. */
|
||||
int getEdgeIndent() const noexcept { return edgeIndent; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the image that the button is currently displaying. */
|
||||
Drawable* getCurrentImage() const noexcept;
|
||||
|
||||
/** Returns the image that the button will use for its normal state. */
|
||||
Drawable* getNormalImage() const noexcept;
|
||||
/** Returns the image that the button will use when the mouse is over it. */
|
||||
Drawable* getOverImage() const noexcept;
|
||||
/** Returns the image that the button will use when the mouse is held down on it. */
|
||||
Drawable* getDownImage() const noexcept;
|
||||
|
||||
/** Can be overridden to specify a custom position for the image within the button. */
|
||||
virtual Rectangle<float> getImageBounds() const;
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the link.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
Note that when the ImageOnButtonBackground style is used, the colour IDs that control
|
||||
the button colour are TextButton::buttonColourId and TextButton::buttonOnColourId.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
textColourId = 0x1004010, /**< The colour to use for the button's text label. */
|
||||
textColourOnId = 0x1004013, /**< The colour to use for the button's text when the button's toggle state is "on". */
|
||||
|
||||
backgroundColourId = 0x1004011, /**< The colour used to fill the button's background (when
|
||||
the button is toggled 'off'). Note that if you use the
|
||||
ImageOnButtonBackground style, you should use TextButton::buttonColourId
|
||||
to change the button's colour. */
|
||||
backgroundOnColourId = 0x1004012, /**< The colour used to fill the button's background (when
|
||||
the button is toggled 'on'). Note that if you use the
|
||||
ImageOnButtonBackground style, you should use TextButton::buttonOnColourId
|
||||
to change the button's colour. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paintButton (Graphics&, bool, bool) override;
|
||||
/** @internal */
|
||||
void buttonStateChanged() override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void enablementChanged() override;
|
||||
/** @internal */
|
||||
void colourChanged() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
bool shouldDrawButtonBackground() const { return style == ImageOnButtonBackground || style == ImageOnButtonBackgroundOriginalSize; }
|
||||
|
||||
//==============================================================================
|
||||
ButtonStyle style;
|
||||
std::unique_ptr<Drawable> normalImage, overImage, downImage, disabledImage,
|
||||
normalImageOn, overImageOn, downImageOn, disabledImageOn;
|
||||
Drawable* currentImage = nullptr;
|
||||
int edgeIndent = 3;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DrawableButton)
|
||||
};
|
||||
|
||||
} // namespace juce
|
124
deps/juce/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp
vendored
Normal file
124
deps/juce/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
HyperlinkButton::HyperlinkButton (const String& linkText,
|
||||
const URL& linkURL)
|
||||
: Button (linkText),
|
||||
url (linkURL),
|
||||
font (14.0f, Font::underlined),
|
||||
resizeFont (true),
|
||||
justification (Justification::centred)
|
||||
{
|
||||
setMouseCursor (MouseCursor::PointingHandCursor);
|
||||
setTooltip (linkURL.toString (false));
|
||||
}
|
||||
|
||||
HyperlinkButton::HyperlinkButton()
|
||||
: Button (String()),
|
||||
font (14.0f, Font::underlined),
|
||||
resizeFont (true),
|
||||
justification (Justification::centred)
|
||||
{
|
||||
setMouseCursor (MouseCursor::PointingHandCursor);
|
||||
}
|
||||
|
||||
HyperlinkButton::~HyperlinkButton()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void HyperlinkButton::setFont (const Font& newFont,
|
||||
const bool resizeToMatchComponentHeight,
|
||||
Justification justificationType)
|
||||
{
|
||||
font = newFont;
|
||||
resizeFont = resizeToMatchComponentHeight;
|
||||
justification = justificationType;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void HyperlinkButton::setURL (const URL& newURL) noexcept
|
||||
{
|
||||
url = newURL;
|
||||
setTooltip (newURL.toString (false));
|
||||
}
|
||||
|
||||
Font HyperlinkButton::getFontToUse() const
|
||||
{
|
||||
if (resizeFont)
|
||||
return font.withHeight ((float) getHeight() * 0.7f);
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
void HyperlinkButton::changeWidthToFitText()
|
||||
{
|
||||
setSize (getFontToUse().getStringWidth (getButtonText()) + 6, getHeight());
|
||||
}
|
||||
|
||||
void HyperlinkButton::setJustificationType (Justification newJustification)
|
||||
{
|
||||
if (justification != newJustification)
|
||||
{
|
||||
justification = newJustification;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void HyperlinkButton::colourChanged()
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void HyperlinkButton::clicked()
|
||||
{
|
||||
if (url.isWellFormed())
|
||||
url.launchInDefaultBrowser();
|
||||
}
|
||||
|
||||
void HyperlinkButton::paintButton (Graphics& g,
|
||||
bool shouldDrawButtonAsHighlighted,
|
||||
bool shouldDrawButtonAsDown)
|
||||
{
|
||||
const Colour textColour (findColour (textColourId));
|
||||
|
||||
if (isEnabled())
|
||||
g.setColour ((shouldDrawButtonAsHighlighted) ? textColour.darker ((shouldDrawButtonAsDown) ? 1.3f : 0.4f)
|
||||
: textColour);
|
||||
else
|
||||
g.setColour (textColour.withMultipliedAlpha (0.4f));
|
||||
|
||||
g.setFont (getFontToUse());
|
||||
|
||||
g.drawText (getButtonText(), getLocalBounds().reduced (1, 0),
|
||||
justification.getOnlyHorizontalFlags() | Justification::verticallyCentred,
|
||||
true);
|
||||
}
|
||||
|
||||
} // namespace juce
|
127
deps/juce/modules/juce_gui_basics/buttons/juce_HyperlinkButton.h
vendored
Normal file
127
deps/juce/modules/juce_gui_basics/buttons/juce_HyperlinkButton.h
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A button showing an underlined weblink, that will launch the link
|
||||
when it's clicked.
|
||||
|
||||
@see Button
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API HyperlinkButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a HyperlinkButton.
|
||||
|
||||
@param linkText the text that will be displayed in the button - this is
|
||||
also set as the Component's name, but the text can be
|
||||
changed later with the Button::setButtonText() method
|
||||
@param linkURL the URL to launch when the user clicks the button
|
||||
*/
|
||||
HyperlinkButton (const String& linkText,
|
||||
const URL& linkURL);
|
||||
|
||||
/** Creates a HyperlinkButton. */
|
||||
HyperlinkButton();
|
||||
|
||||
/** Destructor. */
|
||||
~HyperlinkButton() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the font to use for the text.
|
||||
|
||||
If resizeToMatchComponentHeight is true, the font's height will be adjusted
|
||||
to match the size of the component.
|
||||
*/
|
||||
void setFont (const Font& newFont,
|
||||
bool resizeToMatchComponentHeight,
|
||||
Justification justificationType = Justification::horizontallyCentred);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the link.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
textColourId = 0x1001f00, /**< The colour to use for the URL text. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the URL that the button will trigger. */
|
||||
void setURL (const URL& newURL) noexcept;
|
||||
|
||||
/** Returns the URL that the button will trigger. */
|
||||
const URL& getURL() const noexcept { return url; }
|
||||
|
||||
//==============================================================================
|
||||
/** Resizes the button horizontally to fit snugly around the text.
|
||||
|
||||
This won't affect the button's height.
|
||||
*/
|
||||
void changeWidthToFitText();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the style of justification to be used for positioning the text.
|
||||
(The default is Justification::centred)
|
||||
*/
|
||||
void setJustificationType (Justification justification);
|
||||
|
||||
/** Returns the type of justification, as set in setJustificationType(). */
|
||||
Justification getJustificationType() const noexcept { return justification; }
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void clicked() override;
|
||||
/** @internal */
|
||||
void colourChanged() override;
|
||||
/** @internal */
|
||||
void paintButton (Graphics&, bool, bool) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
using Button::clicked;
|
||||
Font getFontToUse() const;
|
||||
|
||||
//==============================================================================
|
||||
URL url;
|
||||
Font font;
|
||||
bool resizeFont;
|
||||
Justification justification;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HyperlinkButton)
|
||||
};
|
||||
|
||||
} // namespace juce
|
208
deps/juce/modules/juce_gui_basics/buttons/juce_ImageButton.cpp
vendored
Normal file
208
deps/juce/modules/juce_gui_basics/buttons/juce_ImageButton.cpp
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
ImageButton::ImageButton (const String& text_)
|
||||
: Button (text_),
|
||||
scaleImageToFit (true),
|
||||
preserveProportions (true),
|
||||
alphaThreshold (0)
|
||||
{
|
||||
}
|
||||
|
||||
ImageButton::~ImageButton()
|
||||
{
|
||||
}
|
||||
|
||||
void ImageButton::setImages (const bool resizeButtonNowToFitThisImage,
|
||||
const bool rescaleImagesWhenButtonSizeChanges,
|
||||
const bool preserveImageProportions,
|
||||
const Image& normalImage_,
|
||||
const float imageOpacityWhenNormal,
|
||||
Colour overlayColourWhenNormal,
|
||||
const Image& overImage_,
|
||||
const float imageOpacityWhenOver,
|
||||
Colour overlayColourWhenOver,
|
||||
const Image& downImage_,
|
||||
const float imageOpacityWhenDown,
|
||||
Colour overlayColourWhenDown,
|
||||
const float hitTestAlphaThreshold)
|
||||
{
|
||||
normalImage = normalImage_;
|
||||
overImage = overImage_;
|
||||
downImage = downImage_;
|
||||
|
||||
if (resizeButtonNowToFitThisImage && normalImage.isValid())
|
||||
{
|
||||
imageBounds.setSize (normalImage.getWidth(),
|
||||
normalImage.getHeight());
|
||||
|
||||
setSize (imageBounds.getWidth(), imageBounds.getHeight());
|
||||
}
|
||||
|
||||
scaleImageToFit = rescaleImagesWhenButtonSizeChanges;
|
||||
preserveProportions = preserveImageProportions;
|
||||
|
||||
normalOpacity = imageOpacityWhenNormal;
|
||||
normalOverlay = overlayColourWhenNormal;
|
||||
overOpacity = imageOpacityWhenOver;
|
||||
overOverlay = overlayColourWhenOver;
|
||||
downOpacity = imageOpacityWhenDown;
|
||||
downOverlay = overlayColourWhenDown;
|
||||
|
||||
alphaThreshold = (uint8) jlimit (0, 0xff, roundToInt (255.0f * hitTestAlphaThreshold));
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void ImageButton::setHitTestAlphaThreshold(float thresh)
|
||||
{
|
||||
alphaThreshold = (uint8) jlimit (0, 0xff, roundToInt (255.0f * thresh));
|
||||
}
|
||||
|
||||
float ImageButton::getHitTestAlphaThreshold() const
|
||||
{
|
||||
return alphaThreshold / 255.0f;
|
||||
}
|
||||
|
||||
Image ImageButton::getCurrentImage() const
|
||||
{
|
||||
if (isDown() || getToggleState())
|
||||
return getDownImage();
|
||||
|
||||
if (isOver())
|
||||
return getOverImage();
|
||||
|
||||
return getNormalImage();
|
||||
}
|
||||
|
||||
Image ImageButton::getNormalImage() const
|
||||
{
|
||||
return normalImage;
|
||||
}
|
||||
|
||||
Image ImageButton::getOverImage() const
|
||||
{
|
||||
return overImage.isValid() ? overImage
|
||||
: normalImage;
|
||||
}
|
||||
|
||||
Image ImageButton::getDownImage() const
|
||||
{
|
||||
return downImage.isValid() ? downImage
|
||||
: getOverImage();
|
||||
}
|
||||
|
||||
void ImageButton::paintButton (Graphics& g,
|
||||
bool shouldDrawButtonAsHighlighted,
|
||||
bool shouldDrawButtonAsDown)
|
||||
{
|
||||
if (! isEnabled())
|
||||
{
|
||||
shouldDrawButtonAsHighlighted = false;
|
||||
shouldDrawButtonAsDown = false;
|
||||
}
|
||||
|
||||
Image im (getCurrentImage());
|
||||
|
||||
if (im.isValid())
|
||||
{
|
||||
const int iw = im.getWidth();
|
||||
const int ih = im.getHeight();
|
||||
int w = getWidth();
|
||||
int h = getHeight();
|
||||
int x = (w - iw) / 2;
|
||||
int y = (h - ih) / 2;
|
||||
|
||||
if (scaleImageToFit)
|
||||
{
|
||||
if (preserveProportions)
|
||||
{
|
||||
int newW, newH;
|
||||
const float imRatio = (float) ih / (float) iw;
|
||||
const float destRatio = (float) h / (float) w;
|
||||
|
||||
if (imRatio > destRatio)
|
||||
{
|
||||
newW = roundToInt ((float) h / imRatio);
|
||||
newH = h;
|
||||
}
|
||||
else
|
||||
{
|
||||
newW = w;
|
||||
newH = roundToInt ((float) w * imRatio);
|
||||
}
|
||||
|
||||
x = (w - newW) / 2;
|
||||
y = (h - newH) / 2;
|
||||
w = newW;
|
||||
h = newH;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (! scaleImageToFit)
|
||||
{
|
||||
w = iw;
|
||||
h = ih;
|
||||
}
|
||||
|
||||
imageBounds.setBounds (x, y, w, h);
|
||||
|
||||
const bool useDownImage = shouldDrawButtonAsDown || getToggleState();
|
||||
|
||||
getLookAndFeel().drawImageButton (g, &im, x, y, w, h,
|
||||
useDownImage ? downOverlay
|
||||
: (shouldDrawButtonAsHighlighted ? overOverlay
|
||||
: normalOverlay),
|
||||
useDownImage ? downOpacity
|
||||
: (shouldDrawButtonAsHighlighted ? overOpacity
|
||||
: normalOpacity),
|
||||
*this);
|
||||
}
|
||||
}
|
||||
|
||||
bool ImageButton::hitTest (int x, int y)
|
||||
{
|
||||
if (! Component::hitTest (x, y)) // handle setInterceptsMouseClicks
|
||||
return false;
|
||||
|
||||
if (alphaThreshold == 0)
|
||||
return true;
|
||||
|
||||
Image im (getCurrentImage());
|
||||
|
||||
return im.isNull() || ((! imageBounds.isEmpty())
|
||||
&& alphaThreshold < im.getPixelAt (((x - imageBounds.getX()) * im.getWidth()) / imageBounds.getWidth(),
|
||||
((y - imageBounds.getY()) * im.getHeight()) / imageBounds.getHeight()).getAlpha());
|
||||
}
|
||||
|
||||
} // namespace juce
|
164
deps/juce/modules/juce_gui_basics/buttons/juce_ImageButton.h
vendored
Normal file
164
deps/juce/modules/juce_gui_basics/buttons/juce_ImageButton.h
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
As the title suggests, this is a button containing an image.
|
||||
|
||||
The colour and transparency of the image can be set to vary when the
|
||||
button state changes.
|
||||
|
||||
@see Button, ShapeButton, TextButton
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ImageButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an ImageButton.
|
||||
|
||||
Use setImage() to specify the image to use. The colours and opacities that
|
||||
are specified here can be changed later using setImages().
|
||||
|
||||
@param name the name to give the component
|
||||
*/
|
||||
explicit ImageButton (const String& name = String());
|
||||
|
||||
/** Destructor. */
|
||||
~ImageButton() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets up the images to draw in various states.
|
||||
|
||||
@param resizeButtonNowToFitThisImage if true, the button will be immediately
|
||||
resized to the same dimensions as the normal image
|
||||
@param rescaleImagesWhenButtonSizeChanges if true, the image will be rescaled to fit the
|
||||
button when the button's size changes
|
||||
@param preserveImageProportions if true then any rescaling of the image to fit
|
||||
the button will keep the image's x and y proportions
|
||||
correct - i.e. it won't distort its shape, although
|
||||
this might create gaps around the edges
|
||||
@param normalImage the image to use when the button is in its normal state.
|
||||
button no longer needs it.
|
||||
@param imageOpacityWhenNormal the opacity to use when drawing the normal image.
|
||||
@param overlayColourWhenNormal an overlay colour to use to fill the alpha channel of the
|
||||
normal image - if this colour is transparent, no overlay
|
||||
will be drawn. The overlay will be drawn over the top of the
|
||||
image, so you can basically add a solid or semi-transparent
|
||||
colour to the image to brighten or darken it
|
||||
@param overImage the image to use when the mouse is over the button. If
|
||||
you want to use the same image as was set in the normalImage
|
||||
parameter, this value can be a null image.
|
||||
@param imageOpacityWhenOver the opacity to use when drawing the image when the mouse
|
||||
is over the button
|
||||
@param overlayColourWhenOver an overlay colour to use to fill the alpha channel of the
|
||||
image when the mouse is over - if this colour is transparent,
|
||||
no overlay will be drawn
|
||||
@param downImage an image to use when the button is pressed down. If set
|
||||
to a null image, the 'over' image will be drawn instead (or the
|
||||
normal image if there isn't an 'over' image either).
|
||||
@param imageOpacityWhenDown the opacity to use when drawing the image when the button
|
||||
is pressed
|
||||
@param overlayColourWhenDown an overlay colour to use to fill the alpha channel of the
|
||||
image when the button is pressed down - if this colour is
|
||||
transparent, no overlay will be drawn
|
||||
@param hitTestAlphaThreshold if set to zero, the mouse is considered to be over the button
|
||||
whenever it's inside the button's bounding rectangle. If
|
||||
set to values higher than 0, the mouse will only be
|
||||
considered to be over the image when the value of the
|
||||
image's alpha channel at that position is greater than
|
||||
this level.
|
||||
*/
|
||||
void setImages (bool resizeButtonNowToFitThisImage,
|
||||
bool rescaleImagesWhenButtonSizeChanges,
|
||||
bool preserveImageProportions,
|
||||
const Image& normalImage,
|
||||
float imageOpacityWhenNormal,
|
||||
Colour overlayColourWhenNormal,
|
||||
const Image& overImage,
|
||||
float imageOpacityWhenOver,
|
||||
Colour overlayColourWhenOver,
|
||||
const Image& downImage,
|
||||
float imageOpacityWhenDown,
|
||||
Colour overlayColourWhenDown,
|
||||
float hitTestAlphaThreshold = 0.0f);
|
||||
|
||||
/** Returns the currently set 'normal' image. */
|
||||
Image getNormalImage() const;
|
||||
|
||||
/** Returns the image that's drawn when the mouse is over the button.
|
||||
|
||||
If a valid 'over' image has been set, this will return it; otherwise it'll
|
||||
just return the normal image.
|
||||
*/
|
||||
Image getOverImage() const;
|
||||
|
||||
/** Returns the image that's drawn when the button is held down.
|
||||
|
||||
If a valid 'down' image has been set, this will return it; otherwise it'll
|
||||
return the 'over' image or normal image, depending on what's available.
|
||||
*/
|
||||
Image getDownImage() const;
|
||||
|
||||
void setHitTestAlphaThreshold(float thresh);
|
||||
float getHitTestAlphaThreshold() const;
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes. */
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual void drawImageButton (Graphics&, Image*,
|
||||
int imageX, int imageY, int imageW, int imageH,
|
||||
const Colour& overlayColour, float imageOpacity, ImageButton&) = 0;
|
||||
};
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
bool hitTest (int x, int y) override;
|
||||
/** @internal */
|
||||
void paintButton (Graphics&, bool, bool) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
bool scaleImageToFit, preserveProportions;
|
||||
uint8 alphaThreshold;
|
||||
Rectangle<int> imageBounds;
|
||||
Image normalImage, overImage, downImage;
|
||||
float normalOpacity, overOpacity, downOpacity;
|
||||
Colour normalOverlay, overOverlay, downOverlay;
|
||||
|
||||
Image getCurrentImage() const;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImageButton)
|
||||
};
|
||||
|
||||
} // namespace juce
|
137
deps/juce/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp
vendored
Normal file
137
deps/juce/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
ShapeButton::ShapeButton (const String& t, Colour n, Colour o, Colour d)
|
||||
: Button (t),
|
||||
normalColour (n), overColour (o), downColour (d),
|
||||
normalColourOn (n), overColourOn (o), downColourOn (d),
|
||||
useOnColours(false),
|
||||
maintainShapeProportions (false),
|
||||
outlineWidth (0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
ShapeButton::~ShapeButton() {}
|
||||
|
||||
void ShapeButton::setColours (Colour newNormalColour, Colour newOverColour, Colour newDownColour)
|
||||
{
|
||||
normalColour = newNormalColour;
|
||||
overColour = newOverColour;
|
||||
downColour = newDownColour;
|
||||
}
|
||||
|
||||
void ShapeButton::setOnColours (Colour newNormalColourOn, Colour newOverColourOn, Colour newDownColourOn)
|
||||
{
|
||||
normalColourOn = newNormalColourOn;
|
||||
overColourOn = newOverColourOn;
|
||||
downColourOn = newDownColourOn;
|
||||
}
|
||||
|
||||
void ShapeButton::shouldUseOnColours (bool shouldUse)
|
||||
{
|
||||
useOnColours = shouldUse;
|
||||
}
|
||||
|
||||
void ShapeButton::setOutline (Colour newOutlineColour, const float newOutlineWidth)
|
||||
{
|
||||
outlineColour = newOutlineColour;
|
||||
outlineWidth = newOutlineWidth;
|
||||
}
|
||||
|
||||
void ShapeButton::setBorderSize (BorderSize<int> newBorder)
|
||||
{
|
||||
border = newBorder;
|
||||
}
|
||||
|
||||
void ShapeButton::setShape (const Path& newShape,
|
||||
const bool resizeNowToFitThisShape,
|
||||
const bool maintainShapeProportions_,
|
||||
const bool hasShadow)
|
||||
{
|
||||
shape = newShape;
|
||||
maintainShapeProportions = maintainShapeProportions_;
|
||||
|
||||
shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 3, Point<int>()));
|
||||
setComponentEffect (hasShadow ? &shadow : nullptr);
|
||||
|
||||
if (resizeNowToFitThisShape)
|
||||
{
|
||||
auto newBounds = shape.getBounds();
|
||||
|
||||
if (hasShadow)
|
||||
newBounds = newBounds.expanded (4.0f);
|
||||
|
||||
shape.applyTransform (AffineTransform::translation (-newBounds.getX(),
|
||||
-newBounds.getY()));
|
||||
|
||||
setSize (1 + (int) (newBounds.getWidth() + outlineWidth) + border.getLeftAndRight(),
|
||||
1 + (int) (newBounds.getHeight() + outlineWidth) + border.getTopAndBottom());
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void ShapeButton::paintButton (Graphics& g, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown)
|
||||
{
|
||||
if (! isEnabled())
|
||||
{
|
||||
shouldDrawButtonAsHighlighted = false;
|
||||
shouldDrawButtonAsDown = false;
|
||||
}
|
||||
|
||||
auto r = border.subtractedFrom (getLocalBounds())
|
||||
.toFloat()
|
||||
.reduced (outlineWidth * 0.5f);
|
||||
|
||||
if (getComponentEffect() != nullptr)
|
||||
r = r.reduced (2.0f);
|
||||
|
||||
if (shouldDrawButtonAsDown)
|
||||
{
|
||||
const float sizeReductionWhenPressed = 0.04f;
|
||||
|
||||
r = r.reduced (sizeReductionWhenPressed * r.getWidth(),
|
||||
sizeReductionWhenPressed * r.getHeight());
|
||||
}
|
||||
|
||||
auto trans = shape.getTransformToScaleToFit (r, maintainShapeProportions);
|
||||
|
||||
if (shouldDrawButtonAsDown) g.setColour (getToggleState() && useOnColours ? downColourOn : downColour);
|
||||
else if (shouldDrawButtonAsHighlighted) g.setColour (getToggleState() && useOnColours ? overColourOn : overColour);
|
||||
else g.setColour (getToggleState() && useOnColours ? normalColourOn : normalColour);
|
||||
|
||||
g.fillPath (shape, trans);
|
||||
|
||||
if (outlineWidth > 0.0f)
|
||||
{
|
||||
g.setColour (outlineColour);
|
||||
g.strokePath (shape, PathStrokeType (outlineWidth), trans);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
126
deps/juce/modules/juce_gui_basics/buttons/juce_ShapeButton.h
vendored
Normal file
126
deps/juce/modules/juce_gui_basics/buttons/juce_ShapeButton.h
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A button that contains a filled shape.
|
||||
|
||||
@see Button, ImageButton, TextButton, ArrowButton
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ShapeButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ShapeButton.
|
||||
|
||||
@param name a name to give the component - see Component::setName()
|
||||
@param normalColour the colour to fill the shape with when the mouse isn't over
|
||||
@param overColour the colour to use when the mouse is over the shape
|
||||
@param downColour the colour to use when the button is in the pressed-down state
|
||||
*/
|
||||
ShapeButton (const String& name,
|
||||
Colour normalColour,
|
||||
Colour overColour,
|
||||
Colour downColour);
|
||||
|
||||
/** Destructor. */
|
||||
~ShapeButton() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the shape to use.
|
||||
|
||||
@param newShape the shape to use
|
||||
@param resizeNowToFitThisShape if true, the button will be resized to fit the shape's bounds
|
||||
@param maintainShapeProportions if true, the shape's proportions will be kept fixed when
|
||||
the button is resized
|
||||
@param hasDropShadow if true, the button will be given a drop-shadow effect
|
||||
*/
|
||||
void setShape (const Path& newShape,
|
||||
bool resizeNowToFitThisShape,
|
||||
bool maintainShapeProportions,
|
||||
bool hasDropShadow);
|
||||
|
||||
/** Set the colours to use for drawing the shape.
|
||||
|
||||
@param normalColour the colour to fill the shape with when the mouse isn't over
|
||||
@param overColour the colour to use when the mouse is over the shape
|
||||
@param downColour the colour to use when the button is in the pressed-down state
|
||||
*/
|
||||
void setColours (Colour normalColour,
|
||||
Colour overColour,
|
||||
Colour downColour);
|
||||
|
||||
/** Sets the colours to use for drawing the shape when the button's toggle state is 'on'. To enable this behaviour, use the
|
||||
shouldUseOnColours() method.
|
||||
|
||||
@param normalColourOn the colour to fill the shape with when the mouse isn't over and the button's toggle state is 'on'
|
||||
@param overColourOn the colour to use when the mouse is over the shape and the button's toggle state is 'on'
|
||||
@param downColourOn the colour to use when the button is in the pressed-down state and the button's toggle state is 'on'
|
||||
*/
|
||||
void setOnColours (Colour normalColourOn,
|
||||
Colour overColourOn,
|
||||
Colour downColourOn);
|
||||
|
||||
/** Set whether the button should use the 'on' set of colours when its toggle state is 'on'.
|
||||
By default these will be the same as the normal colours but the setOnColours method can be
|
||||
used to provide a different set of colours.
|
||||
*/
|
||||
void shouldUseOnColours (bool shouldUse);
|
||||
|
||||
/** Sets up an outline to draw around the shape.
|
||||
|
||||
@param outlineColour the colour to use
|
||||
@param outlineStrokeWidth the thickness of line to draw
|
||||
*/
|
||||
void setOutline (Colour outlineColour, float outlineStrokeWidth);
|
||||
|
||||
/** This lets you specify a border to be left around the edge of the button when
|
||||
drawing the shape.
|
||||
*/
|
||||
void setBorderSize (BorderSize<int> border);
|
||||
|
||||
/** @internal */
|
||||
void paintButton (Graphics&, bool, bool) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Colour normalColour, overColour, downColour,
|
||||
normalColourOn, overColourOn, downColourOn, outlineColour;
|
||||
bool useOnColours;
|
||||
DropShadowEffect shadow;
|
||||
Path shape;
|
||||
BorderSize<int> border;
|
||||
bool maintainShapeProportions;
|
||||
float outlineWidth;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ShapeButton)
|
||||
};
|
||||
|
||||
} // namespace juce
|
77
deps/juce/modules/juce_gui_basics/buttons/juce_TextButton.cpp
vendored
Normal file
77
deps/juce/modules/juce_gui_basics/buttons/juce_TextButton.cpp
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
TextButton::TextButton() : Button (String())
|
||||
{
|
||||
}
|
||||
|
||||
TextButton::TextButton (const String& name) : Button (name)
|
||||
{
|
||||
}
|
||||
|
||||
TextButton::TextButton (const String& name, const String& toolTip) : Button (name)
|
||||
{
|
||||
setTooltip (toolTip);
|
||||
}
|
||||
|
||||
TextButton::~TextButton()
|
||||
{
|
||||
}
|
||||
|
||||
void TextButton::paintButton (Graphics& g, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown)
|
||||
{
|
||||
auto& lf = getLookAndFeel();
|
||||
|
||||
lf.drawButtonBackground (g, *this,
|
||||
findColour (getToggleState() ? buttonOnColourId : buttonColourId),
|
||||
shouldDrawButtonAsHighlighted, shouldDrawButtonAsDown);
|
||||
|
||||
lf.drawButtonText (g, *this, shouldDrawButtonAsHighlighted, shouldDrawButtonAsDown);
|
||||
}
|
||||
|
||||
void TextButton::colourChanged()
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
void TextButton::changeWidthToFitText()
|
||||
{
|
||||
changeWidthToFitText (getHeight());
|
||||
}
|
||||
|
||||
void TextButton::changeWidthToFitText (const int newHeight)
|
||||
{
|
||||
setSize (getBestWidthForHeight (newHeight), newHeight);
|
||||
}
|
||||
|
||||
int TextButton::getBestWidthForHeight (int buttonHeight)
|
||||
{
|
||||
return getLookAndFeel().getTextButtonWidthToFitText (*this, buttonHeight);
|
||||
}
|
||||
|
||||
} // namespace juce
|
109
deps/juce/modules/juce_gui_basics/buttons/juce_TextButton.h
vendored
Normal file
109
deps/juce/modules/juce_gui_basics/buttons/juce_TextButton.h
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A button that uses the standard lozenge-shaped background with a line of
|
||||
text on it.
|
||||
|
||||
@see Button, DrawableButton
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API TextButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a TextButton. */
|
||||
TextButton();
|
||||
|
||||
/** Creates a TextButton.
|
||||
@param buttonName the text to put in the button (the component's name is also
|
||||
initially set to this string, but these can be changed later
|
||||
using the setName() and setButtonText() methods)
|
||||
*/
|
||||
explicit TextButton (const String& buttonName);
|
||||
|
||||
/** Creates a TextButton.
|
||||
@param buttonName the text to put in the button (the component's name is also
|
||||
initially set to this string, but these can be changed later
|
||||
using the setName() and setButtonText() methods)
|
||||
@param toolTip an optional string to use as a tooltip
|
||||
*/
|
||||
TextButton (const String& buttonName, const String& toolTip);
|
||||
|
||||
/** Destructor. */
|
||||
~TextButton() override;
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the button.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
buttonColourId = 0x1000100, /**< The colour used to fill the button shape (when the button is toggled
|
||||
'off'). The look-and-feel class might re-interpret this to add
|
||||
effects, etc. */
|
||||
buttonOnColourId = 0x1000101, /**< The colour used to fill the button shape (when the button is toggled
|
||||
'on'). The look-and-feel class might re-interpret this to add
|
||||
effects, etc. */
|
||||
textColourOffId = 0x1000102, /**< The colour to use for the button's text when the button's toggle state is "off". */
|
||||
textColourOnId = 0x1000103 /**< The colour to use for the button's text.when the button's toggle state is "on". */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Changes this button's width to fit neatly around its current text, without
|
||||
changing its height.
|
||||
*/
|
||||
void changeWidthToFitText();
|
||||
|
||||
/** Resizes the button's width to fit neatly around its current text, and gives it
|
||||
the specified height.
|
||||
*/
|
||||
void changeWidthToFitText (int newHeight);
|
||||
|
||||
/** Returns the width that the LookAndFeel suggests would be best for this button if it
|
||||
had the given height.
|
||||
*/
|
||||
int getBestWidthForHeight (int buttonHeight);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paintButton (Graphics&, bool, bool) override;
|
||||
/** @internal */
|
||||
void colourChanged() override;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TextButton)
|
||||
};
|
||||
|
||||
} // namespace juce
|
65
deps/juce/modules/juce_gui_basics/buttons/juce_ToggleButton.cpp
vendored
Normal file
65
deps/juce/modules/juce_gui_basics/buttons/juce_ToggleButton.cpp
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
ToggleButton::ToggleButton()
|
||||
: Button (String())
|
||||
{
|
||||
setClickingTogglesState (true);
|
||||
}
|
||||
|
||||
ToggleButton::ToggleButton (const String& buttonText)
|
||||
: Button (buttonText)
|
||||
{
|
||||
setClickingTogglesState (true);
|
||||
}
|
||||
|
||||
ToggleButton::~ToggleButton()
|
||||
{
|
||||
}
|
||||
|
||||
void ToggleButton::paintButton (Graphics& g, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown)
|
||||
{
|
||||
getLookAndFeel().drawToggleButton (g, *this, shouldDrawButtonAsHighlighted, shouldDrawButtonAsDown);
|
||||
}
|
||||
|
||||
void ToggleButton::changeWidthToFitText()
|
||||
{
|
||||
getLookAndFeel().changeToggleButtonWidthToFitText (*this);
|
||||
}
|
||||
|
||||
void ToggleButton::colourChanged()
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> ToggleButton::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<ButtonAccessibilityHandler> (*this, AccessibilityRole::toggleButton);
|
||||
}
|
||||
|
||||
} // namespace juce
|
92
deps/juce/modules/juce_gui_basics/buttons/juce_ToggleButton.h
vendored
Normal file
92
deps/juce/modules/juce_gui_basics/buttons/juce_ToggleButton.h
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A button that can be toggled on/off.
|
||||
|
||||
All buttons can be toggle buttons, but this lets you create one of the
|
||||
standard ones which has a tick-box and a text label next to it.
|
||||
|
||||
@see Button, DrawableButton, TextButton
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ToggleButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ToggleButton. */
|
||||
ToggleButton();
|
||||
|
||||
/** Creates a ToggleButton.
|
||||
|
||||
@param buttonText the text to put in the button (the component's name is also
|
||||
initially set to this string, but these can be changed later
|
||||
using the setName() and setButtonText() methods)
|
||||
*/
|
||||
explicit ToggleButton (const String& buttonText);
|
||||
|
||||
/** Destructor. */
|
||||
~ToggleButton() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Resizes the button to fit neatly around its current text.
|
||||
The button's height won't be affected, only its width.
|
||||
*/
|
||||
void changeWidthToFitText();
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the button.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
textColourId = 0x1006501, /**< The colour to use for the button's text. */
|
||||
tickColourId = 0x1006502, /**< The colour to use for the tick mark. */
|
||||
tickDisabledColourId = 0x1006503 /**< The colour to use for the disabled tick mark and/or outline. */
|
||||
};
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paintButton (Graphics&, bool, bool) override;
|
||||
/** @internal */
|
||||
void colourChanged() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToggleButton)
|
||||
};
|
||||
|
||||
} // namespace juce
|
113
deps/juce/modules/juce_gui_basics/buttons/juce_ToolbarButton.cpp
vendored
Normal file
113
deps/juce/modules/juce_gui_basics/buttons/juce_ToolbarButton.cpp
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
ToolbarButton::ToolbarButton (const int iid, const String& buttonText,
|
||||
std::unique_ptr<Drawable> normalIm,
|
||||
std::unique_ptr<Drawable> toggledOnIm)
|
||||
: ToolbarItemComponent (iid, buttonText, true),
|
||||
normalImage (std::move (normalIm)),
|
||||
toggledOnImage (std::move (toggledOnIm))
|
||||
{
|
||||
jassert (normalImage != nullptr);
|
||||
}
|
||||
|
||||
ToolbarButton::~ToolbarButton()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool ToolbarButton::getToolbarItemSizes (int toolbarDepth, bool /*isToolbarVertical*/, int& preferredSize, int& minSize, int& maxSize)
|
||||
{
|
||||
preferredSize = minSize = maxSize = toolbarDepth;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ToolbarButton::paintButtonArea (Graphics&, int /*width*/, int /*height*/, bool /*isMouseOver*/, bool /*isMouseDown*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ToolbarButton::contentAreaChanged (const Rectangle<int>&)
|
||||
{
|
||||
buttonStateChanged();
|
||||
}
|
||||
|
||||
void ToolbarButton::setCurrentImage (Drawable* const newImage)
|
||||
{
|
||||
if (newImage != currentImage)
|
||||
{
|
||||
removeChildComponent (currentImage);
|
||||
currentImage = newImage;
|
||||
|
||||
if (currentImage != nullptr)
|
||||
{
|
||||
enablementChanged();
|
||||
addAndMakeVisible (currentImage);
|
||||
updateDrawable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ToolbarButton::updateDrawable()
|
||||
{
|
||||
if (currentImage != nullptr)
|
||||
{
|
||||
currentImage->setInterceptsMouseClicks (false, false);
|
||||
currentImage->setTransformToFit (getContentArea().toFloat(), RectanglePlacement::centred);
|
||||
currentImage->setAlpha (isEnabled() ? 1.0f : 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
void ToolbarButton::resized()
|
||||
{
|
||||
ToolbarItemComponent::resized();
|
||||
updateDrawable();
|
||||
}
|
||||
|
||||
void ToolbarButton::enablementChanged()
|
||||
{
|
||||
ToolbarItemComponent::enablementChanged();
|
||||
updateDrawable();
|
||||
}
|
||||
|
||||
Drawable* ToolbarButton::getImageToUse() const
|
||||
{
|
||||
if (getStyle() == Toolbar::textOnly)
|
||||
return nullptr;
|
||||
|
||||
if (getToggleState() && toggledOnImage != nullptr)
|
||||
return toggledOnImage.get();
|
||||
|
||||
return normalImage.get();
|
||||
}
|
||||
|
||||
void ToolbarButton::buttonStateChanged()
|
||||
{
|
||||
setCurrentImage (getImageToUse());
|
||||
}
|
||||
|
||||
} // namespace juce
|
98
deps/juce/modules/juce_gui_basics/buttons/juce_ToolbarButton.h
vendored
Normal file
98
deps/juce/modules/juce_gui_basics/buttons/juce_ToolbarButton.h
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A type of button designed to go on a toolbar.
|
||||
|
||||
This simple button can have two Drawable objects specified - one for normal
|
||||
use and another one (optionally) for the button's "on" state if it's a
|
||||
toggle button.
|
||||
|
||||
@see Toolbar, ToolbarItemFactory, ToolbarItemComponent, Drawable, Button
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ToolbarButton : public ToolbarItemComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ToolbarButton.
|
||||
|
||||
@param itemId the ID for this toolbar item type. This is passed through to the
|
||||
ToolbarItemComponent constructor
|
||||
@param labelText the text to display on the button (if the toolbar is using a style
|
||||
that shows text labels). This is passed through to the
|
||||
ToolbarItemComponent constructor
|
||||
@param normalImage a drawable object that the button should use as its icon. The object
|
||||
that is passed-in here will be kept by this object and will be
|
||||
deleted when no longer needed or when this button is deleted.
|
||||
@param toggledOnImage a drawable object that the button can use as its icon if the button
|
||||
is in a toggled-on state (see the Button::getToggleState() method). If
|
||||
nullptr is passed-in here, then the normal image will be used instead,
|
||||
regardless of the toggle state. The object that is passed-in here will be
|
||||
owned by this object and will be deleted when no longer needed or when
|
||||
this button is deleted.
|
||||
*/
|
||||
ToolbarButton (int itemId,
|
||||
const String& labelText,
|
||||
std::unique_ptr<Drawable> normalImage,
|
||||
std::unique_ptr<Drawable> toggledOnImage);
|
||||
|
||||
/** Destructor. */
|
||||
~ToolbarButton() override;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
bool getToolbarItemSizes (int toolbarDepth, bool isToolbarVertical, int& preferredSize,
|
||||
int& minSize, int& maxSize) override;
|
||||
/** @internal */
|
||||
void paintButtonArea (Graphics&, int width, int height, bool isMouseOver, bool isMouseDown) override;
|
||||
/** @internal */
|
||||
void contentAreaChanged (const Rectangle<int>&) override;
|
||||
/** @internal */
|
||||
void buttonStateChanged() override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void enablementChanged() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
std::unique_ptr<Drawable> normalImage, toggledOnImage;
|
||||
Drawable* currentImage = nullptr;
|
||||
|
||||
void updateDrawable();
|
||||
Drawable* getImageToUse() const;
|
||||
void setCurrentImage (Drawable*);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToolbarButton)
|
||||
};
|
||||
|
||||
} // namespace juce
|
90
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandID.h
vendored
Normal file
90
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandID.h
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** A type used to hold the unique ID for an application command.
|
||||
|
||||
This is a numeric type, so it can be stored as an integer.
|
||||
|
||||
@see ApplicationCommandInfo, ApplicationCommandManager,
|
||||
ApplicationCommandTarget, KeyPressMappingSet
|
||||
*/
|
||||
using CommandID = int;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A set of general-purpose application command IDs.
|
||||
|
||||
Because these commands are likely to be used in most apps, they're defined
|
||||
here to help different apps to use the same numeric values for them.
|
||||
|
||||
Of course you don't have to use these, but some of them are used internally by
|
||||
JUCE - e.g. the quit ID is recognised as a command by the JUCEApplication class.
|
||||
|
||||
@see ApplicationCommandInfo, ApplicationCommandManager,
|
||||
ApplicationCommandTarget, KeyPressMappingSet
|
||||
*/
|
||||
namespace StandardApplicationCommandIDs
|
||||
{
|
||||
enum
|
||||
{
|
||||
/** This command ID should be used to send a "Quit the App" command.
|
||||
|
||||
This command is recognised by the JUCEApplication class, so if it is invoked
|
||||
and no other ApplicationCommandTarget handles the event first, the JUCEApplication
|
||||
object will catch it and call JUCEApplicationBase::systemRequestedQuit().
|
||||
*/
|
||||
quit = 0x1001,
|
||||
|
||||
/** The command ID that should be used to send a "Delete" command. */
|
||||
del = 0x1002,
|
||||
|
||||
/** The command ID that should be used to send a "Cut" command. */
|
||||
cut = 0x1003,
|
||||
|
||||
/** The command ID that should be used to send a "Copy to clipboard" command. */
|
||||
copy = 0x1004,
|
||||
|
||||
/** The command ID that should be used to send a "Paste from clipboard" command. */
|
||||
paste = 0x1005,
|
||||
|
||||
/** The command ID that should be used to send a "Select all" command. */
|
||||
selectAll = 0x1006,
|
||||
|
||||
/** The command ID that should be used to send a "Deselect all" command. */
|
||||
deselectAll = 0x1007,
|
||||
|
||||
/** The command ID that should be used to send a "undo" command. */
|
||||
undo = 0x1008,
|
||||
|
||||
/** The command ID that should be used to send a "redo" command. */
|
||||
redo = 0x1009
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace juce
|
66
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.cpp
vendored
Normal file
66
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.cpp
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
ApplicationCommandInfo::ApplicationCommandInfo (const CommandID cid) noexcept
|
||||
: commandID (cid), flags (0)
|
||||
{
|
||||
}
|
||||
|
||||
void ApplicationCommandInfo::setInfo (const String& shortName_,
|
||||
const String& description_,
|
||||
const String& categoryName_,
|
||||
const int flags_) noexcept
|
||||
{
|
||||
shortName = shortName_;
|
||||
description = description_;
|
||||
categoryName = categoryName_;
|
||||
flags = flags_;
|
||||
}
|
||||
|
||||
void ApplicationCommandInfo::setActive (const bool b) noexcept
|
||||
{
|
||||
if (b)
|
||||
flags &= ~isDisabled;
|
||||
else
|
||||
flags |= isDisabled;
|
||||
}
|
||||
|
||||
void ApplicationCommandInfo::setTicked (const bool b) noexcept
|
||||
{
|
||||
if (b)
|
||||
flags |= isTicked;
|
||||
else
|
||||
flags &= ~isTicked;
|
||||
}
|
||||
|
||||
void ApplicationCommandInfo::addDefaultKeypress (const int keyCode, ModifierKeys modifiers) noexcept
|
||||
{
|
||||
defaultKeypresses.add (KeyPress (keyCode, modifiers, 0));
|
||||
}
|
||||
|
||||
} // namespace juce
|
190
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.h
vendored
Normal file
190
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.h
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds information describing an application command.
|
||||
|
||||
This object is used to pass information about a particular command, such as its
|
||||
name, description and other usage flags.
|
||||
|
||||
When an ApplicationCommandTarget is asked to provide information about the commands
|
||||
it can perform, this is the structure gets filled-in to describe each one.
|
||||
|
||||
@see ApplicationCommandTarget, ApplicationCommandTarget::getCommandInfo(),
|
||||
ApplicationCommandManager
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
struct JUCE_API ApplicationCommandInfo
|
||||
{
|
||||
//==============================================================================
|
||||
explicit ApplicationCommandInfo (CommandID commandID) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a number of the structures values at once.
|
||||
|
||||
The meanings of each of the parameters is described below, in the appropriate
|
||||
member variable's description.
|
||||
*/
|
||||
void setInfo (const String& shortName,
|
||||
const String& description,
|
||||
const String& categoryName,
|
||||
int flags) noexcept;
|
||||
|
||||
/** An easy way to set or remove the isDisabled bit in the structure's flags field.
|
||||
|
||||
If isActive is true, the flags member has the isDisabled bit cleared; if isActive
|
||||
is false, the bit is set.
|
||||
*/
|
||||
void setActive (bool isActive) noexcept;
|
||||
|
||||
/** An easy way to set or remove the isTicked bit in the structure's flags field.
|
||||
*/
|
||||
void setTicked (bool isTicked) noexcept;
|
||||
|
||||
/** Handy method for adding a keypress to the defaultKeypresses array.
|
||||
|
||||
This is just so you can write things like:
|
||||
@code
|
||||
myinfo.addDefaultKeypress ('s', ModifierKeys::commandModifier);
|
||||
@endcode
|
||||
instead of
|
||||
@code
|
||||
myinfo.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier));
|
||||
@endcode
|
||||
*/
|
||||
void addDefaultKeypress (int keyCode, ModifierKeys modifiers) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** The command's unique ID number.
|
||||
*/
|
||||
CommandID commandID;
|
||||
|
||||
/** A short name to describe the command.
|
||||
|
||||
This should be suitable for use in menus, on buttons that trigger the command, etc.
|
||||
|
||||
You can use the setInfo() method to quickly set this and some of the command's
|
||||
other properties.
|
||||
*/
|
||||
String shortName;
|
||||
|
||||
/** A longer description of the command.
|
||||
|
||||
This should be suitable for use in contexts such as a KeyMappingEditorComponent or
|
||||
pop-up tooltip describing what the command does.
|
||||
|
||||
You can use the setInfo() method to quickly set this and some of the command's
|
||||
other properties.
|
||||
*/
|
||||
String description;
|
||||
|
||||
/** A named category that the command fits into.
|
||||
|
||||
You can give your commands any category you like, and these will be displayed in
|
||||
contexts such as the KeyMappingEditorComponent, where the category is used to group
|
||||
commands together.
|
||||
|
||||
You can use the setInfo() method to quickly set this and some of the command's
|
||||
other properties.
|
||||
*/
|
||||
String categoryName;
|
||||
|
||||
/** A list of zero or more keypresses that should be used as the default keys for
|
||||
this command.
|
||||
|
||||
Methods such as KeyPressMappingSet::resetToDefaultMappings() will use the keypresses in
|
||||
this list to initialise the default set of key-to-command mappings.
|
||||
|
||||
@see addDefaultKeypress
|
||||
*/
|
||||
Array<KeyPress> defaultKeypresses;
|
||||
|
||||
//==============================================================================
|
||||
/** Flags describing the ways in which this command should be used.
|
||||
|
||||
A bitwise-OR of these values is stored in the ApplicationCommandInfo::flags
|
||||
variable.
|
||||
*/
|
||||
enum CommandFlags
|
||||
{
|
||||
/** Indicates that the command can't currently be performed.
|
||||
|
||||
The ApplicationCommandTarget::getCommandInfo() method must set this flag if it's
|
||||
not currently permissible to perform the command. If the flag is set, then
|
||||
components that trigger the command, e.g. PopupMenu, may choose to grey-out the
|
||||
command or show themselves as not being enabled.
|
||||
|
||||
@see ApplicationCommandInfo::setActive
|
||||
*/
|
||||
isDisabled = 1 << 0,
|
||||
|
||||
/** Indicates that the command should have a tick next to it on a menu.
|
||||
|
||||
If your command is shown on a menu and this is set, it'll show a tick next to
|
||||
it. Other components such as buttons may also use this flag to indicate that it
|
||||
is a value that can be toggled, and is currently in the 'on' state.
|
||||
|
||||
@see ApplicationCommandInfo::setTicked
|
||||
*/
|
||||
isTicked = 1 << 1,
|
||||
|
||||
/** If this flag is present, then when a KeyPressMappingSet invokes the command,
|
||||
it will call the command twice, once on key-down and again on key-up.
|
||||
|
||||
@see ApplicationCommandTarget::InvocationInfo
|
||||
*/
|
||||
wantsKeyUpDownCallbacks = 1 << 2,
|
||||
|
||||
/** If this flag is present, then a KeyMappingEditorComponent will not display the
|
||||
command in its list.
|
||||
*/
|
||||
hiddenFromKeyEditor = 1 << 3,
|
||||
|
||||
/** If this flag is present, then a KeyMappingEditorComponent will display the
|
||||
command in its list, but won't allow the assigned keypress to be changed.
|
||||
*/
|
||||
readOnlyInKeyEditor = 1 << 4,
|
||||
|
||||
/** If this flag is present and the command is invoked from a keypress, then any
|
||||
buttons or menus that are also connected to the command will not flash to
|
||||
indicate that they've been triggered.
|
||||
*/
|
||||
dontTriggerVisualFeedback = 1 << 5
|
||||
};
|
||||
|
||||
/** A bitwise-OR of the values specified in the CommandFlags enum.
|
||||
|
||||
You can use the setInfo() method to quickly set this and some of the command's
|
||||
other properties.
|
||||
*/
|
||||
int flags;
|
||||
};
|
||||
|
||||
} // namespace juce
|
322
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp
vendored
Normal file
322
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp
vendored
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
ApplicationCommandManager::ApplicationCommandManager()
|
||||
{
|
||||
keyMappings.reset (new KeyPressMappingSet (*this));
|
||||
Desktop::getInstance().addFocusChangeListener (this);
|
||||
}
|
||||
|
||||
ApplicationCommandManager::~ApplicationCommandManager()
|
||||
{
|
||||
Desktop::getInstance().removeFocusChangeListener (this);
|
||||
keyMappings.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ApplicationCommandManager::clearCommands()
|
||||
{
|
||||
commands.clear();
|
||||
keyMappings->clearAllKeyPresses();
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::registerCommand (const ApplicationCommandInfo& newCommand)
|
||||
{
|
||||
// zero isn't a valid command ID!
|
||||
jassert (newCommand.commandID != 0);
|
||||
|
||||
// the name isn't optional!
|
||||
jassert (newCommand.shortName.isNotEmpty());
|
||||
|
||||
if (auto* command = getMutableCommandForID (newCommand.commandID))
|
||||
{
|
||||
// Trying to re-register the same command ID with different parameters can often indicate a typo.
|
||||
// This assertion is here because I've found it useful catching some mistakes, but it may also cause
|
||||
// false alarms if you're deliberately updating some flags for a command.
|
||||
jassert (newCommand.shortName == getCommandForID (newCommand.commandID)->shortName
|
||||
&& newCommand.categoryName == getCommandForID (newCommand.commandID)->categoryName
|
||||
&& newCommand.defaultKeypresses == getCommandForID (newCommand.commandID)->defaultKeypresses
|
||||
&& (newCommand.flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor))
|
||||
== (getCommandForID (newCommand.commandID)->flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor)));
|
||||
|
||||
*command = newCommand;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* newInfo = new ApplicationCommandInfo (newCommand);
|
||||
newInfo->flags &= ~ApplicationCommandInfo::isTicked;
|
||||
commands.add (newInfo);
|
||||
|
||||
keyMappings->resetToDefaultMapping (newCommand.commandID);
|
||||
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::registerAllCommandsForTarget (ApplicationCommandTarget* target)
|
||||
{
|
||||
if (target != nullptr)
|
||||
{
|
||||
Array<CommandID> commandIDs;
|
||||
target->getAllCommands (commandIDs);
|
||||
|
||||
for (int i = 0; i < commandIDs.size(); ++i)
|
||||
{
|
||||
ApplicationCommandInfo info (commandIDs.getUnchecked(i));
|
||||
target->getCommandInfo (info.commandID, info);
|
||||
|
||||
registerCommand (info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::removeCommand (const CommandID commandID)
|
||||
{
|
||||
for (int i = commands.size(); --i >= 0;)
|
||||
{
|
||||
if (commands.getUnchecked (i)->commandID == commandID)
|
||||
{
|
||||
commands.remove (i);
|
||||
triggerAsyncUpdate();
|
||||
|
||||
const Array<KeyPress> keys (keyMappings->getKeyPressesAssignedToCommand (commandID));
|
||||
|
||||
for (int j = keys.size(); --j >= 0;)
|
||||
keyMappings->removeKeyPress (keys.getReference (j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::commandStatusChanged()
|
||||
{
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandInfo* ApplicationCommandManager::getMutableCommandForID (CommandID commandID) const noexcept
|
||||
{
|
||||
for (int i = commands.size(); --i >= 0;)
|
||||
if (commands.getUnchecked(i)->commandID == commandID)
|
||||
return commands.getUnchecked(i);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ApplicationCommandInfo* ApplicationCommandManager::getCommandForID (CommandID commandID) const noexcept
|
||||
{
|
||||
return getMutableCommandForID (commandID);
|
||||
}
|
||||
|
||||
String ApplicationCommandManager::getNameOfCommand (CommandID commandID) const noexcept
|
||||
{
|
||||
if (auto* ci = getCommandForID (commandID))
|
||||
return ci->shortName;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
String ApplicationCommandManager::getDescriptionOfCommand (CommandID commandID) const noexcept
|
||||
{
|
||||
if (auto* ci = getCommandForID (commandID))
|
||||
return ci->description.isNotEmpty() ? ci->description
|
||||
: ci->shortName;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
StringArray ApplicationCommandManager::getCommandCategories() const
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
for (int i = 0; i < commands.size(); ++i)
|
||||
s.addIfNotAlreadyThere (commands.getUnchecked(i)->categoryName, false);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Array<CommandID> ApplicationCommandManager::getCommandsInCategory (const String& categoryName) const
|
||||
{
|
||||
Array<CommandID> results;
|
||||
|
||||
for (int i = 0; i < commands.size(); ++i)
|
||||
if (commands.getUnchecked(i)->categoryName == categoryName)
|
||||
results.add (commands.getUnchecked(i)->commandID);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool ApplicationCommandManager::invokeDirectly (CommandID commandID, bool asynchronously)
|
||||
{
|
||||
ApplicationCommandTarget::InvocationInfo info (commandID);
|
||||
info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct;
|
||||
|
||||
return invoke (info, asynchronously);
|
||||
}
|
||||
|
||||
bool ApplicationCommandManager::invoke (const ApplicationCommandTarget::InvocationInfo& inf, bool asynchronously)
|
||||
{
|
||||
// This call isn't thread-safe for use from a non-UI thread without locking the message
|
||||
// manager first..
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
bool ok = false;
|
||||
ApplicationCommandInfo commandInfo (0);
|
||||
|
||||
if (auto* target = getTargetForCommand (inf.commandID, commandInfo))
|
||||
{
|
||||
ApplicationCommandTarget::InvocationInfo info (inf);
|
||||
info.commandFlags = commandInfo.flags;
|
||||
|
||||
sendListenerInvokeCallback (info);
|
||||
ok = target->invoke (info, asynchronously);
|
||||
commandStatusChanged();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* ApplicationCommandManager::getFirstCommandTarget (CommandID)
|
||||
{
|
||||
return firstTarget != nullptr ? firstTarget
|
||||
: findDefaultComponentTarget();
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::setFirstCommandTarget (ApplicationCommandTarget* newTarget) noexcept
|
||||
{
|
||||
firstTarget = newTarget;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget* ApplicationCommandManager::getTargetForCommand (CommandID commandID,
|
||||
ApplicationCommandInfo& upToDateInfo)
|
||||
{
|
||||
auto* target = getFirstCommandTarget (commandID);
|
||||
|
||||
if (target == nullptr)
|
||||
target = JUCEApplication::getInstance();
|
||||
|
||||
if (target != nullptr)
|
||||
target = target->getTargetForCommand (commandID);
|
||||
|
||||
if (target != nullptr)
|
||||
{
|
||||
upToDateInfo.commandID = commandID;
|
||||
target->getCommandInfo (commandID, upToDateInfo);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* ApplicationCommandManager::findTargetForComponent (Component* c)
|
||||
{
|
||||
auto* target = dynamic_cast<ApplicationCommandTarget*> (c);
|
||||
|
||||
if (target == nullptr && c != nullptr)
|
||||
target = c->findParentComponentOfClass<ApplicationCommandTarget>();
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget()
|
||||
{
|
||||
auto* c = Component::getCurrentlyFocusedComponent();
|
||||
|
||||
if (c == nullptr)
|
||||
{
|
||||
if (auto* activeWindow = TopLevelWindow::getActiveTopLevelWindow())
|
||||
{
|
||||
if (auto* peer = activeWindow->getPeer())
|
||||
{
|
||||
c = peer->getLastFocusedSubcomponent();
|
||||
|
||||
if (c == nullptr)
|
||||
c = activeWindow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c == nullptr)
|
||||
{
|
||||
auto& desktop = Desktop::getInstance();
|
||||
|
||||
// getting a bit desperate now: try all desktop comps..
|
||||
for (int i = desktop.getNumComponents(); --i >= 0;)
|
||||
if (auto* component = desktop.getComponent (i))
|
||||
if (isForegroundOrEmbeddedProcess (component))
|
||||
if (auto* peer = component->getPeer())
|
||||
if (auto* target = findTargetForComponent (peer->getLastFocusedSubcomponent()))
|
||||
return target;
|
||||
}
|
||||
|
||||
if (c != nullptr)
|
||||
{
|
||||
// if we're focused on a ResizableWindow, chances are that it's the content
|
||||
// component that really should get the event. And if not, the event will
|
||||
// still be passed up to the top level window anyway, so let's send it to the
|
||||
// content comp.
|
||||
if (auto* resizableWindow = dynamic_cast<ResizableWindow*> (c))
|
||||
if (auto* content = resizableWindow->getContentComponent())
|
||||
c = content;
|
||||
|
||||
if (auto* target = findTargetForComponent (c))
|
||||
return target;
|
||||
}
|
||||
|
||||
return JUCEApplication::getInstance();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ApplicationCommandManager::addListener (ApplicationCommandManagerListener* listener)
|
||||
{
|
||||
listeners.add (listener);
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::removeListener (ApplicationCommandManagerListener* listener)
|
||||
{
|
||||
listeners.remove (listener);
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo& info)
|
||||
{
|
||||
listeners.call ([&] (ApplicationCommandManagerListener& l) { l.applicationCommandInvoked (info); });
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::handleAsyncUpdate()
|
||||
{
|
||||
listeners.call ([] (ApplicationCommandManagerListener& l) { l.applicationCommandListChanged(); });
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::globalFocusChanged (Component*)
|
||||
{
|
||||
commandStatusChanged();
|
||||
}
|
||||
|
||||
} // namespace juce
|
349
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h
vendored
Normal file
349
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h
vendored
Normal file
@ -0,0 +1,349 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
One of these objects holds a list of all the commands your app can perform,
|
||||
and despatches these commands when needed.
|
||||
|
||||
Application commands are a good way to trigger actions in your app, e.g. "Quit",
|
||||
"Copy", "Paste", etc. Menus, buttons and keypresses can all be given commands
|
||||
to invoke automatically, which means you don't have to handle the result of a menu
|
||||
or button click manually. Commands are despatched to ApplicationCommandTarget objects
|
||||
which can choose which events they want to handle.
|
||||
|
||||
This architecture also allows for nested ApplicationCommandTargets, so that for example
|
||||
you could have two different objects, one inside the other, both of which can respond to
|
||||
a "delete" command. Depending on which one has focus, the command will be sent to the
|
||||
appropriate place, regardless of whether it was triggered by a menu, keypress or some other
|
||||
method.
|
||||
|
||||
To set up your app to use commands, you'll need to do the following:
|
||||
|
||||
- Create a global ApplicationCommandManager to hold the list of all possible
|
||||
commands. (This will also manage a set of key-mappings for them).
|
||||
|
||||
- Make some of your UI components (or other objects) inherit from ApplicationCommandTarget.
|
||||
This allows the object to provide a list of commands that it can perform, and
|
||||
to handle them.
|
||||
|
||||
- Register each type of command using ApplicationCommandManager::registerAllCommandsForTarget(),
|
||||
or ApplicationCommandManager::registerCommand().
|
||||
|
||||
- If you want key-presses to trigger your commands, use the ApplicationCommandManager::getKeyMappings()
|
||||
method to access the key-mapper object, which you will need to register as a key-listener
|
||||
in whatever top-level component you're using. See the KeyPressMappingSet class for more help
|
||||
about setting this up.
|
||||
|
||||
- Use methods such as PopupMenu::addCommandItem() or Button::setCommandToTrigger() to
|
||||
cause these commands to be invoked automatically.
|
||||
|
||||
- Commands can be invoked directly by your code using ApplicationCommandManager::invokeDirectly().
|
||||
|
||||
When a command is invoked, the ApplicationCommandManager will try to choose the best
|
||||
ApplicationCommandTarget to receive the specified command. To do this it will use the
|
||||
current keyboard focus to see which component might be interested, and will search the
|
||||
component hierarchy for those that also implement the ApplicationCommandTarget interface.
|
||||
If an ApplicationCommandTarget isn't interested in the command that is being invoked, then
|
||||
the next one in line will be tried (see the ApplicationCommandTarget::getNextCommandTarget()
|
||||
method), and so on until ApplicationCommandTarget::getNextCommandTarget() returns nullptr.
|
||||
At this point if the command still hasn't been performed, it will be passed to the current
|
||||
JUCEApplication object (which is itself an ApplicationCommandTarget).
|
||||
|
||||
To exert some custom control over which ApplicationCommandTarget is chosen to invoke a command,
|
||||
you can override the ApplicationCommandManager::getFirstCommandTarget() method and choose
|
||||
the object yourself.
|
||||
|
||||
@see ApplicationCommandTarget, ApplicationCommandInfo
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ApplicationCommandManager : private AsyncUpdater,
|
||||
private FocusChangeListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an ApplicationCommandManager.
|
||||
|
||||
Once created, you'll need to register all your app's commands with it, using
|
||||
ApplicationCommandManager::registerAllCommandsForTarget() or
|
||||
ApplicationCommandManager::registerCommand().
|
||||
*/
|
||||
ApplicationCommandManager();
|
||||
|
||||
/** Destructor.
|
||||
|
||||
Make sure that you don't delete this if pointers to it are still being used by
|
||||
objects such as PopupMenus or Buttons.
|
||||
*/
|
||||
~ApplicationCommandManager() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Clears the current list of all commands.
|
||||
Note that this will also clear the contents of the KeyPressMappingSet.
|
||||
*/
|
||||
void clearCommands();
|
||||
|
||||
/** Adds a command to the list of registered commands.
|
||||
@see registerAllCommandsForTarget
|
||||
*/
|
||||
void registerCommand (const ApplicationCommandInfo& newCommand);
|
||||
|
||||
/** Adds all the commands that this target publishes to the manager's list.
|
||||
|
||||
This will use ApplicationCommandTarget::getAllCommands() and ApplicationCommandTarget::getCommandInfo()
|
||||
to get details about all the commands that this target can do, and will call
|
||||
registerCommand() to add each one to the manger's list.
|
||||
|
||||
@see registerCommand
|
||||
*/
|
||||
void registerAllCommandsForTarget (ApplicationCommandTarget* target);
|
||||
|
||||
/** Removes the command with a specified ID.
|
||||
Note that this will also remove any key mappings that are mapped to the command.
|
||||
*/
|
||||
void removeCommand (CommandID commandID);
|
||||
|
||||
/** This should be called to tell the manager that one of its registered commands may have changed
|
||||
its active status.
|
||||
|
||||
Because the command manager only finds out whether a command is active or inactive by querying
|
||||
the current ApplicationCommandTarget, this is used to tell it that things may have changed. It
|
||||
allows things like buttons to update their enablement, etc.
|
||||
|
||||
This method will cause an asynchronous call to ApplicationCommandManagerListener::applicationCommandListChanged()
|
||||
for any registered listeners.
|
||||
*/
|
||||
void commandStatusChanged();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of commands that have been registered.
|
||||
@see registerCommand
|
||||
*/
|
||||
int getNumCommands() const noexcept { return commands.size(); }
|
||||
|
||||
/** Returns the details about one of the registered commands.
|
||||
The index is between 0 and (getNumCommands() - 1).
|
||||
*/
|
||||
const ApplicationCommandInfo* getCommandForIndex (int index) const noexcept { return commands [index]; }
|
||||
|
||||
/** Returns the details about a given command ID.
|
||||
|
||||
This will search the list of registered commands for one with the given command
|
||||
ID number, and return its associated info. If no matching command is found, this
|
||||
will return nullptr.
|
||||
*/
|
||||
const ApplicationCommandInfo* getCommandForID (CommandID commandID) const noexcept;
|
||||
|
||||
/** Returns the name field for a command.
|
||||
|
||||
An empty string is returned if no command with this ID has been registered.
|
||||
@see getDescriptionOfCommand
|
||||
*/
|
||||
String getNameOfCommand (CommandID commandID) const noexcept;
|
||||
|
||||
/** Returns the description field for a command.
|
||||
|
||||
An empty string is returned if no command with this ID has been registered. If the
|
||||
command has no description, this will return its short name field instead.
|
||||
|
||||
@see getNameOfCommand
|
||||
*/
|
||||
String getDescriptionOfCommand (CommandID commandID) const noexcept;
|
||||
|
||||
/** Returns the list of categories.
|
||||
|
||||
This will go through all registered commands, and return a list of all the distinct
|
||||
categoryName values from their ApplicationCommandInfo structure.
|
||||
|
||||
@see getCommandsInCategory()
|
||||
*/
|
||||
StringArray getCommandCategories() const;
|
||||
|
||||
/** Returns a list of all the command UIDs in a particular category.
|
||||
@see getCommandCategories()
|
||||
*/
|
||||
Array<CommandID> getCommandsInCategory (const String& categoryName) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the manager's internal set of key mappings.
|
||||
|
||||
This object can be used to edit the keypresses. To actually link this object up
|
||||
to invoke commands when a key is pressed, see the comments for the KeyPressMappingSet
|
||||
class.
|
||||
|
||||
@see KeyPressMappingSet
|
||||
*/
|
||||
KeyPressMappingSet* getKeyMappings() const noexcept { return keyMappings.get(); }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Invokes the given command directly, sending it to the default target.
|
||||
This is just an easy way to call invoke() without having to fill out the InvocationInfo
|
||||
structure.
|
||||
*/
|
||||
bool invokeDirectly (CommandID commandID, bool asynchronously);
|
||||
|
||||
/** Sends a command to the default target.
|
||||
|
||||
This will choose a target using getFirstCommandTarget(), and send the specified command
|
||||
to it using the ApplicationCommandTarget::invoke() method. This means that if the
|
||||
first target can't handle the command, it will be passed on to targets further down the
|
||||
chain (see ApplicationCommandTarget::invoke() for more info).
|
||||
|
||||
@param invocationInfo this must be correctly filled-in, describing the context for
|
||||
the invocation.
|
||||
@param asynchronously if false, the command will be performed before this method returns.
|
||||
If true, a message will be posted so that the command will be performed
|
||||
later on the message thread, and this method will return immediately.
|
||||
|
||||
@see ApplicationCommandTarget::invoke
|
||||
*/
|
||||
bool invoke (const ApplicationCommandTarget::InvocationInfo& invocationInfo,
|
||||
bool asynchronously);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Chooses the ApplicationCommandTarget to which a command should be sent.
|
||||
|
||||
Whenever the manager needs to know which target a command should be sent to, it calls
|
||||
this method to determine the first one to try.
|
||||
|
||||
By default, this method will return the target that was set by calling setFirstCommandTarget().
|
||||
If no target is set, it will return the result of findDefaultComponentTarget().
|
||||
|
||||
If you need to make sure all commands go via your own custom target, then you can
|
||||
either use setFirstCommandTarget() to specify a single target, or override this method
|
||||
if you need more complex logic to choose one.
|
||||
|
||||
It may return nullptr if no targets are available.
|
||||
|
||||
@see getTargetForCommand, invoke, invokeDirectly
|
||||
*/
|
||||
virtual ApplicationCommandTarget* getFirstCommandTarget (CommandID commandID);
|
||||
|
||||
/** Sets a target to be returned by getFirstCommandTarget().
|
||||
|
||||
If this is set to nullptr, then getFirstCommandTarget() will by default return the
|
||||
result of findDefaultComponentTarget().
|
||||
|
||||
If you use this to set a target, make sure you call setFirstCommandTarget(nullptr)
|
||||
before deleting the target object.
|
||||
*/
|
||||
void setFirstCommandTarget (ApplicationCommandTarget* newTarget) noexcept;
|
||||
|
||||
/** Tries to find the best target to use to perform a given command.
|
||||
|
||||
This will call getFirstCommandTarget() to find the preferred target, and will
|
||||
check whether that target can handle the given command. If it can't, then it'll use
|
||||
ApplicationCommandTarget::getNextCommandTarget() to find the next one to try, and
|
||||
so on until no more are available.
|
||||
|
||||
If no targets are found that can perform the command, this method will return nullptr.
|
||||
|
||||
If a target is found, then it will get the target to fill-in the upToDateInfo
|
||||
structure with the latest info about that command, so that the caller can see
|
||||
whether the command is disabled, ticked, etc.
|
||||
*/
|
||||
ApplicationCommandTarget* getTargetForCommand (CommandID commandID,
|
||||
ApplicationCommandInfo& upToDateInfo);
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a listener that will be called when various events occur. */
|
||||
void addListener (ApplicationCommandManagerListener* listener);
|
||||
|
||||
/** Deregisters a previously-added listener. */
|
||||
void removeListener (ApplicationCommandManagerListener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Looks for a suitable command target based on which Components have the keyboard focus.
|
||||
|
||||
This is used by the default implementation of ApplicationCommandTarget::getFirstCommandTarget(),
|
||||
but is exposed here in case it's useful.
|
||||
|
||||
It tries to pick the best ApplicationCommandTarget by looking at focused components, top level
|
||||
windows, etc., and using the findTargetForComponent() method.
|
||||
*/
|
||||
static ApplicationCommandTarget* findDefaultComponentTarget();
|
||||
|
||||
/** Examines this component and all its parents in turn, looking for the first one
|
||||
which is an ApplicationCommandTarget.
|
||||
|
||||
Returns the first ApplicationCommandTarget that it finds, or nullptr if none of them
|
||||
implement that class.
|
||||
*/
|
||||
static ApplicationCommandTarget* findTargetForComponent (Component*);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
OwnedArray<ApplicationCommandInfo> commands;
|
||||
ListenerList<ApplicationCommandManagerListener> listeners;
|
||||
std::unique_ptr<KeyPressMappingSet> keyMappings;
|
||||
ApplicationCommandTarget* firstTarget = nullptr;
|
||||
|
||||
void sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo&);
|
||||
void handleAsyncUpdate() override;
|
||||
void globalFocusChanged (Component*) override;
|
||||
ApplicationCommandInfo* getMutableCommandForID (CommandID) const noexcept;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandManager)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A listener that receives callbacks from an ApplicationCommandManager when
|
||||
commands are invoked or the command list is changed.
|
||||
|
||||
@see ApplicationCommandManager::addListener, ApplicationCommandManager::removeListener
|
||||
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ApplicationCommandManagerListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
virtual ~ApplicationCommandManagerListener() = default;
|
||||
|
||||
/** Called when an app command is about to be invoked. */
|
||||
virtual void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo&) = 0;
|
||||
|
||||
/** Called when commands are registered or deregistered from the
|
||||
command manager, or when commands are made active or inactive.
|
||||
|
||||
Note that if you're using this to watch for changes to whether a command is disabled,
|
||||
you'll need to make sure that ApplicationCommandManager::commandStatusChanged() is called
|
||||
whenever the status of your command might have changed.
|
||||
*/
|
||||
virtual void applicationCommandListChanged() = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
186
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.cpp
vendored
Normal file
186
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.cpp
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
class ApplicationCommandTarget::CommandMessage : public MessageManager::MessageBase
|
||||
{
|
||||
public:
|
||||
CommandMessage (ApplicationCommandTarget* const target, const InvocationInfo& inf)
|
||||
: owner (target), info (inf)
|
||||
{
|
||||
}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
if (ApplicationCommandTarget* const target = owner)
|
||||
target->tryToInvoke (info, false);
|
||||
}
|
||||
|
||||
private:
|
||||
WeakReference<ApplicationCommandTarget> owner;
|
||||
const InvocationInfo info;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (CommandMessage)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget::ApplicationCommandTarget() {}
|
||||
ApplicationCommandTarget::~ApplicationCommandTarget() {}
|
||||
|
||||
//==============================================================================
|
||||
bool ApplicationCommandTarget::tryToInvoke (const InvocationInfo& info, const bool async)
|
||||
{
|
||||
if (isCommandActive (info.commandID))
|
||||
{
|
||||
if (async)
|
||||
{
|
||||
(new CommandMessage (this, info))->post();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (perform (info))
|
||||
return true;
|
||||
|
||||
// Hmm.. your target claimed that it could perform this command, but failed to do so.
|
||||
// If it can't do it at the moment for some reason, it should clear the 'isActive' flag
|
||||
// when it returns the command's info.
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget* ApplicationCommandTarget::findFirstTargetParentComponent()
|
||||
{
|
||||
if (Component* const c = dynamic_cast<Component*> (this))
|
||||
return c->findParentComponentOfClass<ApplicationCommandTarget>();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget* ApplicationCommandTarget::getTargetForCommand (const CommandID commandID)
|
||||
{
|
||||
ApplicationCommandTarget* target = this;
|
||||
int depth = 0;
|
||||
|
||||
while (target != nullptr)
|
||||
{
|
||||
Array<CommandID> commandIDs;
|
||||
target->getAllCommands (commandIDs);
|
||||
|
||||
if (commandIDs.contains (commandID))
|
||||
return target;
|
||||
|
||||
target = target->getNextCommandTarget();
|
||||
|
||||
++depth;
|
||||
jassert (depth < 100); // could be a recursive command chain??
|
||||
jassert (target != this); // definitely a recursive command chain!
|
||||
|
||||
if (depth > 100 || target == this)
|
||||
break;
|
||||
}
|
||||
|
||||
if (target == nullptr)
|
||||
{
|
||||
target = JUCEApplication::getInstance();
|
||||
|
||||
if (target != nullptr)
|
||||
{
|
||||
Array<CommandID> commandIDs;
|
||||
target->getAllCommands (commandIDs);
|
||||
|
||||
if (commandIDs.contains (commandID))
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ApplicationCommandTarget::isCommandActive (const CommandID commandID)
|
||||
{
|
||||
ApplicationCommandInfo info (commandID);
|
||||
info.flags = ApplicationCommandInfo::isDisabled;
|
||||
|
||||
getCommandInfo (commandID, info);
|
||||
|
||||
return (info.flags & ApplicationCommandInfo::isDisabled) == 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool ApplicationCommandTarget::invoke (const InvocationInfo& info, const bool async)
|
||||
{
|
||||
ApplicationCommandTarget* target = this;
|
||||
int depth = 0;
|
||||
|
||||
while (target != nullptr)
|
||||
{
|
||||
if (target->tryToInvoke (info, async))
|
||||
return true;
|
||||
|
||||
target = target->getNextCommandTarget();
|
||||
|
||||
++depth;
|
||||
jassert (depth < 100); // could be a recursive command chain??
|
||||
jassert (target != this); // definitely a recursive command chain!
|
||||
|
||||
if (depth > 100 || target == this)
|
||||
break;
|
||||
}
|
||||
|
||||
if (target == nullptr)
|
||||
{
|
||||
target = JUCEApplication::getInstance();
|
||||
|
||||
if (target != nullptr)
|
||||
return target->tryToInvoke (info, async);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ApplicationCommandTarget::invokeDirectly (const CommandID commandID, const bool asynchronously)
|
||||
{
|
||||
ApplicationCommandTarget::InvocationInfo info (commandID);
|
||||
info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct;
|
||||
|
||||
return invoke (info, asynchronously);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget::InvocationInfo::InvocationInfo (const CommandID command)
|
||||
: commandID (command),
|
||||
commandFlags (0),
|
||||
invocationMethod (direct),
|
||||
originatingComponent (nullptr),
|
||||
isKeyDown (false),
|
||||
millisecsSinceKeyPressed (0)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace juce
|
244
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.h
vendored
Normal file
244
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.h
vendored
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A command target publishes a list of command IDs that it can perform.
|
||||
|
||||
An ApplicationCommandManager despatches commands to targets, which must be
|
||||
able to provide information about what commands they can handle.
|
||||
|
||||
To create a target, you'll need to inherit from this class, implementing all of
|
||||
its pure virtual methods.
|
||||
|
||||
For info about how a target is chosen to receive a command, see
|
||||
ApplicationCommandManager::getFirstCommandTarget().
|
||||
|
||||
@see ApplicationCommandManager, ApplicationCommandInfo
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ApplicationCommandTarget
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a command target. */
|
||||
ApplicationCommandTarget();
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ApplicationCommandTarget();
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains contextual details about the invocation of a command.
|
||||
*/
|
||||
struct JUCE_API InvocationInfo
|
||||
{
|
||||
//==============================================================================
|
||||
InvocationInfo (const CommandID commandID);
|
||||
|
||||
//==============================================================================
|
||||
/** The UID of the command that should be performed. */
|
||||
CommandID commandID;
|
||||
|
||||
/** The command's flags.
|
||||
See ApplicationCommandInfo for a description of these flag values.
|
||||
*/
|
||||
int commandFlags;
|
||||
|
||||
//==============================================================================
|
||||
/** The types of context in which the command might be called. */
|
||||
enum InvocationMethod
|
||||
{
|
||||
direct = 0, /**< The command is being invoked directly by a piece of code. */
|
||||
fromKeyPress, /**< The command is being invoked by a key-press. */
|
||||
fromMenu, /**< The command is being invoked by a menu selection. */
|
||||
fromButton /**< The command is being invoked by a button click. */
|
||||
};
|
||||
|
||||
/** The type of event that triggered this command. */
|
||||
InvocationMethod invocationMethod;
|
||||
|
||||
//==============================================================================
|
||||
/** If triggered by a keypress or menu, this will be the component that had the
|
||||
keyboard focus at the time.
|
||||
|
||||
If triggered by a button, it may be set to that component, or it may be null.
|
||||
*/
|
||||
Component* originatingComponent;
|
||||
|
||||
//==============================================================================
|
||||
/** The keypress that was used to invoke it.
|
||||
|
||||
Note that this will be an invalid keypress if the command was invoked
|
||||
by some other means than a keyboard shortcut.
|
||||
*/
|
||||
KeyPress keyPress;
|
||||
|
||||
/** True if the callback is being invoked when the key is pressed,
|
||||
false if the key is being released.
|
||||
|
||||
@see KeyPressMappingSet::addCommand()
|
||||
*/
|
||||
bool isKeyDown;
|
||||
|
||||
/** If the key is being released, this indicates how long it had been held
|
||||
down for.
|
||||
|
||||
(Only relevant if isKeyDown is false.)
|
||||
*/
|
||||
int millisecsSinceKeyPressed;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This must return the next target to try after this one.
|
||||
|
||||
When a command is being sent, and the first target can't handle
|
||||
that command, this method is used to determine the next target that should
|
||||
be tried.
|
||||
|
||||
It may return nullptr if it doesn't know of another target.
|
||||
|
||||
If your target is a Component, you would usually use the findFirstTargetParentComponent()
|
||||
method to return a parent component that might want to handle it.
|
||||
|
||||
@see invoke
|
||||
*/
|
||||
virtual ApplicationCommandTarget* getNextCommandTarget() = 0;
|
||||
|
||||
/** This must return a complete list of commands that this target can handle.
|
||||
|
||||
Your target should add all the command IDs that it handles to the array that is
|
||||
passed-in.
|
||||
*/
|
||||
virtual void getAllCommands (Array<CommandID>& commands) = 0;
|
||||
|
||||
/** This must provide details about one of the commands that this target can perform.
|
||||
|
||||
This will be called with one of the command IDs that the target provided in its
|
||||
getAllCommands() methods.
|
||||
|
||||
It should fill-in all appropriate fields of the ApplicationCommandInfo structure with
|
||||
suitable information about the command. (The commandID field will already have been filled-in
|
||||
by the caller).
|
||||
|
||||
The easiest way to set the info is using the ApplicationCommandInfo::setInfo() method to
|
||||
set all the fields at once.
|
||||
|
||||
If the command is currently inactive for some reason, this method must use
|
||||
ApplicationCommandInfo::setActive() to make that clear, (or it should set the isDisabled
|
||||
bit of the ApplicationCommandInfo::flags field).
|
||||
|
||||
Any default key-presses for the command should be appended to the
|
||||
ApplicationCommandInfo::defaultKeypresses field.
|
||||
|
||||
Note that if you change something that affects the status of the commands
|
||||
that would be returned by this method (e.g. something that makes some commands
|
||||
active or inactive), you should call ApplicationCommandManager::commandStatusChanged()
|
||||
to cause the manager to refresh its status.
|
||||
*/
|
||||
virtual void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) = 0;
|
||||
|
||||
/** This must actually perform the specified command.
|
||||
|
||||
If this target is able to perform the command specified by the commandID field of the
|
||||
InvocationInfo structure, then it should do so, and must return true.
|
||||
|
||||
If it can't handle this command, it should return false, which tells the caller to pass
|
||||
the command on to the next target in line.
|
||||
|
||||
@see invoke, ApplicationCommandManager::invoke
|
||||
*/
|
||||
virtual bool perform (const InvocationInfo& info) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Makes this target invoke a command.
|
||||
|
||||
Your code can call this method to invoke a command on this target, but normally
|
||||
you'd call it indirectly via ApplicationCommandManager::invoke() or
|
||||
ApplicationCommandManager::invokeDirectly().
|
||||
|
||||
If this target can perform the given command, it will call its perform() method to
|
||||
do so. If not, then getNextCommandTarget() will be used to determine the next target
|
||||
to try, and the command will be passed along to it.
|
||||
|
||||
@param invocationInfo this must be correctly filled-in, describing the context for
|
||||
the invocation.
|
||||
@param asynchronously if false, the command will be performed before this method returns.
|
||||
If true, a message will be posted so that the command will be performed
|
||||
later on the message thread, and this method will return immediately.
|
||||
@see perform, ApplicationCommandManager::invoke
|
||||
*/
|
||||
bool invoke (const InvocationInfo& invocationInfo,
|
||||
const bool asynchronously);
|
||||
|
||||
/** Invokes a given command directly on this target.
|
||||
|
||||
This is just an easy way to call invoke() without having to fill out the InvocationInfo
|
||||
structure.
|
||||
*/
|
||||
bool invokeDirectly (const CommandID commandID,
|
||||
const bool asynchronously);
|
||||
|
||||
//==============================================================================
|
||||
/** Searches this target and all subsequent ones for the first one that can handle
|
||||
the specified command.
|
||||
|
||||
This will use getNextCommandTarget() to determine the chain of targets to try
|
||||
after this one.
|
||||
*/
|
||||
ApplicationCommandTarget* getTargetForCommand (const CommandID commandID);
|
||||
|
||||
/** Checks whether this command can currently be performed by this target.
|
||||
|
||||
This will return true only if a call to getCommandInfo() doesn't set the
|
||||
isDisabled flag to indicate that the command is inactive.
|
||||
*/
|
||||
bool isCommandActive (const CommandID commandID);
|
||||
|
||||
/** If this object is a Component, this method will search upwards in its current
|
||||
UI hierarchy for the next parent component that implements the
|
||||
ApplicationCommandTarget class.
|
||||
|
||||
If your target is a Component, this is a very handy method to use in your
|
||||
getNextCommandTarget() implementation.
|
||||
*/
|
||||
ApplicationCommandTarget* findFirstTargetParentComponent();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class CommandMessage;
|
||||
friend class CommandMessage;
|
||||
|
||||
bool tryToInvoke (const InvocationInfo&, bool async);
|
||||
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (ApplicationCommandTarget)
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandTarget)
|
||||
};
|
||||
|
||||
} // namespace juce
|
418
deps/juce/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp
vendored
Normal file
418
deps/juce/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp
vendored
Normal file
@ -0,0 +1,418 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
KeyPressMappingSet::KeyPressMappingSet (ApplicationCommandManager& cm)
|
||||
: commandManager (cm)
|
||||
{
|
||||
Desktop::getInstance().addFocusChangeListener (this);
|
||||
}
|
||||
|
||||
KeyPressMappingSet::KeyPressMappingSet (const KeyPressMappingSet& other)
|
||||
: KeyListener(), ChangeBroadcaster(), FocusChangeListener(), commandManager (other.commandManager)
|
||||
{
|
||||
Desktop::getInstance().addFocusChangeListener (this);
|
||||
}
|
||||
|
||||
KeyPressMappingSet::~KeyPressMappingSet()
|
||||
{
|
||||
Desktop::getInstance().removeFocusChangeListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Array<KeyPress> KeyPressMappingSet::getKeyPressesAssignedToCommand (const CommandID commandID) const
|
||||
{
|
||||
for (int i = 0; i < mappings.size(); ++i)
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
return mappings.getUnchecked (i)->keypresses;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::addKeyPress (const CommandID commandID, const KeyPress& newKeyPress, int insertIndex)
|
||||
{
|
||||
// If you specify an upper-case letter but no shift key, how is the user supposed to press it!?
|
||||
// Stick to lower-case letters when defining a keypress, to avoid ambiguity.
|
||||
jassert (! (CharacterFunctions::isUpperCase (newKeyPress.getTextCharacter())
|
||||
&& ! newKeyPress.getModifiers().isShiftDown()));
|
||||
|
||||
if (findCommandForKeyPress (newKeyPress) != commandID)
|
||||
{
|
||||
if (newKeyPress.isValid())
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
{
|
||||
mappings.getUnchecked(i)->keypresses.insert (insertIndex, newKeyPress);
|
||||
|
||||
sendChangeMessage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID))
|
||||
{
|
||||
CommandMapping* const cm = new CommandMapping();
|
||||
cm->commandID = commandID;
|
||||
cm->keypresses.add (newKeyPress);
|
||||
cm->wantsKeyUpDownCallbacks = (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) != 0;
|
||||
|
||||
mappings.add (cm);
|
||||
sendChangeMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If you hit this, you're trying to attach a keypress to a command ID that
|
||||
// doesn't exist, so the key is not being attached.
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void addKeyPresses (KeyPressMappingSet& set, const ApplicationCommandInfo* const ci)
|
||||
{
|
||||
for (int j = 0; j < ci->defaultKeypresses.size(); ++j)
|
||||
set.addKeyPress (ci->commandID, ci->defaultKeypresses.getReference (j));
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::resetToDefaultMappings()
|
||||
{
|
||||
mappings.clear();
|
||||
|
||||
for (int i = 0; i < commandManager.getNumCommands(); ++i)
|
||||
addKeyPresses (*this, commandManager.getCommandForIndex (i));
|
||||
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::resetToDefaultMapping (const CommandID commandID)
|
||||
{
|
||||
clearAllKeyPresses (commandID);
|
||||
|
||||
if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID))
|
||||
addKeyPresses (*this, ci);
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::clearAllKeyPresses()
|
||||
{
|
||||
if (mappings.size() > 0)
|
||||
{
|
||||
sendChangeMessage();
|
||||
mappings.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::clearAllKeyPresses (const CommandID commandID)
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
{
|
||||
mappings.remove (i);
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::removeKeyPress (const KeyPress& keypress)
|
||||
{
|
||||
if (keypress.isValid())
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
CommandMapping& cm = *mappings.getUnchecked(i);
|
||||
|
||||
for (int j = cm.keypresses.size(); --j >= 0;)
|
||||
{
|
||||
if (keypress == cm.keypresses [j])
|
||||
{
|
||||
cm.keypresses.remove (j);
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::removeKeyPress (const CommandID commandID, const int keyPressIndex)
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
{
|
||||
mappings.getUnchecked(i)->keypresses.remove (keyPressIndex);
|
||||
sendChangeMessage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
CommandID KeyPressMappingSet::findCommandForKeyPress (const KeyPress& keyPress) const noexcept
|
||||
{
|
||||
for (int i = 0; i < mappings.size(); ++i)
|
||||
if (mappings.getUnchecked(i)->keypresses.contains (keyPress))
|
||||
return mappings.getUnchecked(i)->commandID;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool KeyPressMappingSet::containsMapping (const CommandID commandID, const KeyPress& keyPress) const noexcept
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
return mappings.getUnchecked(i)->keypresses.contains (keyPress);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::invokeCommand (const CommandID commandID,
|
||||
const KeyPress& key,
|
||||
const bool isKeyDown,
|
||||
const int millisecsSinceKeyPressed,
|
||||
Component* const originatingComponent) const
|
||||
{
|
||||
ApplicationCommandTarget::InvocationInfo info (commandID);
|
||||
|
||||
info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromKeyPress;
|
||||
info.isKeyDown = isKeyDown;
|
||||
info.keyPress = key;
|
||||
info.millisecsSinceKeyPressed = millisecsSinceKeyPressed;
|
||||
info.originatingComponent = originatingComponent;
|
||||
|
||||
commandManager.invoke (info, false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool KeyPressMappingSet::restoreFromXml (const XmlElement& xmlVersion)
|
||||
{
|
||||
if (xmlVersion.hasTagName ("KEYMAPPINGS"))
|
||||
{
|
||||
if (xmlVersion.getBoolAttribute ("basedOnDefaults", true))
|
||||
{
|
||||
// if the XML was created as a set of differences from the default mappings,
|
||||
// (i.e. by calling createXml (true)), then we need to first restore the defaults.
|
||||
resetToDefaultMappings();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the XML was created calling createXml (false), then we need to clear all
|
||||
// the keys and treat the xml as describing the entire set of mappings.
|
||||
clearAllKeyPresses();
|
||||
}
|
||||
|
||||
for (auto* map : xmlVersion.getChildIterator())
|
||||
{
|
||||
const CommandID commandId = map->getStringAttribute ("commandId").getHexValue32();
|
||||
|
||||
if (commandId != 0)
|
||||
{
|
||||
auto key = KeyPress::createFromDescription (map->getStringAttribute ("key"));
|
||||
|
||||
if (map->hasTagName ("MAPPING"))
|
||||
{
|
||||
addKeyPress (commandId, key);
|
||||
}
|
||||
else if (map->hasTagName ("UNMAPPING"))
|
||||
{
|
||||
for (auto& m : mappings)
|
||||
if (m->commandID == commandId)
|
||||
m->keypresses.removeAllInstancesOf (key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlElement> KeyPressMappingSet::createXml (const bool saveDifferencesFromDefaultSet) const
|
||||
{
|
||||
std::unique_ptr<KeyPressMappingSet> defaultSet;
|
||||
|
||||
if (saveDifferencesFromDefaultSet)
|
||||
{
|
||||
defaultSet = std::make_unique<KeyPressMappingSet> (commandManager);
|
||||
defaultSet->resetToDefaultMappings();
|
||||
}
|
||||
|
||||
auto doc = std::make_unique<XmlElement> ("KEYMAPPINGS");
|
||||
|
||||
doc->setAttribute ("basedOnDefaults", saveDifferencesFromDefaultSet);
|
||||
|
||||
for (int i = 0; i < mappings.size(); ++i)
|
||||
{
|
||||
auto& cm = *mappings.getUnchecked(i);
|
||||
|
||||
for (int j = 0; j < cm.keypresses.size(); ++j)
|
||||
{
|
||||
if (defaultSet == nullptr
|
||||
|| ! defaultSet->containsMapping (cm.commandID, cm.keypresses.getReference (j)))
|
||||
{
|
||||
auto map = doc->createNewChildElement ("MAPPING");
|
||||
|
||||
map->setAttribute ("commandId", String::toHexString ((int) cm.commandID));
|
||||
map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID));
|
||||
map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultSet != nullptr)
|
||||
{
|
||||
for (int i = 0; i < defaultSet->mappings.size(); ++i)
|
||||
{
|
||||
auto& cm = *defaultSet->mappings.getUnchecked(i);
|
||||
|
||||
for (int j = 0; j < cm.keypresses.size(); ++j)
|
||||
{
|
||||
if (! containsMapping (cm.commandID, cm.keypresses.getReference (j)))
|
||||
{
|
||||
auto map = doc->createNewChildElement ("UNMAPPING");
|
||||
|
||||
map->setAttribute ("commandId", String::toHexString ((int) cm.commandID));
|
||||
map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID));
|
||||
map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool KeyPressMappingSet::keyPressed (const KeyPress& key, Component* const originatingComponent)
|
||||
{
|
||||
bool commandWasDisabled = false;
|
||||
|
||||
for (int i = 0; i < mappings.size(); ++i)
|
||||
{
|
||||
CommandMapping& cm = *mappings.getUnchecked(i);
|
||||
|
||||
if (cm.keypresses.contains (key))
|
||||
{
|
||||
if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (cm.commandID))
|
||||
{
|
||||
if ((ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) == 0)
|
||||
{
|
||||
ApplicationCommandInfo info (0);
|
||||
|
||||
if (commandManager.getTargetForCommand (cm.commandID, info) != nullptr)
|
||||
{
|
||||
if ((info.flags & ApplicationCommandInfo::isDisabled) == 0)
|
||||
{
|
||||
invokeCommand (cm.commandID, key, true, 0, originatingComponent);
|
||||
return true;
|
||||
}
|
||||
|
||||
commandWasDisabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (originatingComponent != nullptr && commandWasDisabled)
|
||||
originatingComponent->getLookAndFeel().playAlertSound();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KeyPressMappingSet::keyStateChanged (const bool /*isKeyDown*/, Component* originatingComponent)
|
||||
{
|
||||
bool used = false;
|
||||
const uint32 now = Time::getMillisecondCounter();
|
||||
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
CommandMapping& cm = *mappings.getUnchecked(i);
|
||||
|
||||
if (cm.wantsKeyUpDownCallbacks)
|
||||
{
|
||||
for (int j = cm.keypresses.size(); --j >= 0;)
|
||||
{
|
||||
const KeyPress key (cm.keypresses.getReference (j));
|
||||
const bool isDown = key.isCurrentlyDown();
|
||||
|
||||
int keyPressEntryIndex = 0;
|
||||
bool wasDown = false;
|
||||
|
||||
for (int k = keysDown.size(); --k >= 0;)
|
||||
{
|
||||
if (key == keysDown.getUnchecked(k)->key)
|
||||
{
|
||||
keyPressEntryIndex = k;
|
||||
wasDown = true;
|
||||
used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDown != wasDown)
|
||||
{
|
||||
int millisecs = 0;
|
||||
|
||||
if (isDown)
|
||||
{
|
||||
KeyPressTime* const k = new KeyPressTime();
|
||||
k->key = key;
|
||||
k->timeWhenPressed = now;
|
||||
|
||||
keysDown.add (k);
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint32 pressTime = keysDown.getUnchecked (keyPressEntryIndex)->timeWhenPressed;
|
||||
|
||||
if (now > pressTime)
|
||||
millisecs = (int) (now - pressTime);
|
||||
|
||||
keysDown.remove (keyPressEntryIndex);
|
||||
}
|
||||
|
||||
invokeCommand (cm.commandID, key, isDown, millisecs, originatingComponent);
|
||||
used = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::globalFocusChanged (Component* focusedComponent)
|
||||
{
|
||||
if (focusedComponent != nullptr)
|
||||
focusedComponent->keyStateChanged (false);
|
||||
}
|
||||
|
||||
} // namespace juce
|
244
deps/juce/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h
vendored
Normal file
244
deps/juce/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h
vendored
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 and edits a list of keypresses, which it uses to invoke the appropriate
|
||||
command in an ApplicationCommandManager.
|
||||
|
||||
Normally, you won't actually create a KeyPressMappingSet directly, because
|
||||
each ApplicationCommandManager contains its own KeyPressMappingSet, so typically
|
||||
you'd create yourself an ApplicationCommandManager, and call its
|
||||
ApplicationCommandManager::getKeyMappings() method to get a pointer to its
|
||||
KeyPressMappingSet.
|
||||
|
||||
For one of these to actually use keypresses, you'll need to add it as a KeyListener
|
||||
to the top-level component for which you want to handle keystrokes. So for example:
|
||||
|
||||
@code
|
||||
class MyMainWindow : public Component
|
||||
{
|
||||
ApplicationCommandManager* myCommandManager;
|
||||
|
||||
public:
|
||||
MyMainWindow()
|
||||
{
|
||||
myCommandManager = new ApplicationCommandManager();
|
||||
|
||||
// first, make sure the command manager has registered all the commands that its
|
||||
// targets can perform..
|
||||
myCommandManager->registerAllCommandsForTarget (myCommandTarget1);
|
||||
myCommandManager->registerAllCommandsForTarget (myCommandTarget2);
|
||||
|
||||
// this will use the command manager to initialise the KeyPressMappingSet with
|
||||
// the default keypresses that were specified when the targets added their commands
|
||||
// to the manager.
|
||||
myCommandManager->getKeyMappings()->resetToDefaultMappings();
|
||||
|
||||
// having set up the default key-mappings, you might now want to load the last set
|
||||
// of mappings that the user configured.
|
||||
myCommandManager->getKeyMappings()->restoreFromXml (lastSavedKeyMappingsXML);
|
||||
|
||||
// Now tell our top-level window to send any keypresses that arrive to the
|
||||
// KeyPressMappingSet, which will use them to invoke the appropriate commands.
|
||||
addKeyListener (myCommandManager->getKeyMappings());
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
@endcode
|
||||
|
||||
KeyPressMappingSet derives from ChangeBroadcaster so that interested parties can
|
||||
register to be told when a command or mapping is added, removed, etc.
|
||||
|
||||
There's also a UI component called KeyMappingEditorComponent that can be used
|
||||
to easily edit the key mappings.
|
||||
|
||||
@see Component::addKeyListener(), KeyMappingEditorComponent, ApplicationCommandManager
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API KeyPressMappingSet : public KeyListener,
|
||||
public ChangeBroadcaster,
|
||||
private FocusChangeListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a KeyPressMappingSet for a given command manager.
|
||||
|
||||
Normally, you won't actually create a KeyPressMappingSet directly, because
|
||||
each ApplicationCommandManager contains its own KeyPressMappingSet, so the
|
||||
best thing to do is to create your ApplicationCommandManager, and use the
|
||||
ApplicationCommandManager::getKeyMappings() method to access its mappings.
|
||||
|
||||
When a suitable keypress happens, the manager's invoke() method will be
|
||||
used to invoke the appropriate command.
|
||||
|
||||
@see ApplicationCommandManager
|
||||
*/
|
||||
explicit KeyPressMappingSet (ApplicationCommandManager&);
|
||||
|
||||
/** Creates an copy of a KeyPressMappingSet. */
|
||||
KeyPressMappingSet (const KeyPressMappingSet&);
|
||||
|
||||
/** Destructor. */
|
||||
~KeyPressMappingSet() override;
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandManager& getCommandManager() const noexcept { return commandManager; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a list of keypresses that are assigned to a particular command.
|
||||
|
||||
@param commandID the command's ID
|
||||
*/
|
||||
Array<KeyPress> getKeyPressesAssignedToCommand (CommandID commandID) const;
|
||||
|
||||
/** Assigns a keypress to a command.
|
||||
|
||||
If the keypress is already assigned to a different command, it will first be
|
||||
removed from that command, to avoid it triggering multiple functions.
|
||||
|
||||
@param commandID the ID of the command that you want to add a keypress to. If
|
||||
this is 0, the keypress will be removed from anything that it
|
||||
was previously assigned to, but not re-assigned
|
||||
@param newKeyPress the new key-press
|
||||
@param insertIndex if this is less than zero, the key will be appended to the
|
||||
end of the list of keypresses; otherwise the new keypress will
|
||||
be inserted into the existing list at this index
|
||||
*/
|
||||
void addKeyPress (CommandID commandID,
|
||||
const KeyPress& newKeyPress,
|
||||
int insertIndex = -1);
|
||||
|
||||
/** Reset all mappings to the defaults, as dictated by the ApplicationCommandManager.
|
||||
@see resetToDefaultMapping
|
||||
*/
|
||||
void resetToDefaultMappings();
|
||||
|
||||
/** Resets all key-mappings to the defaults for a particular command.
|
||||
@see resetToDefaultMappings
|
||||
*/
|
||||
void resetToDefaultMapping (CommandID commandID);
|
||||
|
||||
/** Removes all keypresses that are assigned to any commands. */
|
||||
void clearAllKeyPresses();
|
||||
|
||||
/** Removes all keypresses that are assigned to a particular command. */
|
||||
void clearAllKeyPresses (CommandID commandID);
|
||||
|
||||
/** Removes one of the keypresses that are assigned to a command.
|
||||
See the getKeyPressesAssignedToCommand() for the list of keypresses to
|
||||
which the keyPressIndex refers.
|
||||
*/
|
||||
void removeKeyPress (CommandID commandID, int keyPressIndex);
|
||||
|
||||
/** Removes a keypress from any command that it may be assigned to. */
|
||||
void removeKeyPress (const KeyPress& keypress);
|
||||
|
||||
/** Returns true if the given command is linked to this key. */
|
||||
bool containsMapping (CommandID commandID, const KeyPress& keyPress) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Looks for a command that corresponds to a keypress.
|
||||
@returns the UID of the command or 0 if none was found
|
||||
*/
|
||||
CommandID findCommandForKeyPress (const KeyPress& keyPress) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to recreate the mappings from a previously stored state.
|
||||
|
||||
The XML passed in must have been created by the createXml() method.
|
||||
|
||||
If the stored state makes any reference to commands that aren't
|
||||
currently available, these will be ignored.
|
||||
|
||||
If the set of mappings being loaded was a set of differences (using createXml (true)),
|
||||
then this will call resetToDefaultMappings() and then merge the saved mappings
|
||||
on top. If the saved set was created with createXml (false), then this method
|
||||
will first clear all existing mappings and load the saved ones as a complete set.
|
||||
|
||||
@returns true if it manages to load the XML correctly
|
||||
@see createXml
|
||||
*/
|
||||
bool restoreFromXml (const XmlElement& xmlVersion);
|
||||
|
||||
/** Creates an XML representation of the current mappings.
|
||||
|
||||
This will produce a lump of XML that can be later reloaded using
|
||||
restoreFromXml() to recreate the current mapping state.
|
||||
|
||||
@param saveDifferencesFromDefaultSet if this is false, then all keypresses
|
||||
will be saved into the XML. If it's true, then the XML will
|
||||
only store the differences between the current mappings and
|
||||
the default mappings you'd get from calling resetToDefaultMappings().
|
||||
The advantage of saving a set of differences from the default is that
|
||||
if you change the default mappings (in a new version of your app, for
|
||||
example), then these will be merged into a user's saved preferences.
|
||||
|
||||
@see restoreFromXml
|
||||
*/
|
||||
std::unique_ptr<XmlElement> createXml (bool saveDifferencesFromDefaultSet) const;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&, Component*) override;
|
||||
/** @internal */
|
||||
bool keyStateChanged (bool isKeyDown, Component*) override;
|
||||
/** @internal */
|
||||
void globalFocusChanged (Component*) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ApplicationCommandManager& commandManager;
|
||||
|
||||
struct CommandMapping
|
||||
{
|
||||
CommandID commandID;
|
||||
Array<KeyPress> keypresses;
|
||||
bool wantsKeyUpDownCallbacks;
|
||||
};
|
||||
|
||||
OwnedArray<CommandMapping> mappings;
|
||||
|
||||
struct KeyPressTime
|
||||
{
|
||||
KeyPress key;
|
||||
uint32 timeWhenPressed;
|
||||
};
|
||||
|
||||
OwnedArray<KeyPressTime> keysDown;
|
||||
|
||||
void invokeCommand (const CommandID, const KeyPress&, const bool isKeyDown,
|
||||
const int millisecsSinceKeyPressed, Component* originator) const;
|
||||
|
||||
KeyPressMappingSet& operator= (const KeyPressMappingSet&);
|
||||
JUCE_LEAK_DETECTOR (KeyPressMappingSet)
|
||||
};
|
||||
|
||||
} // namespace juce
|
72
deps/juce/modules/juce_gui_basics/components/juce_CachedComponentImage.h
vendored
Normal file
72
deps/juce/modules/juce_gui_basics/components/juce_CachedComponentImage.h
vendored
Normal file
@ -0,0 +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
|
3194
deps/juce/modules/juce_gui_basics/components/juce_Component.cpp
vendored
Normal file
3194
deps/juce/modules/juce_gui_basics/components/juce_Component.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2598
deps/juce/modules/juce_gui_basics/components/juce_Component.h
vendored
Normal file
2598
deps/juce/modules/juce_gui_basics/components/juce_Component.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
38
deps/juce/modules/juce_gui_basics/components/juce_ComponentListener.cpp
vendored
Normal file
38
deps/juce/modules/juce_gui_basics/components/juce_ComponentListener.cpp
vendored
Normal file
@ -0,0 +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
|
122
deps/juce/modules/juce_gui_basics/components/juce_ComponentListener.h
vendored
Normal file
122
deps/juce/modules/juce_gui_basics/components/juce_ComponentListener.h
vendored
Normal file
@ -0,0 +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
|
72
deps/juce/modules/juce_gui_basics/components/juce_ComponentTraverser.h
vendored
Normal file
72
deps/juce/modules/juce_gui_basics/components/juce_ComponentTraverser.h
vendored
Normal file
@ -0,0 +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
|
360
deps/juce/modules/juce_gui_basics/components/juce_FocusTraverser.cpp
vendored
Normal file
360
deps/juce/modules/juce_gui_basics/components/juce_FocusTraverser.cpp
vendored
Normal file
@ -0,0 +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
|
93
deps/juce/modules/juce_gui_basics/components/juce_FocusTraverser.h
vendored
Normal file
93
deps/juce/modules/juce_gui_basics/components/juce_FocusTraverser.h
vendored
Normal file
@ -0,0 +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
|
292
deps/juce/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp
vendored
Normal file
292
deps/juce/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp
vendored
Normal file
@ -0,0 +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
|
324
deps/juce/modules/juce_gui_basics/components/juce_ModalComponentManager.h
vendored
Normal file
324
deps/juce/modules/juce_gui_basics/components/juce_ModalComponentManager.h
vendored
Normal file
@ -0,0 +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
|
348
deps/juce/modules/juce_gui_basics/desktop/juce_Desktop.cpp
vendored
Normal file
348
deps/juce/modules/juce_gui_basics/desktop/juce_Desktop.cpp
vendored
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
Desktop::Desktop()
|
||||
: mouseSources (new MouseInputSource::SourceList()),
|
||||
masterScaleFactor ((float) getDefaultMasterScale()),
|
||||
nativeDarkModeChangeDetectorImpl (createNativeDarkModeChangeDetectorImpl())
|
||||
{
|
||||
displays.reset (new Displays (*this));
|
||||
}
|
||||
|
||||
Desktop::~Desktop()
|
||||
{
|
||||
setScreenSaverEnabled (true);
|
||||
animator.cancelAllAnimations (false);
|
||||
|
||||
jassert (instance == this);
|
||||
instance = nullptr;
|
||||
|
||||
// doh! If you don't delete all your windows before exiting, you're going to
|
||||
// be leaking memory!
|
||||
jassert (desktopComponents.size() == 0);
|
||||
}
|
||||
|
||||
Desktop& JUCE_CALLTYPE Desktop::getInstance()
|
||||
{
|
||||
if (instance == nullptr)
|
||||
instance = new Desktop();
|
||||
|
||||
return *instance;
|
||||
}
|
||||
|
||||
Desktop* Desktop::instance = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
int Desktop::getNumComponents() const noexcept
|
||||
{
|
||||
return desktopComponents.size();
|
||||
}
|
||||
|
||||
Component* Desktop::getComponent (int index) const noexcept
|
||||
{
|
||||
return desktopComponents [index];
|
||||
}
|
||||
|
||||
Component* Desktop::findComponentAt (Point<int> screenPosition) const
|
||||
{
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
for (int i = desktopComponents.size(); --i >= 0;)
|
||||
{
|
||||
auto* c = desktopComponents.getUnchecked(i);
|
||||
|
||||
if (c->isVisible())
|
||||
{
|
||||
auto relative = c->getLocalPoint (nullptr, screenPosition);
|
||||
|
||||
if (c->contains (relative))
|
||||
return c->getComponentAt (relative);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LookAndFeel& Desktop::getDefaultLookAndFeel() noexcept
|
||||
{
|
||||
if (auto lf = currentLookAndFeel.get())
|
||||
return *lf;
|
||||
|
||||
if (defaultLookAndFeel == nullptr)
|
||||
defaultLookAndFeel.reset (new LookAndFeel_V4());
|
||||
|
||||
auto lf = defaultLookAndFeel.get();
|
||||
jassert (lf != nullptr);
|
||||
currentLookAndFeel = lf;
|
||||
return *lf;
|
||||
}
|
||||
|
||||
void Desktop::setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel)
|
||||
{
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
currentLookAndFeel = newDefaultLookAndFeel;
|
||||
|
||||
for (int i = getNumComponents(); --i >= 0;)
|
||||
if (auto* c = getComponent (i))
|
||||
c->sendLookAndFeelChange();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::addDesktopComponent (Component* c)
|
||||
{
|
||||
jassert (c != nullptr);
|
||||
jassert (! desktopComponents.contains (c));
|
||||
desktopComponents.addIfNotAlreadyThere (c);
|
||||
}
|
||||
|
||||
void Desktop::removeDesktopComponent (Component* c)
|
||||
{
|
||||
desktopComponents.removeFirstMatchingValue (c);
|
||||
}
|
||||
|
||||
void Desktop::componentBroughtToFront (Component* c)
|
||||
{
|
||||
auto index = desktopComponents.indexOf (c);
|
||||
jassert (index >= 0);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
int newIndex = -1;
|
||||
|
||||
if (! c->isAlwaysOnTop())
|
||||
{
|
||||
newIndex = desktopComponents.size();
|
||||
|
||||
while (newIndex > 0 && desktopComponents.getUnchecked (newIndex - 1)->isAlwaysOnTop())
|
||||
--newIndex;
|
||||
|
||||
--newIndex;
|
||||
}
|
||||
|
||||
desktopComponents.move (index, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Point<int> Desktop::getMousePosition()
|
||||
{
|
||||
return getMousePositionFloat().roundToInt();
|
||||
}
|
||||
|
||||
Point<float> Desktop::getMousePositionFloat()
|
||||
{
|
||||
return getInstance().getMainMouseSource().getScreenPosition();
|
||||
}
|
||||
|
||||
void Desktop::setMousePosition (Point<int> newPosition)
|
||||
{
|
||||
getInstance().getMainMouseSource().setScreenPosition (newPosition.toFloat());
|
||||
}
|
||||
|
||||
Point<int> Desktop::getLastMouseDownPosition()
|
||||
{
|
||||
return getInstance().getMainMouseSource().getLastMouseDownPosition().roundToInt();
|
||||
}
|
||||
|
||||
int Desktop::getMouseButtonClickCounter() const noexcept { return mouseClickCounter; }
|
||||
int Desktop::getMouseWheelMoveCounter() const noexcept { return mouseWheelCounter; }
|
||||
|
||||
void Desktop::incrementMouseClickCounter() noexcept { ++mouseClickCounter; }
|
||||
void Desktop::incrementMouseWheelCounter() noexcept { ++mouseWheelCounter; }
|
||||
|
||||
const Array<MouseInputSource>& Desktop::getMouseSources() const noexcept { return mouseSources->sourceArray; }
|
||||
int Desktop::getNumMouseSources() const noexcept { return mouseSources->sources.size(); }
|
||||
int Desktop::getNumDraggingMouseSources() const noexcept { return mouseSources->getNumDraggingMouseSources(); }
|
||||
MouseInputSource* Desktop::getMouseSource (int index) const noexcept { return mouseSources->getMouseSource (index); }
|
||||
MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept { return mouseSources->getDraggingMouseSource (index); }
|
||||
MouseInputSource Desktop::getMainMouseSource() const noexcept { return MouseInputSource (mouseSources->sources.getUnchecked(0)); }
|
||||
void Desktop::beginDragAutoRepeat (int interval) { mouseSources->beginDragAutoRepeat (interval); }
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::addFocusChangeListener (FocusChangeListener* l) { focusListeners.add (l); }
|
||||
void Desktop::removeFocusChangeListener (FocusChangeListener* l) { focusListeners.remove (l); }
|
||||
void Desktop::triggerFocusCallback() { triggerAsyncUpdate(); }
|
||||
|
||||
void Desktop::handleAsyncUpdate()
|
||||
{
|
||||
// The component may be deleted during this operation, but we'll use a SafePointer rather than a
|
||||
// BailOutChecker so that any remaining listeners will still get a callback (with a null pointer).
|
||||
focusListeners.call ([currentFocus = WeakReference<Component> { Component::getCurrentlyFocusedComponent() }] (FocusChangeListener& l)
|
||||
{
|
||||
l.globalFocusChanged (currentFocus.get());
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::addDarkModeSettingListener (DarkModeSettingListener* l) { darkModeSettingListeners.add (l); }
|
||||
void Desktop::removeDarkModeSettingListener (DarkModeSettingListener* l) { darkModeSettingListeners.remove (l); }
|
||||
|
||||
void Desktop::darkModeChanged() { darkModeSettingListeners.call ([] (DarkModeSettingListener& l) { l.darkModeSettingChanged(); }); }
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::resetTimer()
|
||||
{
|
||||
if (mouseListeners.size() == 0)
|
||||
stopTimer();
|
||||
else
|
||||
startTimer (100);
|
||||
|
||||
lastFakeMouseMove = getMousePositionFloat();
|
||||
}
|
||||
|
||||
ListenerList<MouseListener>& Desktop::getMouseListeners()
|
||||
{
|
||||
resetTimer();
|
||||
return mouseListeners;
|
||||
}
|
||||
|
||||
void Desktop::addGlobalMouseListener (MouseListener* listener)
|
||||
{
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
mouseListeners.add (listener);
|
||||
resetTimer();
|
||||
}
|
||||
|
||||
void Desktop::removeGlobalMouseListener (MouseListener* listener)
|
||||
{
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
mouseListeners.remove (listener);
|
||||
resetTimer();
|
||||
}
|
||||
|
||||
void Desktop::timerCallback()
|
||||
{
|
||||
if (lastFakeMouseMove != getMousePositionFloat())
|
||||
sendMouseMove();
|
||||
}
|
||||
|
||||
void Desktop::sendMouseMove()
|
||||
{
|
||||
if (! mouseListeners.isEmpty())
|
||||
{
|
||||
startTimer (20);
|
||||
|
||||
lastFakeMouseMove = getMousePositionFloat();
|
||||
|
||||
if (auto* target = findComponentAt (lastFakeMouseMove.roundToInt()))
|
||||
{
|
||||
Component::BailOutChecker checker (target);
|
||||
auto pos = target->getLocalPoint (nullptr, lastFakeMouseMove);
|
||||
auto now = Time::getCurrentTime();
|
||||
|
||||
const MouseEvent me (getMainMouseSource(), pos, ModifierKeys::currentModifiers, MouseInputSource::invalidPressure,
|
||||
MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
|
||||
MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
|
||||
target, target, now, pos, now, 0, false);
|
||||
|
||||
if (me.mods.isAnyMouseButtonDown())
|
||||
mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseDrag (me); });
|
||||
else
|
||||
mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseMove (me); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::setKioskModeComponent (Component* componentToUse, bool allowMenusAndBars)
|
||||
{
|
||||
if (kioskModeReentrant)
|
||||
return;
|
||||
|
||||
const ScopedValueSetter<bool> setter (kioskModeReentrant, true, false);
|
||||
|
||||
if (kioskModeComponent != componentToUse)
|
||||
{
|
||||
// agh! Don't delete or remove a component from the desktop while it's still the kiosk component!
|
||||
jassert (kioskModeComponent == nullptr || ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
|
||||
|
||||
if (auto* oldKioskComp = kioskModeComponent)
|
||||
{
|
||||
kioskModeComponent = nullptr; // (to make sure that isKioskMode() returns false when resizing the old one)
|
||||
setKioskComponent (oldKioskComp, false, allowMenusAndBars);
|
||||
oldKioskComp->setBounds (kioskComponentOriginalBounds);
|
||||
}
|
||||
|
||||
kioskModeComponent = componentToUse;
|
||||
|
||||
if (kioskModeComponent != nullptr)
|
||||
{
|
||||
// Only components that are already on the desktop can be put into kiosk mode!
|
||||
jassert (ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
|
||||
|
||||
kioskComponentOriginalBounds = kioskModeComponent->getBounds();
|
||||
setKioskComponent (kioskModeComponent, true, allowMenusAndBars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::setOrientationsEnabled (int newOrientations)
|
||||
{
|
||||
if (allowedOrientations != newOrientations)
|
||||
{
|
||||
// Dodgy set of flags being passed here! Make sure you specify at least one permitted orientation.
|
||||
jassert (newOrientations != 0 && (newOrientations & ~allOrientations) == 0);
|
||||
|
||||
allowedOrientations = newOrientations;
|
||||
allowedOrientationsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int Desktop::getOrientationsEnabled() const noexcept
|
||||
{
|
||||
return allowedOrientations;
|
||||
}
|
||||
|
||||
bool Desktop::isOrientationEnabled (DisplayOrientation orientation) const noexcept
|
||||
{
|
||||
// Make sure you only pass one valid flag in here...
|
||||
jassert (orientation == upright || orientation == upsideDown
|
||||
|| orientation == rotatedClockwise || orientation == rotatedAntiClockwise);
|
||||
|
||||
return (allowedOrientations & orientation) != 0;
|
||||
}
|
||||
|
||||
void Desktop::setGlobalScaleFactor (float newScaleFactor) noexcept
|
||||
{
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
if (masterScaleFactor != newScaleFactor)
|
||||
{
|
||||
masterScaleFactor = newScaleFactor;
|
||||
displays->refresh();
|
||||
}
|
||||
}
|
||||
|
||||
bool Desktop::isHeadless() const noexcept
|
||||
{
|
||||
return displays->displays.isEmpty();
|
||||
}
|
||||
|
||||
} // namespace juce
|
481
deps/juce/modules/juce_gui_basics/desktop/juce_Desktop.h
vendored
Normal file
481
deps/juce/modules/juce_gui_basics/desktop/juce_Desktop.h
vendored
Normal file
@ -0,0 +1,481 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Classes can implement this interface and register themselves with the Desktop class
|
||||
to receive callbacks when the currently focused component changes.
|
||||
|
||||
@see Desktop::addFocusChangeListener, Desktop::removeFocusChangeListener
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FocusChangeListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~FocusChangeListener() = default;
|
||||
|
||||
/** Callback to indicate that the currently focused component has changed. */
|
||||
virtual void globalFocusChanged (Component* focusedComponent) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Classes can implement this interface and register themselves with the Desktop class
|
||||
to receive callbacks when the operating system dark mode setting changes. The
|
||||
Desktop::isDarkModeActive() method can then be used to query the current setting.
|
||||
|
||||
@see Desktop::addDarkModeSettingListener, Desktop::removeDarkModeSettingListener,
|
||||
Desktop::isDarkModeActive
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DarkModeSettingListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~DarkModeSettingListener() = default;
|
||||
|
||||
/** Callback to indicate that the dark mode setting has changed. */
|
||||
virtual void darkModeSettingChanged() = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Describes and controls aspects of the computer's desktop.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API Desktop : private DeletedAtShutdown,
|
||||
private Timer,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** There's only one desktop object, and this method will return it. */
|
||||
static Desktop& JUCE_CALLTYPE getInstance();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the mouse position.
|
||||
|
||||
The coordinates are relative to the top-left of the main monitor.
|
||||
|
||||
Note that this is just a shortcut for calling getMainMouseSource().getScreenPosition(), and
|
||||
you should only resort to grabbing the global mouse position if there's really no
|
||||
way to get the coordinates via a mouse event callback instead.
|
||||
*/
|
||||
static Point<int> getMousePosition();
|
||||
|
||||
/** Makes the mouse pointer jump to a given location.
|
||||
The coordinates are relative to the top-left of the main monitor.
|
||||
Note that this is a pretty old method, kept around mainly for backwards-compatibility,
|
||||
and you should use the MouseInputSource class directly in new code.
|
||||
*/
|
||||
static void setMousePosition (Point<int> newPosition);
|
||||
|
||||
/** Returns the last position at which a mouse button was pressed.
|
||||
|
||||
Note that this is just a shortcut for calling getMainMouseSource().getLastMouseDownPosition(),
|
||||
and in a multi-touch environment, it doesn't make much sense. ALWAYS prefer to
|
||||
get this information via other means, such as MouseEvent::getMouseDownScreenPosition()
|
||||
if possible, and only ever call this as a last resort.
|
||||
*/
|
||||
static Point<int> getLastMouseDownPosition();
|
||||
|
||||
/** Returns the number of times the mouse button has been clicked since the app started.
|
||||
Each mouse-down event increments this number by 1.
|
||||
@see getMouseWheelMoveCounter
|
||||
*/
|
||||
int getMouseButtonClickCounter() const noexcept;
|
||||
|
||||
/** Returns the number of times the mouse wheel has been moved since the app started.
|
||||
Each mouse-wheel event increments this number by 1.
|
||||
@see getMouseButtonClickCounter
|
||||
*/
|
||||
int getMouseWheelMoveCounter() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** This lets you prevent the screensaver from becoming active.
|
||||
|
||||
Handy if you're running some sort of presentation app where having a screensaver
|
||||
appear would be annoying.
|
||||
|
||||
Pass false to disable the screensaver, and true to re-enable it. (Note that this
|
||||
won't enable a screensaver unless the user has actually set one up).
|
||||
|
||||
The disablement will only happen while the JUCE application is the foreground
|
||||
process - if another task is running in front of it, then the screensaver will
|
||||
be unaffected.
|
||||
|
||||
@see isScreenSaverEnabled
|
||||
*/
|
||||
static void setScreenSaverEnabled (bool isEnabled);
|
||||
|
||||
/** Returns true if the screensaver has not been turned off.
|
||||
|
||||
This will return the last value passed into setScreenSaverEnabled(). Note that
|
||||
it won't tell you whether the user is actually using a screen saver, just
|
||||
whether this app is deliberately preventing one from running.
|
||||
|
||||
@see setScreenSaverEnabled
|
||||
*/
|
||||
static bool isScreenSaverEnabled();
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a MouseListener that will receive all mouse events that occur on
|
||||
any component.
|
||||
|
||||
@see removeGlobalMouseListener
|
||||
*/
|
||||
void addGlobalMouseListener (MouseListener* listener);
|
||||
|
||||
/** Unregisters a MouseListener that was added with addGlobalMouseListener().
|
||||
|
||||
@see addGlobalMouseListener
|
||||
*/
|
||||
void removeGlobalMouseListener (MouseListener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a FocusChangeListener that will receive a callback whenever the focused
|
||||
component changes.
|
||||
|
||||
@see removeFocusChangeListener
|
||||
*/
|
||||
void addFocusChangeListener (FocusChangeListener* listener);
|
||||
|
||||
/** Unregisters a FocusChangeListener that was added with addFocusChangeListener().
|
||||
|
||||
@see addFocusChangeListener
|
||||
*/
|
||||
void removeFocusChangeListener (FocusChangeListener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a DarkModeSettingListener that will receive a callback when the
|
||||
operating system dark mode setting changes. To query whether dark mode is on
|
||||
use the isDarkModeActive() method.
|
||||
|
||||
@see isDarkModeActive, removeDarkModeSettingListener
|
||||
*/
|
||||
void addDarkModeSettingListener (DarkModeSettingListener* listener);
|
||||
|
||||
/** Unregisters a DarkModeSettingListener that was added with addDarkModeSettingListener().
|
||||
|
||||
@see addDarkModeSettingListener
|
||||
*/
|
||||
void removeDarkModeSettingListener (DarkModeSettingListener* listener);
|
||||
|
||||
/** True if the operating system "dark mode" is active.
|
||||
|
||||
To receive a callback when this setting changes implement the DarkModeSettingListener
|
||||
interface and use the addDarkModeSettingListener() to register a listener.
|
||||
|
||||
@see addDarkModeSettingListener, removeDarkModeSettingListener
|
||||
*/
|
||||
bool isDarkModeActive() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Takes a component and makes it full-screen, removing the taskbar, dock, etc.
|
||||
|
||||
The component must already be on the desktop for this method to work. It will
|
||||
be resized to completely fill the screen and any extraneous taskbars, menu bars,
|
||||
etc will be hidden.
|
||||
|
||||
To exit kiosk mode, just call setKioskModeComponent (nullptr). When this is called,
|
||||
the component that's currently being used will be resized back to the size
|
||||
and position it was in before being put into this mode.
|
||||
|
||||
If allowMenusAndBars is true, things like the menu and dock (on mac) are still
|
||||
allowed to pop up when the mouse moves onto them. If this is false, it'll try
|
||||
to hide as much on-screen paraphernalia as possible.
|
||||
*/
|
||||
void setKioskModeComponent (Component* componentToUse,
|
||||
bool allowMenusAndBars = true);
|
||||
|
||||
/** Returns the component that is currently being used in kiosk-mode.
|
||||
|
||||
This is the component that was last set by setKioskModeComponent(). If none
|
||||
has been set, this returns nullptr.
|
||||
*/
|
||||
Component* getKioskModeComponent() const noexcept { return kioskModeComponent; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of components that are currently active as top-level
|
||||
desktop windows.
|
||||
|
||||
@see getComponent, Component::addToDesktop
|
||||
*/
|
||||
int getNumComponents() const noexcept;
|
||||
|
||||
/** Returns one of the top-level desktop window components.
|
||||
|
||||
The index is from 0 to getNumComponents() - 1. This could return 0 if the
|
||||
index is out-of-range.
|
||||
|
||||
@see getNumComponents, Component::addToDesktop
|
||||
*/
|
||||
Component* getComponent (int index) const noexcept;
|
||||
|
||||
/** Finds the component at a given screen location.
|
||||
|
||||
This will drill down into top-level windows to find the child component at
|
||||
the given position.
|
||||
|
||||
Returns nullptr if the coordinates are inside a non-JUCE window.
|
||||
*/
|
||||
Component* findComponentAt (Point<int> screenPosition) const;
|
||||
|
||||
/** The Desktop object has a ComponentAnimator instance which can be used for performing
|
||||
your animations.
|
||||
|
||||
Having a single shared ComponentAnimator object makes it more efficient when multiple
|
||||
components are being moved around simultaneously. It's also more convenient than having
|
||||
to manage your own instance of one.
|
||||
|
||||
@see ComponentAnimator
|
||||
*/
|
||||
ComponentAnimator& getAnimator() noexcept { return animator; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current default look-and-feel for components which don't have one
|
||||
explicitly set.
|
||||
@see setDefaultLookAndFeel
|
||||
*/
|
||||
LookAndFeel& getDefaultLookAndFeel() noexcept;
|
||||
|
||||
/** Changes the default look-and-feel.
|
||||
@param newDefaultLookAndFeel the new look-and-feel object to use - if this is
|
||||
set to nullptr, it will revert to using the system's
|
||||
default one. The object passed-in must be deleted by the
|
||||
caller when it's no longer needed.
|
||||
@see getDefaultLookAndFeel
|
||||
*/
|
||||
void setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel);
|
||||
|
||||
//==============================================================================
|
||||
/** Provides access to the array of mouse sources, for iteration.
|
||||
In a traditional single-mouse system, there might be only one MouseInputSource. On a
|
||||
multi-touch system, there could be one input source per potential finger. The number
|
||||
of mouse sources returned here may increase dynamically as the program runs.
|
||||
To find out how many mouse events are currently happening, use getNumDraggingMouseSources().
|
||||
*/
|
||||
const Array<MouseInputSource>& getMouseSources() const noexcept;
|
||||
|
||||
/** Returns the number of MouseInputSource objects the system has at its disposal.
|
||||
In a traditional single-mouse system, there might be only one MouseInputSource. On a
|
||||
multi-touch system, there could be one input source per potential finger. The number
|
||||
of mouse sources returned here may increase dynamically as the program runs.
|
||||
To find out how many mouse events are currently happening, use getNumDraggingMouseSources().
|
||||
@see getMouseSource
|
||||
*/
|
||||
int getNumMouseSources() const noexcept;
|
||||
|
||||
/** Returns one of the system's MouseInputSource objects.
|
||||
The index should be from 0 to getNumMouseSources() - 1. Out-of-range indexes will return
|
||||
a null pointer.
|
||||
In a traditional single-mouse system, there might be only one object. On a multi-touch
|
||||
system, there could be one input source per potential finger.
|
||||
*/
|
||||
MouseInputSource* getMouseSource (int index) const noexcept;
|
||||
|
||||
/** Returns the main mouse input device that the system is using.
|
||||
@see getNumMouseSources()
|
||||
*/
|
||||
MouseInputSource getMainMouseSource() const noexcept;
|
||||
|
||||
/** Returns the number of mouse-sources that are currently being dragged.
|
||||
In a traditional single-mouse system, this will be 0 or 1, depending on whether a
|
||||
JUCE component has the button down on it. In a multi-touch system, this could
|
||||
be any number from 0 to the number of simultaneous touches that can be detected.
|
||||
*/
|
||||
int getNumDraggingMouseSources() const noexcept;
|
||||
|
||||
/** Returns one of the mouse sources that's currently being dragged.
|
||||
The index should be between 0 and getNumDraggingMouseSources() - 1. If the index is
|
||||
out of range, or if no mice or fingers are down, this will return a null pointer.
|
||||
*/
|
||||
MouseInputSource* getDraggingMouseSource (int index) const noexcept;
|
||||
|
||||
/** Ensures that a non-stop stream of mouse-drag events will be sent during the
|
||||
current mouse-drag operation.
|
||||
|
||||
This allows you to make sure that mouseDrag() events are sent continuously, even
|
||||
when the mouse isn't moving. This can be useful for things like auto-scrolling
|
||||
components when the mouse is near an edge.
|
||||
|
||||
Call this method during a mouseDown() or mouseDrag() callback, specifying the
|
||||
minimum interval between consecutive mouse drag callbacks. The callbacks
|
||||
will continue until the mouse is released, and then the interval will be reset,
|
||||
so you need to make sure it's called every time you begin a drag event.
|
||||
Passing an interval of 0 or less will cancel the auto-repeat.
|
||||
|
||||
@see mouseDrag
|
||||
*/
|
||||
void beginDragAutoRepeat (int millisecondsBetweenCallbacks);
|
||||
|
||||
//==============================================================================
|
||||
/** In a tablet/mobile device which can be turned around, this is used to indicate the orientation. */
|
||||
enum DisplayOrientation
|
||||
{
|
||||
upright = 1, /**< Indicates that the device is the normal way up. */
|
||||
upsideDown = 2, /**< Indicates that the device is upside-down. */
|
||||
rotatedClockwise = 4, /**< Indicates that the device is turned 90 degrees clockwise from its upright position. */
|
||||
rotatedAntiClockwise = 8, /**< Indicates that the device is turned 90 degrees anti-clockwise from its upright position. */
|
||||
|
||||
allOrientations = 1 + 2 + 4 + 8 /**< A combination of all the orientation values */
|
||||
};
|
||||
|
||||
/** In a tablet device which can be turned around, this returns the current orientation. */
|
||||
DisplayOrientation getCurrentOrientation() const;
|
||||
|
||||
/** Sets which orientations the display is allowed to auto-rotate to.
|
||||
|
||||
For devices that support rotating desktops, this lets you specify which of the orientations your app can use.
|
||||
|
||||
The parameter is a bitwise or-ed combination of the values in DisplayOrientation, and must contain at least one
|
||||
set bit.
|
||||
*/
|
||||
void setOrientationsEnabled (int allowedOrientations);
|
||||
|
||||
/** Returns the set of orientations the display is allowed to rotate to.
|
||||
@see setOrientationsEnabled
|
||||
*/
|
||||
int getOrientationsEnabled() const noexcept;
|
||||
|
||||
/** Returns whether the display is allowed to auto-rotate to the given orientation.
|
||||
Each orientation can be enabled using setOrientationEnabled(). By default, all orientations are allowed.
|
||||
*/
|
||||
bool isOrientationEnabled (DisplayOrientation orientation) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the Displays object representing the connected displays.
|
||||
|
||||
@see Displays
|
||||
*/
|
||||
const Displays& getDisplays() const noexcept { return *displays; }
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a global scale factor to be used for all desktop windows.
|
||||
Setting this will also scale the monitor sizes that are returned by getDisplays().
|
||||
*/
|
||||
void setGlobalScaleFactor (float newScaleFactor) noexcept;
|
||||
|
||||
/** Returns the current global scale factor, as set by setGlobalScaleFactor().
|
||||
@see setGlobalScaleFactor
|
||||
*/
|
||||
float getGlobalScaleFactor() const noexcept { return masterScaleFactor; }
|
||||
|
||||
//==============================================================================
|
||||
/** True if the OS supports semitransparent windows */
|
||||
static bool canUseSemiTransparentWindows() noexcept;
|
||||
|
||||
#if JUCE_MAC && ! defined (DOXYGEN)
|
||||
[[deprecated ("This macOS-specific method has been deprecated in favour of the cross-platform "
|
||||
" isDarkModeActive() method.")]]
|
||||
static bool isOSXDarkModeActive() { return Desktop::getInstance().isDarkModeActive(); }
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true on a headless system where there are no connected displays. */
|
||||
bool isHeadless() const noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static Desktop* instance;
|
||||
|
||||
friend class Component;
|
||||
friend class ComponentPeer;
|
||||
friend class MouseInputSourceInternal;
|
||||
friend class DeletedAtShutdown;
|
||||
friend class TopLevelWindowManager;
|
||||
friend class Displays;
|
||||
|
||||
std::unique_ptr<MouseInputSource::SourceList> mouseSources;
|
||||
|
||||
ListenerList<MouseListener> mouseListeners;
|
||||
ListenerList<FocusChangeListener> focusListeners;
|
||||
ListenerList<DarkModeSettingListener> darkModeSettingListeners;
|
||||
|
||||
Array<Component*> desktopComponents;
|
||||
Array<ComponentPeer*> peers;
|
||||
|
||||
std::unique_ptr<Displays> displays;
|
||||
|
||||
Point<float> lastFakeMouseMove;
|
||||
void sendMouseMove();
|
||||
|
||||
int mouseClickCounter = 0, mouseWheelCounter = 0;
|
||||
void incrementMouseClickCounter() noexcept;
|
||||
void incrementMouseWheelCounter() noexcept;
|
||||
|
||||
std::unique_ptr<LookAndFeel> defaultLookAndFeel;
|
||||
WeakReference<LookAndFeel> currentLookAndFeel;
|
||||
|
||||
Component* kioskModeComponent = nullptr;
|
||||
Rectangle<int> kioskComponentOriginalBounds;
|
||||
bool kioskModeReentrant = false;
|
||||
|
||||
int allowedOrientations = allOrientations;
|
||||
void allowedOrientationsChanged();
|
||||
|
||||
float masterScaleFactor;
|
||||
|
||||
ComponentAnimator animator;
|
||||
|
||||
void timerCallback() override;
|
||||
void resetTimer();
|
||||
ListenerList<MouseListener>& getMouseListeners();
|
||||
|
||||
void addDesktopComponent (Component*);
|
||||
void removeDesktopComponent (Component*);
|
||||
void componentBroughtToFront (Component*);
|
||||
|
||||
void setKioskComponent (Component*, bool shouldBeEnabled, bool allowMenusAndBars);
|
||||
|
||||
void triggerFocusCallback();
|
||||
void handleAsyncUpdate() override;
|
||||
|
||||
static Point<float> getMousePositionFloat();
|
||||
|
||||
static double getDefaultMasterScale();
|
||||
|
||||
Desktop();
|
||||
~Desktop() override;
|
||||
|
||||
//==============================================================================
|
||||
class NativeDarkModeChangeDetectorImpl;
|
||||
std::unique_ptr<NativeDarkModeChangeDetectorImpl> nativeDarkModeChangeDetectorImpl;
|
||||
|
||||
static std::unique_ptr<NativeDarkModeChangeDetectorImpl> createNativeDarkModeChangeDetectorImpl();
|
||||
void darkModeChanged();
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Desktop)
|
||||
};
|
||||
|
||||
} // namespace juce
|
438
deps/juce/modules/juce_gui_basics/desktop/juce_Displays.cpp
vendored
Normal file
438
deps/juce/modules/juce_gui_basics/desktop/juce_Displays.cpp
vendored
Normal file
@ -0,0 +1,438 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
Displays::Displays (Desktop& desktop)
|
||||
{
|
||||
init (desktop);
|
||||
}
|
||||
|
||||
void Displays::init (Desktop& desktop)
|
||||
{
|
||||
findDisplays (desktop.getGlobalScaleFactor());
|
||||
}
|
||||
|
||||
const Displays::Display* Displays::getDisplayForRect (Rectangle<int> rect, bool isPhysical) const noexcept
|
||||
{
|
||||
int maxArea = -1;
|
||||
const Display* foundDisplay = nullptr;
|
||||
|
||||
for (auto& display : displays)
|
||||
{
|
||||
auto displayArea = display.totalArea;
|
||||
|
||||
if (isPhysical)
|
||||
displayArea = (displayArea.withZeroOrigin() * display.scale) + display.topLeftPhysical;
|
||||
|
||||
displayArea = displayArea.getIntersection (rect);
|
||||
auto area = displayArea.getWidth() * displayArea.getHeight();
|
||||
|
||||
if (area >= maxArea)
|
||||
{
|
||||
maxArea = area;
|
||||
foundDisplay = &display;
|
||||
}
|
||||
}
|
||||
|
||||
return foundDisplay;
|
||||
}
|
||||
|
||||
const Displays::Display* Displays::getDisplayForPoint (Point<int> point, bool isPhysical) const noexcept
|
||||
{
|
||||
auto minDistance = std::numeric_limits<int>::max();
|
||||
const Display* foundDisplay = nullptr;
|
||||
|
||||
for (auto& display : displays)
|
||||
{
|
||||
auto displayArea = display.totalArea;
|
||||
|
||||
if (isPhysical)
|
||||
displayArea = (displayArea.withZeroOrigin() * display.scale) + display.topLeftPhysical;
|
||||
|
||||
if (displayArea.contains (point))
|
||||
return &display;
|
||||
|
||||
auto distance = displayArea.getCentre().getDistanceFrom (point);
|
||||
|
||||
if (distance <= minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
foundDisplay = &display;
|
||||
}
|
||||
}
|
||||
|
||||
return foundDisplay;
|
||||
}
|
||||
|
||||
Rectangle<int> Displays::physicalToLogical (Rectangle<int> rect, const Display* useScaleFactorOfDisplay) const noexcept
|
||||
{
|
||||
return physicalToLogical (rect.toFloat(), useScaleFactorOfDisplay).toNearestInt();
|
||||
}
|
||||
|
||||
Rectangle<float> Displays::physicalToLogical (Rectangle<float> rect, const Display* useScaleFactorOfDisplay) const noexcept
|
||||
{
|
||||
const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
|
||||
: getDisplayForRect (rect.toNearestInt(), true);
|
||||
|
||||
if (display == nullptr)
|
||||
return rect;
|
||||
|
||||
auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
|
||||
|
||||
return ((rect - display->topLeftPhysical.toFloat()) / (display->scale / globalScale))
|
||||
+ (display->totalArea.getTopLeft().toFloat() * globalScale);
|
||||
}
|
||||
|
||||
Rectangle<int> Displays::logicalToPhysical (Rectangle<int> rect, const Display* useScaleFactorOfDisplay) const noexcept
|
||||
{
|
||||
return logicalToPhysical (rect.toFloat(), useScaleFactorOfDisplay).toNearestInt();
|
||||
}
|
||||
|
||||
Rectangle<float> Displays::logicalToPhysical (Rectangle<float> rect, const Display* useScaleFactorOfDisplay) const noexcept
|
||||
{
|
||||
const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
|
||||
: getDisplayForRect (rect.toNearestInt(), false);
|
||||
|
||||
if (display == nullptr)
|
||||
return rect;
|
||||
|
||||
auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
|
||||
|
||||
return ((rect.toFloat() - (display->totalArea.getTopLeft().toFloat() * globalScale)) * (display->scale / globalScale))
|
||||
+ display->topLeftPhysical.toFloat();
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
Point<ValueType> Displays::physicalToLogical (Point<ValueType> point, const Display* useScaleFactorOfDisplay) const noexcept
|
||||
{
|
||||
const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
|
||||
: getDisplayForPoint (point.roundToInt(), true);
|
||||
|
||||
if (display == nullptr)
|
||||
return point;
|
||||
|
||||
auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
|
||||
|
||||
Point<ValueType> logicalTopLeft (static_cast<ValueType> (display->totalArea.getX()), static_cast<ValueType> (display->totalArea.getY()));
|
||||
Point<ValueType> physicalTopLeft (static_cast<ValueType> (display->topLeftPhysical.getX()), static_cast<ValueType> (display->topLeftPhysical.getY()));
|
||||
|
||||
return ((point - physicalTopLeft) / (display->scale / globalScale)) + (logicalTopLeft * globalScale);
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
Point<ValueType> Displays::logicalToPhysical (Point<ValueType> point, const Display* useScaleFactorOfDisplay) const noexcept
|
||||
{
|
||||
const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
|
||||
: getDisplayForPoint (point.roundToInt(), false);
|
||||
|
||||
if (display == nullptr)
|
||||
return point;
|
||||
|
||||
auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
|
||||
|
||||
Point<ValueType> logicalTopLeft (static_cast<ValueType> (display->totalArea.getX()), static_cast<ValueType> (display->totalArea.getY()));
|
||||
Point<ValueType> physicalTopLeft (static_cast<ValueType> (display->topLeftPhysical.getX()), static_cast<ValueType> (display->topLeftPhysical.getY()));
|
||||
|
||||
return ((point - (logicalTopLeft * globalScale)) * (display->scale / globalScale)) + physicalTopLeft;
|
||||
}
|
||||
|
||||
const Displays::Display* Displays::getPrimaryDisplay() const noexcept
|
||||
{
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
for (auto& d : displays)
|
||||
if (d.isMain)
|
||||
return &d;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RectangleList<int> Displays::getRectangleList (bool userAreasOnly) const
|
||||
{
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
RectangleList<int> rl;
|
||||
|
||||
for (auto& d : displays)
|
||||
rl.addWithoutMerging (userAreasOnly ? d.userArea : d.totalArea);
|
||||
|
||||
return rl;
|
||||
}
|
||||
|
||||
Rectangle<int> Displays::getTotalBounds (bool userAreasOnly) const
|
||||
{
|
||||
return getRectangleList (userAreasOnly).getBounds();
|
||||
}
|
||||
|
||||
void Displays::refresh()
|
||||
{
|
||||
Array<Display> oldDisplays;
|
||||
oldDisplays.swapWith (displays);
|
||||
|
||||
init (Desktop::getInstance());
|
||||
|
||||
if (oldDisplays != displays)
|
||||
{
|
||||
for (auto i = ComponentPeer::getNumPeers(); --i >= 0;)
|
||||
if (auto* peer = ComponentPeer::getPeer (i))
|
||||
peer->handleScreenSizeChange();
|
||||
}
|
||||
}
|
||||
|
||||
bool operator== (const Displays::Display& d1, const Displays::Display& d2) noexcept;
|
||||
bool operator== (const Displays::Display& d1, const Displays::Display& d2) noexcept
|
||||
{
|
||||
return d1.isMain == d2.isMain
|
||||
&& d1.totalArea == d2.totalArea
|
||||
&& d1.userArea == d2.userArea
|
||||
&& d1.topLeftPhysical == d2.topLeftPhysical
|
||||
&& d1.scale == d2.scale
|
||||
&& d1.dpi == d2.dpi;
|
||||
}
|
||||
|
||||
bool operator!= (const Displays::Display& d1, const Displays::Display& d2) noexcept;
|
||||
bool operator!= (const Displays::Display& d1, const Displays::Display& d2) noexcept { return ! (d1 == d2); }
|
||||
|
||||
//==============================================================================
|
||||
// These methods are used for converting the totalArea and userArea Rectangles in Display from physical to logical
|
||||
// pixels. We do this by constructing a graph of connected displays where the root node has position (0, 0); this can be
|
||||
// safely converted to logical pixels using its scale factor and we can then traverse the graph and work out the logical pixels
|
||||
// for all the other connected displays. We need to do this as the logical bounds of a display depend not only on its scale
|
||||
// factor but also the scale factor of the displays connected to it.
|
||||
|
||||
/**
|
||||
Represents a node in our graph of displays.
|
||||
*/
|
||||
struct DisplayNode
|
||||
{
|
||||
/** The Display object that this represents. */
|
||||
Displays::Display* display;
|
||||
|
||||
/** True if this represents the 'root' display with position (0, 0). */
|
||||
bool isRoot = false;
|
||||
|
||||
/** The parent node of this node in our display graph. This will have a correct logicalArea. */
|
||||
DisplayNode* parent = nullptr;
|
||||
|
||||
/** The logical area to be calculated. This will be valid after processDisplay() has
|
||||
been called on this node.
|
||||
*/
|
||||
Rectangle<double> logicalArea;
|
||||
};
|
||||
|
||||
/** Recursive - will calculate and set the logicalArea member of current. */
|
||||
static void processDisplay (DisplayNode* currentNode, Array<DisplayNode>& allNodes)
|
||||
{
|
||||
const auto physicalArea = currentNode->display->totalArea.toDouble();
|
||||
const auto scale = currentNode->display->scale;
|
||||
|
||||
if (! currentNode->isRoot)
|
||||
{
|
||||
const auto logicalWidth = physicalArea.getWidth() / scale;
|
||||
const auto logicalHeight = physicalArea.getHeight() / scale;
|
||||
|
||||
const auto physicalParentArea = currentNode->parent->display->totalArea.toDouble();
|
||||
const auto logicalParentArea = currentNode->parent->logicalArea; // logical area of parent has already been calculated
|
||||
const auto parentScale = currentNode->parent->display->scale;
|
||||
|
||||
Rectangle<double> logicalArea (0.0, 0.0, logicalWidth, logicalHeight);
|
||||
|
||||
if (physicalArea.getRight() == physicalParentArea.getX()) logicalArea.setPosition ({ logicalParentArea.getX() - logicalWidth, physicalArea.getY() / parentScale }); // on left
|
||||
else if (physicalArea.getX() == physicalParentArea.getRight()) logicalArea.setPosition ({ logicalParentArea.getRight(), physicalArea.getY() / parentScale }); // on right
|
||||
else if (physicalArea.getBottom() == physicalParentArea.getY()) logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getY() - logicalHeight }); // on top
|
||||
else if (physicalArea.getY() == physicalParentArea.getBottom()) logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getBottom() }); // on bottom
|
||||
else jassertfalse;
|
||||
|
||||
currentNode->logicalArea = logicalArea;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If currentNode is the root (position (0, 0)) then we can just scale the physical area
|
||||
currentNode->logicalArea = physicalArea / scale;
|
||||
currentNode->parent = currentNode;
|
||||
}
|
||||
|
||||
// Find child nodes
|
||||
Array<DisplayNode*> children;
|
||||
for (auto& node : allNodes)
|
||||
{
|
||||
// Already calculated
|
||||
if (node.parent != nullptr)
|
||||
continue;
|
||||
|
||||
const auto otherPhysicalArea = node.display->totalArea.toDouble();
|
||||
|
||||
// If the displays are touching on any side
|
||||
if (otherPhysicalArea.getX() == physicalArea.getRight() || otherPhysicalArea.getRight() == physicalArea.getX()
|
||||
|| otherPhysicalArea.getY() == physicalArea.getBottom() || otherPhysicalArea.getBottom() == physicalArea.getY())
|
||||
{
|
||||
node.parent = currentNode;
|
||||
children.add (&node);
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively process all child nodes
|
||||
for (auto child : children)
|
||||
processDisplay (child, allNodes);
|
||||
}
|
||||
|
||||
/** This is called when the displays Array has been filled out with the info for all connected displays and the
|
||||
totalArea and userArea Rectangles need to be converted from physical to logical coordinates.
|
||||
*/
|
||||
void Displays::updateToLogical()
|
||||
{
|
||||
if (displays.size() == 1)
|
||||
{
|
||||
auto& display = displays.getReference (0);
|
||||
|
||||
display.totalArea = (display.totalArea.toDouble() / display.scale).toNearestInt();
|
||||
display.userArea = (display.userArea.toDouble() / display.scale).toNearestInt();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Array<DisplayNode> displayNodes;
|
||||
|
||||
for (auto& d : displays)
|
||||
{
|
||||
DisplayNode node;
|
||||
|
||||
node.display = &d;
|
||||
|
||||
if (d.totalArea.getTopLeft() == Point<int>())
|
||||
node.isRoot = true;
|
||||
|
||||
displayNodes.add (node);
|
||||
}
|
||||
|
||||
auto* root = [&displayNodes]() -> DisplayNode*
|
||||
{
|
||||
for (auto& node : displayNodes)
|
||||
if (node.isRoot)
|
||||
return &node;
|
||||
|
||||
auto minDistance = std::numeric_limits<int>::max();
|
||||
DisplayNode* retVal = nullptr;
|
||||
|
||||
for (auto& node : displayNodes)
|
||||
{
|
||||
auto distance = node.display->totalArea.getTopLeft().getDistanceFrom ({});
|
||||
|
||||
if (distance < minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
retVal = &node;
|
||||
}
|
||||
}
|
||||
|
||||
if (retVal != nullptr)
|
||||
retVal->isRoot = true;
|
||||
|
||||
return retVal;
|
||||
}();
|
||||
|
||||
// Must have a root node!
|
||||
jassert (root != nullptr);
|
||||
|
||||
// Recursively traverse the display graph from the root and work out logical bounds
|
||||
processDisplay (root, displayNodes);
|
||||
|
||||
for (auto& node : displayNodes)
|
||||
{
|
||||
// All of the nodes should have a parent
|
||||
jassert (node.parent != nullptr);
|
||||
|
||||
auto relativeUserArea = (node.display->userArea.toDouble() - node.display->totalArea.toDouble().getTopLeft()) / node.display->scale;
|
||||
|
||||
// Now set Display::totalArea and ::userArea using the logical area that we have calculated
|
||||
node.display->topLeftPhysical = node.display->totalArea.getTopLeft();
|
||||
node.display->totalArea = node.logicalArea.toNearestInt();
|
||||
node.display->userArea = (relativeUserArea + node.logicalArea.getTopLeft()).toNearestInt();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// explicit template instantiations
|
||||
template Point<int> Displays::physicalToLogical (Point<int>, const Display*) const noexcept;
|
||||
template Point<float> Displays::physicalToLogical (Point<float>, const Display*) const noexcept;
|
||||
|
||||
template Point<int> Displays::logicalToPhysical (Point<int>, const Display*) const noexcept;
|
||||
template Point<float> Displays::logicalToPhysical (Point<float>, const Display*) const noexcept;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
// Deprecated methods
|
||||
const Displays::Display& Displays::getDisplayContaining (Point<int> position) const noexcept
|
||||
{
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
const auto* best = &displays.getReference (0);
|
||||
auto bestDistance = std::numeric_limits<int>::max();
|
||||
|
||||
for (auto& d : displays)
|
||||
{
|
||||
if (d.totalArea.contains (position))
|
||||
{
|
||||
best = &d;
|
||||
break;
|
||||
}
|
||||
|
||||
auto distance = d.totalArea.getCentre().getDistanceFrom (position);
|
||||
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
bestDistance = distance;
|
||||
best = &d;
|
||||
}
|
||||
}
|
||||
|
||||
return *best;
|
||||
}
|
||||
|
||||
const Displays::Display& Displays::findDisplayForRect (Rectangle<int> rect, bool isPhysical) const noexcept
|
||||
{
|
||||
if (auto* display = getDisplayForRect (rect, isPhysical))
|
||||
return *display;
|
||||
|
||||
return emptyDisplay;
|
||||
}
|
||||
|
||||
const Displays::Display& Displays::findDisplayForPoint (Point<int> point, bool isPhysical) const noexcept
|
||||
{
|
||||
if (auto* display = getDisplayForPoint (point, isPhysical))
|
||||
return *display;
|
||||
|
||||
return emptyDisplay;
|
||||
}
|
||||
|
||||
const Displays::Display& Displays::getMainDisplay() const noexcept
|
||||
{
|
||||
if (auto* display = getPrimaryDisplay())
|
||||
return *display;
|
||||
|
||||
return emptyDisplay;
|
||||
}
|
||||
|
||||
} // namespace juce
|
194
deps/juce/modules/juce_gui_basics/desktop/juce_Displays.h
vendored
Normal file
194
deps/juce/modules/juce_gui_basics/desktop/juce_Displays.h
vendored
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 details about connected display devices.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API Displays
|
||||
{
|
||||
private:
|
||||
Displays (Desktop&);
|
||||
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Represents a connected display device. */
|
||||
struct JUCE_API Display
|
||||
{
|
||||
/** This will be true if this is the user's main display device. */
|
||||
bool isMain;
|
||||
|
||||
/** The total area of this display in logical pixels including any OS-dependent objects
|
||||
like the taskbar, menu bar, etc.
|
||||
*/
|
||||
Rectangle<int> totalArea;
|
||||
|
||||
/** The total area of this display in logical pixels which isn't covered by OS-dependent
|
||||
objects like the taskbar, menu bar, etc.
|
||||
*/
|
||||
Rectangle<int> userArea;
|
||||
|
||||
/** Represents the area of this display in logical pixels that is not functional for
|
||||
displaying content.
|
||||
|
||||
On mobile devices this may be the area covered by display cutouts and notches, where
|
||||
you still want to draw a background but should not position important content.
|
||||
*/
|
||||
BorderSize<int> safeAreaInsets;
|
||||
|
||||
/** The top-left of this display in physical coordinates. */
|
||||
Point<int> topLeftPhysical;
|
||||
|
||||
/** The scale factor of this display.
|
||||
|
||||
For higher-resolution displays, or displays with a user-defined scale factor set,
|
||||
this may be a value other than 1.0.
|
||||
|
||||
This value is used to convert between physical and logical pixels. For example, a Component
|
||||
with size 10x10 will use 20x20 physical pixels on a display with a scale factor of 2.0.
|
||||
*/
|
||||
double scale;
|
||||
|
||||
/** The DPI of the display.
|
||||
|
||||
This is the number of physical pixels per inch. To get the number of logical
|
||||
pixels per inch, divide this by the Display::scale value.
|
||||
*/
|
||||
double dpi;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Converts an integer Rectangle from physical to logical pixels.
|
||||
|
||||
If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
|
||||
regardless of the display that the Rectangle to be converted is on.
|
||||
*/
|
||||
Rectangle<int> physicalToLogical (Rectangle<int> physicalRect,
|
||||
const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
|
||||
|
||||
/** Converts a floating-point Rectangle from physical to logical pixels.
|
||||
|
||||
If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
|
||||
regardless of the display that the Rectangle to be converted is on.
|
||||
*/
|
||||
Rectangle<float> physicalToLogical (Rectangle<float> physicalRect,
|
||||
const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
|
||||
|
||||
/** Converts an integer Rectangle from logical to physical pixels.
|
||||
|
||||
If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
|
||||
regardless of the display that the Rectangle to be converted is on.
|
||||
*/
|
||||
Rectangle<int> logicalToPhysical (Rectangle<int> logicalRect,
|
||||
const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
|
||||
|
||||
/** Converts a floating-point Rectangle from logical to physical pixels.
|
||||
|
||||
If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
|
||||
regardless of the display that the Rectangle to be converted is on.
|
||||
*/
|
||||
Rectangle<float> logicalToPhysical (Rectangle<float> logicalRect,
|
||||
const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
|
||||
|
||||
/** Converts a Point from physical to logical pixels.
|
||||
|
||||
If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
|
||||
regardless of the display that the Point to be converted is on.
|
||||
*/
|
||||
template <typename ValueType>
|
||||
Point<ValueType> physicalToLogical (Point<ValueType> physicalPoint,
|
||||
const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
|
||||
|
||||
/** Converts a Point from logical to physical pixels.
|
||||
|
||||
If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
|
||||
regardless of the display that the Point to be converted is on.
|
||||
*/
|
||||
template <typename ValueType>
|
||||
Point<ValueType> logicalToPhysical (Point<ValueType> logicalPoint,
|
||||
const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
|
||||
|
||||
/** Returns the Display object representing the display containing a given Rectangle (either
|
||||
in logical or physical pixels), or nullptr if there are no connected displays.
|
||||
|
||||
If the Rectangle lies outside all the displays then the nearest one will be returned.
|
||||
*/
|
||||
const Display* getDisplayForRect (Rectangle<int> rect, bool isPhysical = false) const noexcept;
|
||||
|
||||
/** Returns the Display object representing the display containing a given Point (either
|
||||
in logical or physical pixels), or nullptr if there are no connected displays.
|
||||
|
||||
If the Point lies outside all the displays then the nearest one will be returned.
|
||||
*/
|
||||
const Display* getDisplayForPoint (Point<int> point, bool isPhysical = false) const noexcept;
|
||||
|
||||
/** Returns the Display object representing the display acting as the user's main screen, or nullptr
|
||||
if there are no connected displays.
|
||||
*/
|
||||
const Display* getPrimaryDisplay() const noexcept;
|
||||
|
||||
/** Returns a RectangleList made up of all the displays in LOGICAL pixels. */
|
||||
RectangleList<int> getRectangleList (bool userAreasOnly) const;
|
||||
|
||||
/** Returns the smallest bounding box which contains all the displays in LOGICAL pixels. */
|
||||
Rectangle<int> getTotalBounds (bool userAreasOnly) const;
|
||||
|
||||
/** An Array containing the Display objects for all of the connected displays. */
|
||||
Array<Display> displays;
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/** @internal */
|
||||
void refresh();
|
||||
/** @internal */
|
||||
~Displays() = default;
|
||||
|
||||
[[deprecated ("Use the getDisplayForPoint or getDisplayForRect methods instead "
|
||||
"as they can deal with converting between logical and physical pixels.")]]
|
||||
const Display& getDisplayContaining (Point<int> position) const noexcept;
|
||||
|
||||
// These methods have been deprecated - use the methods which return a Display* instead as they will return
|
||||
// nullptr on headless systems with no connected displays
|
||||
[[deprecated]] const Display& findDisplayForRect (Rectangle<int>, bool isPhysical = false) const noexcept;
|
||||
[[deprecated]] const Display& findDisplayForPoint (Point<int>, bool isPhysical = false) const noexcept;
|
||||
[[deprecated]] const Display& getMainDisplay() const noexcept;
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class Desktop;
|
||||
|
||||
void init (Desktop&);
|
||||
void findDisplays (float masterScale);
|
||||
|
||||
void updateToLogical();
|
||||
|
||||
Display emptyDisplay;
|
||||
};
|
||||
|
||||
} // namespace juce
|
201
deps/juce/modules/juce_gui_basics/drawables/juce_Drawable.cpp
vendored
Normal file
201
deps/juce/modules/juce_gui_basics/drawables/juce_Drawable.cpp
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
Drawable::Drawable()
|
||||
{
|
||||
setInterceptsMouseClicks (false, false);
|
||||
setPaintingIsUnclipped (true);
|
||||
setAccessible (false);
|
||||
}
|
||||
|
||||
Drawable::Drawable (const Drawable& other)
|
||||
: Component (other.getName())
|
||||
{
|
||||
setInterceptsMouseClicks (false, false);
|
||||
setPaintingIsUnclipped (true);
|
||||
setAccessible (false);
|
||||
|
||||
setComponentID (other.getComponentID());
|
||||
setTransform (other.getTransform());
|
||||
|
||||
if (auto* clipPath = other.drawableClipPath.get())
|
||||
setClipPath (clipPath->createCopy());
|
||||
}
|
||||
|
||||
Drawable::~Drawable()
|
||||
{
|
||||
}
|
||||
|
||||
void Drawable::applyDrawableClipPath (Graphics& g)
|
||||
{
|
||||
if (drawableClipPath != nullptr)
|
||||
{
|
||||
auto clipPath = drawableClipPath->getOutlineAsPath();
|
||||
|
||||
if (! clipPath.isEmpty())
|
||||
g.getInternalContext().clipToPath (clipPath, {});
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Drawable::draw (Graphics& g, float opacity, const AffineTransform& transform) const
|
||||
{
|
||||
const_cast<Drawable*> (this)->nonConstDraw (g, opacity, transform);
|
||||
}
|
||||
|
||||
void Drawable::nonConstDraw (Graphics& g, float opacity, const AffineTransform& transform)
|
||||
{
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
|
||||
g.addTransform (AffineTransform::translation ((float) -(originRelativeToComponent.x),
|
||||
(float) -(originRelativeToComponent.y))
|
||||
.followedBy (getTransform())
|
||||
.followedBy (transform));
|
||||
|
||||
applyDrawableClipPath (g);
|
||||
|
||||
if (! g.isClipEmpty())
|
||||
{
|
||||
if (opacity < 1.0f)
|
||||
{
|
||||
g.beginTransparencyLayer (opacity);
|
||||
paintEntireComponent (g, true);
|
||||
g.endTransparencyLayer();
|
||||
}
|
||||
else
|
||||
{
|
||||
paintEntireComponent (g, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Drawable::drawAt (Graphics& g, float x, float y, float opacity) const
|
||||
{
|
||||
draw (g, opacity, AffineTransform::translation (x, y));
|
||||
}
|
||||
|
||||
void Drawable::drawWithin (Graphics& g, Rectangle<float> destArea,
|
||||
RectanglePlacement placement, float opacity) const
|
||||
{
|
||||
draw (g, opacity, placement.getTransformToFit (getDrawableBounds(), destArea));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
DrawableComposite* Drawable::getParent() const
|
||||
{
|
||||
return dynamic_cast<DrawableComposite*> (getParentComponent());
|
||||
}
|
||||
|
||||
void Drawable::setClipPath (std::unique_ptr<Drawable> clipPath)
|
||||
{
|
||||
if (drawableClipPath != clipPath)
|
||||
{
|
||||
drawableClipPath = std::move (clipPath);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void Drawable::transformContextToCorrectOrigin (Graphics& g)
|
||||
{
|
||||
g.setOrigin (originRelativeToComponent);
|
||||
}
|
||||
|
||||
void Drawable::parentHierarchyChanged()
|
||||
{
|
||||
setBoundsToEnclose (getDrawableBounds());
|
||||
}
|
||||
|
||||
void Drawable::setBoundsToEnclose (Rectangle<float> area)
|
||||
{
|
||||
Point<int> parentOrigin;
|
||||
|
||||
if (auto* parent = getParent())
|
||||
parentOrigin = parent->originRelativeToComponent;
|
||||
|
||||
auto newBounds = area.getSmallestIntegerContainer() + parentOrigin;
|
||||
originRelativeToComponent = parentOrigin - newBounds.getPosition();
|
||||
setBounds (newBounds);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool Drawable::replaceColour (Colour original, Colour replacement)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
for (auto* c : getChildren())
|
||||
if (auto* d = dynamic_cast<Drawable*> (c))
|
||||
changed = d->replaceColour (original, replacement) || changed;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Drawable::setOriginWithOriginalSize (Point<float> originWithinParent)
|
||||
{
|
||||
setTransform (AffineTransform::translation (originWithinParent.x, originWithinParent.y));
|
||||
}
|
||||
|
||||
void Drawable::setTransformToFit (const Rectangle<float>& area, RectanglePlacement placement)
|
||||
{
|
||||
if (! area.isEmpty())
|
||||
setTransform (placement.getTransformToFit (getDrawableBounds(), area));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<Drawable> Drawable::createFromImageData (const void* data, const size_t numBytes)
|
||||
{
|
||||
auto image = ImageFileFormat::loadFrom (data, numBytes);
|
||||
|
||||
if (image.isValid())
|
||||
return std::make_unique<DrawableImage> (image);
|
||||
|
||||
if (auto svg = parseXMLIfTagMatches (String::createStringFromData (data, (int) numBytes), "svg"))
|
||||
return Drawable::createFromSVG (*svg);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<Drawable> Drawable::createFromImageDataStream (InputStream& dataSource)
|
||||
{
|
||||
MemoryOutputStream mo;
|
||||
mo << dataSource;
|
||||
|
||||
return createFromImageData (mo.getData(), mo.getDataSize());
|
||||
}
|
||||
|
||||
std::unique_ptr<Drawable> Drawable::createFromImageFile (const File& file)
|
||||
{
|
||||
FileInputStream fin (file);
|
||||
|
||||
if (fin.openedOk())
|
||||
return createFromImageDataStream (fin);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace juce
|
213
deps/juce/modules/juce_gui_basics/drawables/juce_Drawable.h
vendored
Normal file
213
deps/juce/modules/juce_gui_basics/drawables/juce_Drawable.h
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
The base class for objects which can draw themselves, e.g. polygons, images, etc.
|
||||
|
||||
@see DrawableComposite, DrawableImage, DrawablePath, DrawableText
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API Drawable : public Component
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** The base class can't be instantiated directly.
|
||||
|
||||
@see DrawableComposite, DrawableImage, DrawablePath, DrawableText
|
||||
*/
|
||||
Drawable();
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
~Drawable() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a deep copy of this Drawable object.
|
||||
|
||||
Use this to create a new copy of this and any sub-objects in the tree.
|
||||
*/
|
||||
virtual std::unique_ptr<Drawable> createCopy() const = 0;
|
||||
|
||||
/** Creates a path that describes the outline of this drawable. */
|
||||
virtual Path getOutlineAsPath() const = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Renders this Drawable object.
|
||||
|
||||
Note that the preferred way to render a drawable in future is by using it
|
||||
as a component and adding it to a parent, so you might want to consider that
|
||||
before using this method.
|
||||
|
||||
@see drawWithin
|
||||
*/
|
||||
void draw (Graphics& g, float opacity,
|
||||
const AffineTransform& transform = AffineTransform()) const;
|
||||
|
||||
/** Renders the Drawable at a given offset within the Graphics context.
|
||||
|
||||
The coordinates passed-in are used to translate the object relative to its own
|
||||
origin before drawing it - this is basically a quick way of saying:
|
||||
|
||||
@code
|
||||
draw (g, AffineTransform::translation (x, y)).
|
||||
@endcode
|
||||
|
||||
Note that the preferred way to render a drawable in future is by using it
|
||||
as a component and adding it to a parent, so you might want to consider that
|
||||
before using this method.
|
||||
*/
|
||||
void drawAt (Graphics& g, float x, float y, float opacity) const;
|
||||
|
||||
/** Renders the Drawable within a rectangle, scaling it to fit neatly inside without
|
||||
changing its aspect-ratio.
|
||||
|
||||
The object can placed arbitrarily within the rectangle based on a Justification type,
|
||||
and can either be made as big as possible, or just reduced to fit.
|
||||
|
||||
Note that the preferred way to render a drawable in future is by using it
|
||||
as a component and adding it to a parent, so you might want to consider that
|
||||
before using this method.
|
||||
|
||||
@param g the graphics context to render onto
|
||||
@param destArea the target rectangle to fit the drawable into
|
||||
@param placement defines the alignment and rescaling to use to fit
|
||||
this object within the target rectangle.
|
||||
@param opacity the opacity to use, in the range 0 to 1.0
|
||||
*/
|
||||
void drawWithin (Graphics& g,
|
||||
Rectangle<float> destArea,
|
||||
RectanglePlacement placement,
|
||||
float opacity) const;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Resets any transformations on this drawable, and positions its origin within
|
||||
its parent component.
|
||||
*/
|
||||
void setOriginWithOriginalSize (Point<float> originWithinParent);
|
||||
|
||||
/** Sets a transform for this drawable that will position it within the specified
|
||||
area of its parent component.
|
||||
*/
|
||||
void setTransformToFit (const Rectangle<float>& areaInParent, RectanglePlacement placement);
|
||||
|
||||
/** Returns the DrawableComposite that contains this object, if there is one. */
|
||||
DrawableComposite* getParent() const;
|
||||
|
||||
/** Sets a the clipping region of this drawable using another drawable.
|
||||
The drawable passed in will be deleted when no longer needed.
|
||||
*/
|
||||
void setClipPath (std::unique_ptr<Drawable> drawableClipPath);
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to turn some kind of image file into a drawable.
|
||||
|
||||
The data could be an image that the ImageFileFormat class understands, or it
|
||||
could be SVG.
|
||||
*/
|
||||
static std::unique_ptr<Drawable> createFromImageData (const void* data, size_t numBytes);
|
||||
|
||||
/** Tries to turn a stream containing some kind of image data into a drawable.
|
||||
|
||||
The data could be an image that the ImageFileFormat class understands, or it
|
||||
could be SVG.
|
||||
*/
|
||||
static std::unique_ptr<Drawable> createFromImageDataStream (InputStream& dataSource);
|
||||
|
||||
/** Tries to turn a file containing some kind of image data into a drawable.
|
||||
|
||||
The data could be an image that the ImageFileFormat class understands, or it
|
||||
could be SVG.
|
||||
*/
|
||||
static std::unique_ptr<Drawable> createFromImageFile (const File& file);
|
||||
|
||||
/** Attempts to parse an SVG (Scalable Vector Graphics) document, and to turn this
|
||||
into a Drawable tree.
|
||||
|
||||
If something goes wrong while parsing, it may return nullptr.
|
||||
|
||||
SVG is a pretty large and complex spec, and this doesn't aim to be a full
|
||||
implementation, but it can return the basic vector objects.
|
||||
*/
|
||||
static std::unique_ptr<Drawable> createFromSVG (const XmlElement& svgDocument);
|
||||
|
||||
/** Attempts to parse an SVG (Scalable Vector Graphics) document from a file,
|
||||
and to turn this into a Drawable tree.
|
||||
|
||||
If something goes wrong while parsing, it may return nullptr.
|
||||
|
||||
SVG is a pretty large and complex spec, and this doesn't aim to be a full
|
||||
implementation, but it can return the basic vector objects.
|
||||
|
||||
Any references to references to external image files will be relative to
|
||||
the parent directory of the file passed.
|
||||
*/
|
||||
static std::unique_ptr<Drawable> createFromSVGFile (const File& svgFile);
|
||||
|
||||
/** Parses an SVG path string and returns it. */
|
||||
static Path parseSVGPath (const String& svgPath);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the area that this drawable covers.
|
||||
The result is expressed in this drawable's own coordinate space, and does not take
|
||||
into account any transforms that may be applied to the component.
|
||||
*/
|
||||
virtual Rectangle<float> getDrawableBounds() const = 0;
|
||||
|
||||
/** Recursively replaces a colour that might be used for filling or stroking.
|
||||
return true if any instances of this colour were found.
|
||||
*/
|
||||
virtual bool replaceColour (Colour originalColour, Colour replacementColour);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
friend class DrawableComposite;
|
||||
friend class DrawableShape;
|
||||
|
||||
/** @internal */
|
||||
void transformContextToCorrectOrigin (Graphics&);
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
void setBoundsToEnclose (Rectangle<float>);
|
||||
/** @internal */
|
||||
void applyDrawableClipPath (Graphics&);
|
||||
|
||||
Point<int> originRelativeToComponent;
|
||||
std::unique_ptr<Drawable> drawableClipPath;
|
||||
|
||||
void nonConstDraw (Graphics&, float opacity, const AffineTransform&);
|
||||
|
||||
Drawable (const Drawable&);
|
||||
Drawable& operator= (const Drawable&);
|
||||
JUCE_LEAK_DETECTOR (Drawable)
|
||||
};
|
||||
|
||||
} // namespace juce
|
164
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp
vendored
Normal file
164
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
DrawableComposite::DrawableComposite()
|
||||
: bounds ({ 0.0f, 0.0f, 100.0f, 100.0f })
|
||||
{
|
||||
setContentArea ({ 0.0f, 0.0f, 100.0f, 100.0f });
|
||||
}
|
||||
|
||||
DrawableComposite::DrawableComposite (const DrawableComposite& other)
|
||||
: Drawable (other),
|
||||
bounds (other.bounds),
|
||||
contentArea (other.contentArea)
|
||||
{
|
||||
for (auto* c : other.getChildren())
|
||||
if (auto* d = dynamic_cast<const Drawable*> (c))
|
||||
addAndMakeVisible (d->createCopy().release());
|
||||
}
|
||||
|
||||
DrawableComposite::~DrawableComposite()
|
||||
{
|
||||
deleteAllChildren();
|
||||
}
|
||||
|
||||
std::unique_ptr<Drawable> DrawableComposite::createCopy() const
|
||||
{
|
||||
return std::make_unique<DrawableComposite> (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Rectangle<float> DrawableComposite::getDrawableBounds() const
|
||||
{
|
||||
Rectangle<float> r;
|
||||
|
||||
for (auto* c : getChildren())
|
||||
if (auto* d = dynamic_cast<const Drawable*> (c))
|
||||
r = r.getUnion (d->isTransformed() ? d->getDrawableBounds().transformedBy (d->getTransform())
|
||||
: d->getDrawableBounds());
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void DrawableComposite::setContentArea (Rectangle<float> newArea)
|
||||
{
|
||||
contentArea = newArea;
|
||||
}
|
||||
|
||||
void DrawableComposite::setBoundingBox (Rectangle<float> newBounds)
|
||||
{
|
||||
setBoundingBox (Parallelogram<float> (newBounds));
|
||||
}
|
||||
|
||||
void DrawableComposite::setBoundingBox (Parallelogram<float> newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
|
||||
auto t = AffineTransform::fromTargetPoints (contentArea.getTopLeft(), bounds.topLeft,
|
||||
contentArea.getTopRight(), bounds.topRight,
|
||||
contentArea.getBottomLeft(), bounds.bottomLeft);
|
||||
|
||||
if (t.isSingularity())
|
||||
t = {};
|
||||
|
||||
setTransform (t);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableComposite::resetBoundingBoxToContentArea()
|
||||
{
|
||||
setBoundingBox (contentArea);
|
||||
}
|
||||
|
||||
void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren()
|
||||
{
|
||||
setContentArea (getDrawableBounds());
|
||||
resetBoundingBoxToContentArea();
|
||||
}
|
||||
|
||||
void DrawableComposite::parentHierarchyChanged()
|
||||
{
|
||||
if (auto* parent = getParent())
|
||||
originRelativeToComponent = parent->originRelativeToComponent - getPosition();
|
||||
}
|
||||
|
||||
void DrawableComposite::childBoundsChanged (Component*)
|
||||
{
|
||||
updateBoundsToFitChildren();
|
||||
}
|
||||
|
||||
void DrawableComposite::childrenChanged()
|
||||
{
|
||||
updateBoundsToFitChildren();
|
||||
}
|
||||
|
||||
void DrawableComposite::updateBoundsToFitChildren()
|
||||
{
|
||||
if (! updateBoundsReentrant)
|
||||
{
|
||||
const ScopedValueSetter<bool> setter (updateBoundsReentrant, true, false);
|
||||
|
||||
Rectangle<int> childArea;
|
||||
|
||||
for (auto* c : getChildren())
|
||||
childArea = childArea.getUnion (c->getBoundsInParent());
|
||||
|
||||
auto delta = childArea.getPosition();
|
||||
childArea += getPosition();
|
||||
|
||||
if (childArea != getBounds())
|
||||
{
|
||||
if (! delta.isOrigin())
|
||||
{
|
||||
originRelativeToComponent -= delta;
|
||||
|
||||
for (auto* c : getChildren())
|
||||
c->setBounds (c->getBounds() - delta);
|
||||
}
|
||||
|
||||
setBounds (childArea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Path DrawableComposite::getOutlineAsPath() const
|
||||
{
|
||||
Path p;
|
||||
|
||||
for (auto* c : getChildren())
|
||||
if (auto* d = dynamic_cast<Drawable*> (c))
|
||||
p.addPath (d->getOutlineAsPath());
|
||||
|
||||
p.applyTransform (getTransform());
|
||||
return p;
|
||||
}
|
||||
|
||||
} // namespace juce
|
117
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableComposite.h
vendored
Normal file
117
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableComposite.h
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A drawable object which acts as a container for a set of other Drawables.
|
||||
|
||||
Note that although this is a Component, it takes ownership of its child components
|
||||
and will delete them, so that you can use it as a self-contained graphic object.
|
||||
The intention is that you should not add your own components to it, only add other
|
||||
Drawable objects.
|
||||
|
||||
@see Drawable
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DrawableComposite : public Drawable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a composite Drawable. */
|
||||
DrawableComposite();
|
||||
|
||||
/** Creates a copy of a DrawableComposite. */
|
||||
DrawableComposite (const DrawableComposite&);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableComposite() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the parallelogram that defines the target position of the content rectangle when the drawable is rendered.
|
||||
@see setContentArea
|
||||
*/
|
||||
void setBoundingBox (Parallelogram<float> newBoundingBox);
|
||||
|
||||
/** Sets the rectangle that defines the target position of the content rectangle when the drawable is rendered.
|
||||
@see setContentArea
|
||||
*/
|
||||
void setBoundingBox (Rectangle<float> newBoundingBox);
|
||||
|
||||
/** Returns the parallelogram that defines the target position of the content rectangle when the drawable is rendered.
|
||||
@see setBoundingBox
|
||||
*/
|
||||
Parallelogram<float> getBoundingBox() const noexcept { return bounds; }
|
||||
|
||||
/** Changes the bounding box transform to match the content area, so that any sub-items will
|
||||
be drawn at their untransformed positions.
|
||||
*/
|
||||
void resetBoundingBoxToContentArea();
|
||||
|
||||
/** Returns the main content rectangle.
|
||||
@see contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName
|
||||
*/
|
||||
Rectangle<float> getContentArea() const noexcept { return contentArea; }
|
||||
|
||||
/** Changes the main content area.
|
||||
@see setBoundingBox, contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName
|
||||
*/
|
||||
void setContentArea (Rectangle<float> newArea);
|
||||
|
||||
/** Resets the content area and the bounding transform to fit around the area occupied
|
||||
by the child components.
|
||||
*/
|
||||
void resetContentAreaAndBoundingBoxToFitChildren();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
std::unique_ptr<Drawable> createCopy() const override;
|
||||
/** @internal */
|
||||
Rectangle<float> getDrawableBounds() const override;
|
||||
/** @internal */
|
||||
void childBoundsChanged (Component*) override;
|
||||
/** @internal */
|
||||
void childrenChanged() override;
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
Path getOutlineAsPath() const override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Parallelogram<float> bounds;
|
||||
Rectangle<float> contentArea;
|
||||
bool updateBoundsReentrant = false;
|
||||
|
||||
void updateBoundsToFitChildren();
|
||||
|
||||
DrawableComposite& operator= (const DrawableComposite&);
|
||||
JUCE_LEAK_DETECTOR (DrawableComposite)
|
||||
};
|
||||
|
||||
} // namespace juce
|
156
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp
vendored
Normal file
156
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
DrawableImage::DrawableImage() : bounds ({ 0.0f, 0.0f, 1.0f, 1.0f })
|
||||
{
|
||||
}
|
||||
|
||||
DrawableImage::DrawableImage (const DrawableImage& other)
|
||||
: Drawable (other),
|
||||
image (other.image),
|
||||
opacity (other.opacity),
|
||||
overlayColour (other.overlayColour),
|
||||
bounds (other.bounds)
|
||||
{
|
||||
setBounds (other.getBounds());
|
||||
}
|
||||
|
||||
DrawableImage::DrawableImage (const Image& imageToUse)
|
||||
{
|
||||
setImageInternal (imageToUse);
|
||||
}
|
||||
|
||||
DrawableImage::~DrawableImage()
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Drawable> DrawableImage::createCopy() const
|
||||
{
|
||||
return std::make_unique<DrawableImage> (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableImage::setImage (const Image& imageToUse)
|
||||
{
|
||||
if (setImageInternal (imageToUse))
|
||||
repaint();
|
||||
}
|
||||
|
||||
void DrawableImage::setOpacity (const float newOpacity)
|
||||
{
|
||||
opacity = newOpacity;
|
||||
}
|
||||
|
||||
void DrawableImage::setOverlayColour (Colour newOverlayColour)
|
||||
{
|
||||
overlayColour = newOverlayColour;
|
||||
}
|
||||
|
||||
void DrawableImage::setBoundingBox (Rectangle<float> newBounds)
|
||||
{
|
||||
setBoundingBox (Parallelogram<float> (newBounds));
|
||||
}
|
||||
|
||||
void DrawableImage::setBoundingBox (Parallelogram<float> newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
|
||||
if (image.isValid())
|
||||
{
|
||||
auto tr = bounds.topLeft + (bounds.topRight - bounds.topLeft) / (float) image.getWidth();
|
||||
auto bl = bounds.topLeft + (bounds.bottomLeft - bounds.topLeft) / (float) image.getHeight();
|
||||
|
||||
auto t = AffineTransform::fromTargetPoints (bounds.topLeft.x, bounds.topLeft.y,
|
||||
tr.x, tr.y,
|
||||
bl.x, bl.y);
|
||||
|
||||
if (t.isSingularity())
|
||||
t = {};
|
||||
|
||||
setTransform (t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableImage::paint (Graphics& g)
|
||||
{
|
||||
if (image.isValid())
|
||||
{
|
||||
if (opacity > 0.0f && ! overlayColour.isOpaque())
|
||||
{
|
||||
g.setOpacity (opacity);
|
||||
g.drawImageAt (image, 0, 0, false);
|
||||
}
|
||||
|
||||
if (! overlayColour.isTransparent())
|
||||
{
|
||||
g.setColour (overlayColour.withMultipliedAlpha (opacity));
|
||||
g.drawImageAt (image, 0, 0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle<float> DrawableImage::getDrawableBounds() const
|
||||
{
|
||||
return image.getBounds().toFloat();
|
||||
}
|
||||
|
||||
bool DrawableImage::hitTest (int x, int y)
|
||||
{
|
||||
return Drawable::hitTest (x, y) && image.isValid() && image.getPixelAt (x, y).getAlpha() >= 127;
|
||||
}
|
||||
|
||||
Path DrawableImage::getOutlineAsPath() const
|
||||
{
|
||||
return {}; // not applicable for images
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool DrawableImage::setImageInternal (const Image& imageToUse)
|
||||
{
|
||||
if (image != imageToUse)
|
||||
{
|
||||
image = imageToUse;
|
||||
setBounds (image.getBounds());
|
||||
setBoundingBox (image.getBounds().toFloat());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> DrawableImage::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::image);
|
||||
}
|
||||
|
||||
} // namespace juce
|
116
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableImage.h
vendored
Normal file
116
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableImage.h
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A drawable object which is a bitmap image.
|
||||
|
||||
@see Drawable
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DrawableImage : public Drawable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DrawableImage();
|
||||
DrawableImage (const DrawableImage&);
|
||||
|
||||
/** Sets the image that this drawable will render. */
|
||||
explicit DrawableImage (const Image& imageToUse);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableImage() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the image that this drawable will render. */
|
||||
void setImage (const Image& imageToUse);
|
||||
|
||||
/** Returns the current image. */
|
||||
const Image& getImage() const noexcept { return image; }
|
||||
|
||||
/** Sets the opacity to use when drawing the image. */
|
||||
void setOpacity (float newOpacity);
|
||||
|
||||
/** Returns the image's opacity. */
|
||||
float getOpacity() const noexcept { return opacity; }
|
||||
|
||||
/** Sets a colour to draw over the image's alpha channel.
|
||||
|
||||
By default this is transparent so isn't drawn, but if you set a non-transparent
|
||||
colour here, then it will be overlaid on the image, using the image's alpha
|
||||
channel as a mask.
|
||||
|
||||
This is handy for doing things like darkening or lightening an image by overlaying
|
||||
it with semi-transparent black or white.
|
||||
*/
|
||||
void setOverlayColour (Colour newOverlayColour);
|
||||
|
||||
/** Returns the overlay colour. */
|
||||
Colour getOverlayColour() const noexcept { return overlayColour; }
|
||||
|
||||
/** Sets the bounding box within which the image should be displayed. */
|
||||
void setBoundingBox (Parallelogram<float> newBounds);
|
||||
|
||||
/** Sets the bounding box within which the image should be displayed. */
|
||||
void setBoundingBox (Rectangle<float> newBounds);
|
||||
|
||||
/** Returns the position to which the image's top-left corner should be remapped in the target
|
||||
coordinate space when rendering this object.
|
||||
@see setTransform
|
||||
*/
|
||||
Parallelogram<float> getBoundingBox() const noexcept { return bounds; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
bool hitTest (int x, int y) override;
|
||||
/** @internal */
|
||||
std::unique_ptr<Drawable> createCopy() const override;
|
||||
/** @internal */
|
||||
Rectangle<float> getDrawableBounds() const override;
|
||||
/** @internal */
|
||||
Path getOutlineAsPath() const override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
bool setImageInternal (const Image&);
|
||||
|
||||
//==============================================================================
|
||||
Image image;
|
||||
float opacity = 1.0f;
|
||||
Colour overlayColour { 0 };
|
||||
Parallelogram<float> bounds;
|
||||
|
||||
DrawableImage& operator= (const DrawableImage&);
|
||||
JUCE_LEAK_DETECTOR (DrawableImage)
|
||||
};
|
||||
|
||||
} // namespace juce
|
57
deps/juce/modules/juce_gui_basics/drawables/juce_DrawablePath.cpp
vendored
Normal file
57
deps/juce/modules/juce_gui_basics/drawables/juce_DrawablePath.cpp
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
DrawablePath::DrawablePath() {}
|
||||
DrawablePath::~DrawablePath() {}
|
||||
|
||||
DrawablePath::DrawablePath (const DrawablePath& other) : DrawableShape (other)
|
||||
{
|
||||
setPath (other.path);
|
||||
}
|
||||
|
||||
std::unique_ptr<Drawable> DrawablePath::createCopy() const
|
||||
{
|
||||
return std::make_unique<DrawablePath> (*this);
|
||||
}
|
||||
|
||||
void DrawablePath::setPath (const Path& newPath)
|
||||
{
|
||||
path = newPath;
|
||||
pathChanged();
|
||||
}
|
||||
|
||||
void DrawablePath::setPath (Path&& newPath)
|
||||
{
|
||||
path = std::move (newPath);
|
||||
pathChanged();
|
||||
}
|
||||
|
||||
const Path& DrawablePath::getPath() const { return path; }
|
||||
const Path& DrawablePath::getStrokePath() const { return strokePath; }
|
||||
|
||||
} // namespace juce
|
77
deps/juce/modules/juce_gui_basics/drawables/juce_DrawablePath.h
vendored
Normal file
77
deps/juce/modules/juce_gui_basics/drawables/juce_DrawablePath.h
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A drawable object which renders a filled or outlined shape.
|
||||
|
||||
For details on how to change the fill and stroke, see the DrawableShape class.
|
||||
|
||||
@see Drawable, DrawableShape
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DrawablePath : public DrawableShape
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a DrawablePath. */
|
||||
DrawablePath();
|
||||
DrawablePath (const DrawablePath&);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawablePath() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the path that will be drawn.
|
||||
@see setFill, setStrokeType
|
||||
*/
|
||||
void setPath (const Path& newPath);
|
||||
|
||||
/** Changes the path that will be drawn.
|
||||
@see setFill, setStrokeType
|
||||
*/
|
||||
void setPath (Path&& newPath);
|
||||
|
||||
/** Returns the current path. */
|
||||
const Path& getPath() const;
|
||||
|
||||
/** Returns the current path for the outline. */
|
||||
const Path& getStrokePath() const;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
std::unique_ptr<Drawable> createCopy() const override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
DrawablePath& operator= (const DrawablePath&);
|
||||
JUCE_LEAK_DETECTOR (DrawablePath)
|
||||
};
|
||||
|
||||
} // namespace juce
|
87
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableRectangle.cpp
vendored
Normal file
87
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableRectangle.cpp
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
DrawableRectangle::DrawableRectangle() {}
|
||||
DrawableRectangle::~DrawableRectangle() {}
|
||||
|
||||
DrawableRectangle::DrawableRectangle (const DrawableRectangle& other)
|
||||
: DrawableShape (other),
|
||||
bounds (other.bounds),
|
||||
cornerSize (other.cornerSize)
|
||||
{
|
||||
rebuildPath();
|
||||
}
|
||||
|
||||
std::unique_ptr<Drawable> DrawableRectangle::createCopy() const
|
||||
{
|
||||
return std::make_unique<DrawableRectangle> (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableRectangle::setRectangle (Parallelogram<float> newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
rebuildPath();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableRectangle::setCornerSize (Point<float> newSize)
|
||||
{
|
||||
if (cornerSize != newSize)
|
||||
{
|
||||
cornerSize = newSize;
|
||||
rebuildPath();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableRectangle::rebuildPath()
|
||||
{
|
||||
auto w = bounds.getWidth();
|
||||
auto h = bounds.getHeight();
|
||||
|
||||
Path newPath;
|
||||
|
||||
if (cornerSize.x > 0 && cornerSize.y > 0)
|
||||
newPath.addRoundedRectangle (0, 0, w, h, cornerSize.x, cornerSize.y);
|
||||
else
|
||||
newPath.addRectangle (0, 0, w, h);
|
||||
|
||||
newPath.applyTransform (AffineTransform::fromTargetPoints (Point<float>(), bounds.topLeft,
|
||||
Point<float> (w, 0), bounds.topRight,
|
||||
Point<float> (0, h), bounds.bottomLeft));
|
||||
|
||||
if (path != newPath)
|
||||
{
|
||||
path.swapWithPath (newPath);
|
||||
pathChanged();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
76
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableRectangle.h
vendored
Normal file
76
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableRectangle.h
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Drawable object which draws a rectangle.
|
||||
|
||||
For details on how to change the fill and stroke, see the DrawableShape class.
|
||||
|
||||
@see Drawable, DrawableShape
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DrawableRectangle : public DrawableShape
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DrawableRectangle();
|
||||
DrawableRectangle (const DrawableRectangle&);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableRectangle() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the rectangle's bounds. */
|
||||
void setRectangle (Parallelogram<float> newBounds);
|
||||
|
||||
/** Returns the rectangle's bounds. */
|
||||
Parallelogram<float> getRectangle() const noexcept { return bounds; }
|
||||
|
||||
/** Returns the corner size to be used. */
|
||||
Point<float> getCornerSize() const noexcept { return cornerSize; }
|
||||
|
||||
/** Sets a new corner size for the rectangle */
|
||||
void setCornerSize (Point<float> newSize);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
std::unique_ptr<Drawable> createCopy() const override;
|
||||
|
||||
private:
|
||||
Parallelogram<float> bounds;
|
||||
Point<float> cornerSize;
|
||||
|
||||
void rebuildPath();
|
||||
|
||||
DrawableRectangle& operator= (const DrawableRectangle&);
|
||||
JUCE_LEAK_DETECTOR (DrawableRectangle)
|
||||
};
|
||||
|
||||
} // namespace juce
|
181
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp
vendored
Normal file
181
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
DrawableShape::DrawableShape()
|
||||
: strokeType (0.0f),
|
||||
mainFill (Colours::black),
|
||||
strokeFill (Colours::black)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableShape::DrawableShape (const DrawableShape& other)
|
||||
: Drawable (other),
|
||||
strokeType (other.strokeType),
|
||||
dashLengths (other.dashLengths),
|
||||
mainFill (other.mainFill),
|
||||
strokeFill (other.strokeFill)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableShape::~DrawableShape()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableShape::setFill (const FillType& newFill)
|
||||
{
|
||||
if (mainFill != newFill)
|
||||
{
|
||||
mainFill = newFill;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableShape::setStrokeFill (const FillType& newFill)
|
||||
{
|
||||
if (strokeFill != newFill)
|
||||
{
|
||||
strokeFill = newFill;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableShape::setStrokeType (const PathStrokeType& newStrokeType)
|
||||
{
|
||||
if (strokeType != newStrokeType)
|
||||
{
|
||||
strokeType = newStrokeType;
|
||||
strokeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableShape::setDashLengths (const Array<float>& newDashLengths)
|
||||
{
|
||||
if (dashLengths != newDashLengths)
|
||||
{
|
||||
dashLengths = newDashLengths;
|
||||
strokeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableShape::setStrokeThickness (const float newThickness)
|
||||
{
|
||||
setStrokeType (PathStrokeType (newThickness, strokeType.getJointStyle(), strokeType.getEndStyle()));
|
||||
}
|
||||
|
||||
bool DrawableShape::isStrokeVisible() const noexcept
|
||||
{
|
||||
return strokeType.getStrokeThickness() > 0.0f && ! strokeFill.isInvisible();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableShape::paint (Graphics& g)
|
||||
{
|
||||
transformContextToCorrectOrigin (g);
|
||||
applyDrawableClipPath (g);
|
||||
|
||||
g.setFillType (mainFill);
|
||||
g.fillPath (path);
|
||||
|
||||
if (isStrokeVisible())
|
||||
{
|
||||
g.setFillType (strokeFill);
|
||||
g.fillPath (strokePath);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableShape::pathChanged()
|
||||
{
|
||||
strokeChanged();
|
||||
}
|
||||
|
||||
void DrawableShape::strokeChanged()
|
||||
{
|
||||
strokePath.clear();
|
||||
const float extraAccuracy = 4.0f;
|
||||
|
||||
if (dashLengths.isEmpty())
|
||||
strokeType.createStrokedPath (strokePath, path, AffineTransform(), extraAccuracy);
|
||||
else
|
||||
strokeType.createDashedStroke (strokePath, path, dashLengths.getRawDataPointer(),
|
||||
dashLengths.size(), AffineTransform(), extraAccuracy);
|
||||
|
||||
setBoundsToEnclose (getDrawableBounds());
|
||||
repaint();
|
||||
}
|
||||
|
||||
Rectangle<float> DrawableShape::getDrawableBounds() const
|
||||
{
|
||||
if (isStrokeVisible())
|
||||
return strokePath.getBounds();
|
||||
|
||||
return path.getBounds();
|
||||
}
|
||||
|
||||
bool DrawableShape::hitTest (int x, int y)
|
||||
{
|
||||
bool allowsClicksOnThisComponent, allowsClicksOnChildComponents;
|
||||
getInterceptsMouseClicks (allowsClicksOnThisComponent, allowsClicksOnChildComponents);
|
||||
|
||||
if (! allowsClicksOnThisComponent)
|
||||
return false;
|
||||
|
||||
auto globalX = (float) (x - originRelativeToComponent.x);
|
||||
auto globalY = (float) (y - originRelativeToComponent.y);
|
||||
|
||||
return path.contains (globalX, globalY)
|
||||
|| (isStrokeVisible() && strokePath.contains (globalX, globalY));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool replaceColourInFill (FillType& fill, Colour original, Colour replacement)
|
||||
{
|
||||
if (fill.colour == original && fill.isColour())
|
||||
{
|
||||
fill = FillType (replacement);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DrawableShape::replaceColour (Colour original, Colour replacement)
|
||||
{
|
||||
bool changed1 = replaceColourInFill (mainFill, original, replacement);
|
||||
bool changed2 = replaceColourInFill (strokeFill, original, replacement);
|
||||
return changed1 || changed2;
|
||||
}
|
||||
|
||||
Path DrawableShape::getOutlineAsPath() const
|
||||
{
|
||||
auto outline = isStrokeVisible() ? strokePath : path;
|
||||
outline.applyTransform (getTransform());
|
||||
return outline;
|
||||
}
|
||||
|
||||
} // namespace juce
|
126
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableShape.h
vendored
Normal file
126
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableShape.h
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class implementing common functionality for Drawable classes which
|
||||
consist of some kind of filled and stroked outline.
|
||||
|
||||
@see DrawablePath, DrawableRectangle
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DrawableShape : public Drawable
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
DrawableShape();
|
||||
DrawableShape (const DrawableShape&);
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
~DrawableShape() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a fill type for the path.
|
||||
This colour is used to fill the path - if you don't want the path to be
|
||||
filled (e.g. if you're just drawing an outline), set this to a transparent
|
||||
colour.
|
||||
|
||||
@see setPath, setStrokeFill
|
||||
*/
|
||||
void setFill (const FillType& newFill);
|
||||
|
||||
/** Returns the current fill type.
|
||||
@see setFill
|
||||
*/
|
||||
const FillType& getFill() const noexcept { return mainFill; }
|
||||
|
||||
/** Sets the fill type with which the outline will be drawn.
|
||||
@see setFill
|
||||
*/
|
||||
void setStrokeFill (const FillType& newStrokeFill);
|
||||
|
||||
/** Returns the current stroke fill.
|
||||
@see setStrokeFill
|
||||
*/
|
||||
const FillType& getStrokeFill() const noexcept { return strokeFill; }
|
||||
|
||||
/** Changes the properties of the outline that will be drawn around the path.
|
||||
If the stroke has 0 thickness, no stroke will be drawn.
|
||||
@see setStrokeThickness, setStrokeColour
|
||||
*/
|
||||
void setStrokeType (const PathStrokeType& newStrokeType);
|
||||
|
||||
/** Changes the stroke thickness.
|
||||
This is a shortcut for calling setStrokeType.
|
||||
*/
|
||||
void setStrokeThickness (float newThickness);
|
||||
|
||||
/** Returns the current outline style. */
|
||||
const PathStrokeType& getStrokeType() const noexcept { return strokeType; }
|
||||
|
||||
/** Provides a set of dash lengths to use for stroking the path. */
|
||||
void setDashLengths (const Array<float>& newDashLengths);
|
||||
|
||||
/** Returns the set of dash lengths that the path is using. */
|
||||
const Array<float>& getDashLengths() const noexcept { return dashLengths; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
Rectangle<float> getDrawableBounds() const override;
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
bool hitTest (int x, int y) override;
|
||||
/** @internal */
|
||||
bool replaceColour (Colour originalColour, Colour replacementColour) override;
|
||||
/** @internal */
|
||||
Path getOutlineAsPath() const override;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Called when the cached path should be updated. */
|
||||
void pathChanged();
|
||||
/** Called when the cached stroke should be updated. */
|
||||
void strokeChanged();
|
||||
/** True if there's a stroke with a non-zero thickness and non-transparent colour. */
|
||||
bool isStrokeVisible() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
PathStrokeType strokeType;
|
||||
Array<float> dashLengths;
|
||||
Path path, strokePath;
|
||||
|
||||
private:
|
||||
FillType mainFill, strokeFill;
|
||||
|
||||
DrawableShape& operator= (const DrawableShape&);
|
||||
};
|
||||
|
||||
} // namespace juce
|
232
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableText.cpp
vendored
Normal file
232
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableText.cpp
vendored
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
DrawableText::DrawableText()
|
||||
: colour (Colours::black),
|
||||
justification (Justification::centredLeft)
|
||||
{
|
||||
setBoundingBox (Parallelogram<float> ({ 0.0f, 0.0f, 50.0f, 20.0f }));
|
||||
setFont (Font (15.0f), true);
|
||||
}
|
||||
|
||||
DrawableText::DrawableText (const DrawableText& other)
|
||||
: Drawable (other),
|
||||
bounds (other.bounds),
|
||||
fontHeight (other.fontHeight),
|
||||
fontHScale (other.fontHScale),
|
||||
font (other.font),
|
||||
text (other.text),
|
||||
colour (other.colour),
|
||||
justification (other.justification)
|
||||
{
|
||||
refreshBounds();
|
||||
}
|
||||
|
||||
DrawableText::~DrawableText()
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Drawable> DrawableText::createCopy() const
|
||||
{
|
||||
return std::make_unique<DrawableText> (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableText::setText (const String& newText)
|
||||
{
|
||||
if (text != newText)
|
||||
{
|
||||
text = newText;
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setColour (Colour newColour)
|
||||
{
|
||||
if (colour != newColour)
|
||||
{
|
||||
colour = newColour;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setFont (const Font& newFont, bool applySizeAndScale)
|
||||
{
|
||||
if (font != newFont)
|
||||
{
|
||||
font = newFont;
|
||||
|
||||
if (applySizeAndScale)
|
||||
{
|
||||
fontHeight = font.getHeight();
|
||||
fontHScale = font.getHorizontalScale();
|
||||
}
|
||||
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setJustification (Justification newJustification)
|
||||
{
|
||||
justification = newJustification;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void DrawableText::setBoundingBox (Parallelogram<float> newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setFontHeight (float newHeight)
|
||||
{
|
||||
if (fontHeight != newHeight)
|
||||
{
|
||||
fontHeight = newHeight;
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setFontHorizontalScale (float newScale)
|
||||
{
|
||||
if (fontHScale != newScale)
|
||||
{
|
||||
fontHScale = newScale;
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::refreshBounds()
|
||||
{
|
||||
auto w = bounds.getWidth();
|
||||
auto h = bounds.getHeight();
|
||||
|
||||
auto height = jlimit (0.01f, jmax (0.01f, h), fontHeight);
|
||||
auto hscale = jlimit (0.01f, jmax (0.01f, w), fontHScale);
|
||||
|
||||
scaledFont = font;
|
||||
scaledFont.setHeight (height);
|
||||
scaledFont.setHorizontalScale (hscale);
|
||||
|
||||
setBoundsToEnclose (getDrawableBounds());
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Rectangle<int> DrawableText::getTextArea (float w, float h) const
|
||||
{
|
||||
return Rectangle<float> (w, h).getSmallestIntegerContainer();
|
||||
}
|
||||
|
||||
AffineTransform DrawableText::getTextTransform (float w, float h) const
|
||||
{
|
||||
return AffineTransform::fromTargetPoints (Point<float>(), bounds.topLeft,
|
||||
Point<float> (w, 0), bounds.topRight,
|
||||
Point<float> (0, h), bounds.bottomLeft);
|
||||
}
|
||||
|
||||
void DrawableText::paint (Graphics& g)
|
||||
{
|
||||
transformContextToCorrectOrigin (g);
|
||||
|
||||
auto w = bounds.getWidth();
|
||||
auto h = bounds.getHeight();
|
||||
|
||||
g.addTransform (getTextTransform (w, h));
|
||||
g.setFont (scaledFont);
|
||||
g.setColour (colour);
|
||||
|
||||
g.drawFittedText (text, getTextArea (w, h), justification, 0x100000);
|
||||
}
|
||||
|
||||
Rectangle<float> DrawableText::getDrawableBounds() const
|
||||
{
|
||||
return bounds.getBoundingBox();
|
||||
}
|
||||
|
||||
Path DrawableText::getOutlineAsPath() const
|
||||
{
|
||||
auto w = bounds.getWidth();
|
||||
auto h = bounds.getHeight();
|
||||
auto area = getTextArea (w, h).toFloat();
|
||||
|
||||
GlyphArrangement arr;
|
||||
arr.addFittedText (scaledFont, text,
|
||||
area.getX(), area.getY(),
|
||||
area.getWidth(), area.getHeight(),
|
||||
justification,
|
||||
0x100000);
|
||||
|
||||
Path pathOfAllGlyphs;
|
||||
|
||||
for (auto& glyph : arr)
|
||||
{
|
||||
Path gylphPath;
|
||||
glyph.createPath (gylphPath);
|
||||
pathOfAllGlyphs.addPath (gylphPath);
|
||||
}
|
||||
|
||||
pathOfAllGlyphs.applyTransform (getTextTransform (w, h).followedBy (getTransform()));
|
||||
|
||||
return pathOfAllGlyphs;
|
||||
}
|
||||
|
||||
bool DrawableText::replaceColour (Colour originalColour, Colour replacementColour)
|
||||
{
|
||||
if (colour != originalColour)
|
||||
return false;
|
||||
|
||||
setColour (replacementColour);
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> DrawableText::createAccessibilityHandler()
|
||||
{
|
||||
class DrawableTextAccessibilityHandler : public AccessibilityHandler
|
||||
{
|
||||
public:
|
||||
DrawableTextAccessibilityHandler (DrawableText& drawableTextToWrap)
|
||||
: AccessibilityHandler (drawableTextToWrap, AccessibilityRole::staticText),
|
||||
drawableText (drawableTextToWrap)
|
||||
{
|
||||
}
|
||||
|
||||
String getTitle() const override { return drawableText.getText(); }
|
||||
|
||||
private:
|
||||
DrawableText& drawableText;
|
||||
};
|
||||
|
||||
return std::make_unique<DrawableTextAccessibilityHandler> (*this);
|
||||
}
|
||||
|
||||
} // namespace juce
|
120
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableText.h
vendored
Normal file
120
deps/juce/modules/juce_gui_basics/drawables/juce_DrawableText.h
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A drawable object which renders a line of text.
|
||||
|
||||
@see Drawable
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DrawableText : public Drawable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a DrawableText object. */
|
||||
DrawableText();
|
||||
DrawableText (const DrawableText&);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableText() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the text to display.*/
|
||||
void setText (const String& newText);
|
||||
|
||||
/** Returns the currently displayed text */
|
||||
const String& getText() const noexcept { return text;}
|
||||
|
||||
/** Sets the colour of the text. */
|
||||
void setColour (Colour newColour);
|
||||
|
||||
/** Returns the current text colour. */
|
||||
Colour getColour() const noexcept { return colour; }
|
||||
|
||||
/** Sets the font to use.
|
||||
Note that the font height and horizontal scale are set using setFontHeight() and
|
||||
setFontHorizontalScale(). If applySizeAndScale is true, then these height
|
||||
and scale values will be changed to match the dimensions of the font supplied;
|
||||
if it is false, then the new font object's height and scale are ignored.
|
||||
*/
|
||||
void setFont (const Font& newFont, bool applySizeAndScale);
|
||||
|
||||
/** Returns the current font. */
|
||||
const Font& getFont() const noexcept { return font; }
|
||||
|
||||
/** Changes the justification of the text within the bounding box. */
|
||||
void setJustification (Justification newJustification);
|
||||
|
||||
/** Returns the current justification. */
|
||||
Justification getJustification() const noexcept { return justification; }
|
||||
|
||||
/** Returns the parallelogram that defines the text bounding box. */
|
||||
Parallelogram<float> getBoundingBox() const noexcept { return bounds; }
|
||||
|
||||
/** Sets the bounding box that contains the text. */
|
||||
void setBoundingBox (Parallelogram<float> newBounds);
|
||||
|
||||
float getFontHeight() const noexcept { return fontHeight; }
|
||||
void setFontHeight (float newHeight);
|
||||
|
||||
float getFontHorizontalScale() const noexcept { return fontHScale; }
|
||||
void setFontHorizontalScale (float newScale);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
std::unique_ptr<Drawable> createCopy() const override;
|
||||
/** @internal */
|
||||
Rectangle<float> getDrawableBounds() const override;
|
||||
/** @internal */
|
||||
Path getOutlineAsPath() const override;
|
||||
/** @internal */
|
||||
bool replaceColour (Colour originalColour, Colour replacementColour) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Parallelogram<float> bounds;
|
||||
float fontHeight, fontHScale;
|
||||
Font font, scaledFont;
|
||||
String text;
|
||||
Colour colour;
|
||||
Justification justification;
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void refreshBounds();
|
||||
Rectangle<int> getTextArea (float width, float height) const;
|
||||
AffineTransform getTextTransform (float width, float height) const;
|
||||
|
||||
DrawableText& operator= (const DrawableText&);
|
||||
JUCE_LEAK_DETECTOR (DrawableText)
|
||||
};
|
||||
|
||||
} // namespace juce
|
1781
deps/juce/modules/juce_gui_basics/drawables/juce_SVGParser.cpp
vendored
Normal file
1781
deps/juce/modules/juce_gui_basics/drawables/juce_SVGParser.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
280
deps/juce/modules/juce_gui_basics/filebrowser/juce_ContentSharer.cpp
vendored
Normal file
280
deps/juce/modules/juce_gui_basics/filebrowser/juce_ContentSharer.cpp
vendored
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
#if JUCE_CONTENT_SHARING
|
||||
//==============================================================================
|
||||
class ContentSharer::PrepareImagesThread : private Thread
|
||||
{
|
||||
public:
|
||||
PrepareImagesThread (ContentSharer& cs, const Array<Image>& imagesToUse,
|
||||
ImageFileFormat* imageFileFormatToUse)
|
||||
: Thread ("ContentSharer::PrepareImagesThread"),
|
||||
owner (cs),
|
||||
images (imagesToUse),
|
||||
imageFileFormat (imageFileFormatToUse == nullptr ? new PNGImageFormat()
|
||||
: imageFileFormatToUse),
|
||||
extension (imageFileFormat->getFormatName().toLowerCase())
|
||||
{
|
||||
startThread();
|
||||
}
|
||||
|
||||
~PrepareImagesThread() override
|
||||
{
|
||||
signalThreadShouldExit();
|
||||
waitForThreadToExit (10000);
|
||||
}
|
||||
|
||||
private:
|
||||
void run() override
|
||||
{
|
||||
for (const auto& image : images)
|
||||
{
|
||||
if (threadShouldExit())
|
||||
return;
|
||||
|
||||
File tempFile = File::createTempFile (extension);
|
||||
|
||||
if (! tempFile.create().wasOk())
|
||||
break;
|
||||
|
||||
std::unique_ptr<FileOutputStream> outputStream (tempFile.createOutputStream());
|
||||
|
||||
if (outputStream == nullptr)
|
||||
break;
|
||||
|
||||
if (imageFileFormat->writeImageToStream (image, *outputStream))
|
||||
owner.temporaryFiles.add (tempFile);
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
void finish()
|
||||
{
|
||||
MessageManager::callAsync ([this]() { owner.filesToSharePrepared(); });
|
||||
}
|
||||
|
||||
ContentSharer& owner;
|
||||
const Array<Image> images;
|
||||
std::unique_ptr<ImageFileFormat> imageFileFormat;
|
||||
String extension;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ContentSharer::PrepareDataThread : private Thread
|
||||
{
|
||||
public:
|
||||
PrepareDataThread (ContentSharer& cs, const MemoryBlock& mb)
|
||||
: Thread ("ContentSharer::PrepareDataThread"),
|
||||
owner (cs),
|
||||
data (mb)
|
||||
{
|
||||
startThread();
|
||||
}
|
||||
|
||||
~PrepareDataThread() override
|
||||
{
|
||||
signalThreadShouldExit();
|
||||
waitForThreadToExit (10000);
|
||||
}
|
||||
|
||||
private:
|
||||
void run() override
|
||||
{
|
||||
File tempFile = File::createTempFile ("data");
|
||||
|
||||
if (tempFile.create().wasOk())
|
||||
{
|
||||
if (auto outputStream = std::unique_ptr<FileOutputStream> (tempFile.createOutputStream()))
|
||||
{
|
||||
size_t pos = 0;
|
||||
size_t totalSize = data.getSize();
|
||||
|
||||
while (pos < totalSize)
|
||||
{
|
||||
if (threadShouldExit())
|
||||
return;
|
||||
|
||||
size_t numToWrite = std::min ((size_t) 8192, totalSize - pos);
|
||||
|
||||
outputStream->write (data.begin() + pos, numToWrite);
|
||||
|
||||
pos += numToWrite;
|
||||
}
|
||||
|
||||
owner.temporaryFiles.add (tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
void finish()
|
||||
{
|
||||
MessageManager::callAsync ([this]() { owner.filesToSharePrepared(); });
|
||||
}
|
||||
|
||||
ContentSharer& owner;
|
||||
const MemoryBlock data;
|
||||
};
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
JUCE_IMPLEMENT_SINGLETON (ContentSharer)
|
||||
|
||||
ContentSharer::ContentSharer() {}
|
||||
ContentSharer::~ContentSharer() { clearSingletonInstance(); }
|
||||
|
||||
void ContentSharer::shareFiles (const Array<URL>& files,
|
||||
std::function<void (bool, const String&)> callbackToUse)
|
||||
{
|
||||
#if JUCE_CONTENT_SHARING
|
||||
startNewShare (callbackToUse);
|
||||
pimpl->shareFiles (files);
|
||||
#else
|
||||
ignoreUnused (files);
|
||||
|
||||
// Content sharing is not available on this platform!
|
||||
jassertfalse;
|
||||
|
||||
if (callbackToUse)
|
||||
callbackToUse (false, "Content sharing is not available on this platform!");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if JUCE_CONTENT_SHARING
|
||||
void ContentSharer::startNewShare (std::function<void (bool, const String&)> callbackToUse)
|
||||
{
|
||||
// You should not start another sharing operation before the previous one is finished.
|
||||
// Forcibly stopping a previous sharing operation is rarely a good idea!
|
||||
jassert (pimpl == nullptr);
|
||||
pimpl.reset();
|
||||
|
||||
prepareDataThread = nullptr;
|
||||
prepareImagesThread = nullptr;
|
||||
|
||||
deleteTemporaryFiles();
|
||||
|
||||
// You need to pass a valid callback.
|
||||
jassert (callbackToUse);
|
||||
callback = std::move (callbackToUse);
|
||||
|
||||
pimpl.reset (createPimpl());
|
||||
}
|
||||
#endif
|
||||
|
||||
void ContentSharer::shareText (const String& text,
|
||||
std::function<void (bool, const String&)> callbackToUse)
|
||||
{
|
||||
#if JUCE_CONTENT_SHARING
|
||||
startNewShare (callbackToUse);
|
||||
pimpl->shareText (text);
|
||||
#else
|
||||
ignoreUnused (text);
|
||||
|
||||
// Content sharing is not available on this platform!
|
||||
jassertfalse;
|
||||
|
||||
if (callbackToUse)
|
||||
callbackToUse (false, "Content sharing is not available on this platform!");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ContentSharer::shareImages (const Array<Image>& images,
|
||||
std::function<void (bool, const String&)> callbackToUse,
|
||||
ImageFileFormat* imageFileFormatToUse)
|
||||
{
|
||||
#if JUCE_CONTENT_SHARING
|
||||
startNewShare (callbackToUse);
|
||||
prepareImagesThread.reset (new PrepareImagesThread (*this, images, imageFileFormatToUse));
|
||||
#else
|
||||
ignoreUnused (images, imageFileFormatToUse);
|
||||
|
||||
// Content sharing is not available on this platform!
|
||||
jassertfalse;
|
||||
|
||||
if (callbackToUse)
|
||||
callbackToUse (false, "Content sharing is not available on this platform!");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if JUCE_CONTENT_SHARING
|
||||
void ContentSharer::filesToSharePrepared()
|
||||
{
|
||||
Array<URL> urls;
|
||||
|
||||
for (const auto& tempFile : temporaryFiles)
|
||||
urls.add (URL (tempFile));
|
||||
|
||||
prepareImagesThread = nullptr;
|
||||
prepareDataThread = nullptr;
|
||||
|
||||
pimpl->shareFiles (urls);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ContentSharer::shareData (const MemoryBlock& mb,
|
||||
std::function<void (bool, const String&)> callbackToUse)
|
||||
{
|
||||
#if JUCE_CONTENT_SHARING
|
||||
startNewShare (callbackToUse);
|
||||
prepareDataThread.reset (new PrepareDataThread (*this, mb));
|
||||
#else
|
||||
ignoreUnused (mb);
|
||||
|
||||
if (callbackToUse)
|
||||
callbackToUse (false, "Content sharing not available on this platform!");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ContentSharer::sharingFinished (bool succeeded, const String& errorDescription)
|
||||
{
|
||||
deleteTemporaryFiles();
|
||||
|
||||
std::function<void (bool, String)> cb;
|
||||
std::swap (cb, callback);
|
||||
|
||||
String error (errorDescription);
|
||||
|
||||
#if JUCE_CONTENT_SHARING
|
||||
pimpl.reset();
|
||||
#endif
|
||||
|
||||
if (cb)
|
||||
cb (succeeded, error);
|
||||
}
|
||||
|
||||
void ContentSharer::deleteTemporaryFiles()
|
||||
{
|
||||
for (auto& f : temporaryFiles)
|
||||
f.deleteFile();
|
||||
|
||||
temporaryFiles.clear();
|
||||
}
|
||||
|
||||
} // namespace juce
|
143
deps/juce/modules/juce_gui_basics/filebrowser/juce_ContentSharer.h
vendored
Normal file
143
deps/juce/modules/juce_gui_basics/filebrowser/juce_ContentSharer.h
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/** A singleton class responsible for sharing content between apps and devices.
|
||||
|
||||
You can share text, images, files or an arbitrary data block.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ContentSharer : public DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
JUCE_DECLARE_SINGLETON (ContentSharer, false)
|
||||
|
||||
/** Shares the given files. Each URL should be either a full file path
|
||||
or it should point to a resource within the application bundle. For
|
||||
resources on iOS it should be something like "content/image.png" if you
|
||||
want to specify a file from application bundle located in "content"
|
||||
directory. On Android you should specify only a filename, without an
|
||||
extension.
|
||||
|
||||
Upon completion you will receive a callback with a sharing result. Note:
|
||||
Sadly on Android the returned success flag may be wrong as there is no
|
||||
standard way the sharing targets report if the sharing operation
|
||||
succeeded. Also, the optional error message is always empty on Android.
|
||||
*/
|
||||
void shareFiles (const Array<URL>& files,
|
||||
std::function<void (bool /*success*/, const String& /*error*/)> callback);
|
||||
|
||||
/** Shares the given text.
|
||||
|
||||
Upon completion you will receive a callback with a sharing result. Note:
|
||||
Sadly on Android the returned success flag may be wrong as there is no
|
||||
standard way the sharing targets report if the sharing operation
|
||||
succeeded. Also, the optional error message is always empty on Android.
|
||||
*/
|
||||
void shareText (const String& text,
|
||||
std::function<void (bool /*success*/, const String& /*error*/)> callback);
|
||||
|
||||
/** A convenience function to share an image. This is useful when you have images
|
||||
loaded in memory. The images will be written to temporary files first, so if
|
||||
you have the images in question stored on disk already call shareFiles() instead.
|
||||
By default, images will be saved to PNG files, but you can supply a custom
|
||||
ImageFileFormat to override this. The custom file format will be owned and
|
||||
deleted by the sharer. e.g.
|
||||
|
||||
@code
|
||||
Graphics g (myImage);
|
||||
g.setColour (Colours::green);
|
||||
g.fillEllipse (20, 20, 300, 200);
|
||||
Array<Image> images;
|
||||
images.add (myImage);
|
||||
ContentSharer::getInstance()->shareImages (images, myCallback);
|
||||
@endcode
|
||||
|
||||
Upon completion you will receive a callback with a sharing result. Note:
|
||||
Sadly on Android the returned success flag may be wrong as there is no
|
||||
standard way the sharing targets report if the sharing operation
|
||||
succeeded. Also, the optional error message is always empty on Android.
|
||||
*/
|
||||
void shareImages (const Array<Image>& images,
|
||||
std::function<void (bool /*success*/, const String& /*error*/)> callback,
|
||||
ImageFileFormat* imageFileFormatToUse = nullptr);
|
||||
|
||||
/** A convenience function to share arbitrary data. The data will be written
|
||||
to a temporary file and then that file will be shared. If you have
|
||||
your data stored on disk already, call shareFiles() instead.
|
||||
|
||||
Upon completion you will receive a callback with a sharing result. Note:
|
||||
Sadly on Android the returned success flag may be wrong as there is no
|
||||
standard way the sharing targets report if the sharing operation
|
||||
succeeded. Also, the optional error message is always empty on Android.
|
||||
*/
|
||||
void shareData (const MemoryBlock& mb,
|
||||
std::function<void (bool /*success*/, const String& /*error*/)> callback);
|
||||
|
||||
private:
|
||||
ContentSharer();
|
||||
~ContentSharer();
|
||||
|
||||
Array<File> temporaryFiles;
|
||||
|
||||
std::function<void (bool, String)> callback;
|
||||
|
||||
#if JUCE_CONTENT_SHARING
|
||||
struct Pimpl
|
||||
{
|
||||
virtual ~Pimpl() {}
|
||||
virtual void shareFiles (const Array<URL>& files) = 0;
|
||||
virtual void shareText (const String& text) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
Pimpl* createPimpl();
|
||||
|
||||
void startNewShare (std::function<void (bool, const String&)>);
|
||||
|
||||
class ContentSharerNativeImpl;
|
||||
friend class ContentSharerNativeImpl;
|
||||
|
||||
class PrepareImagesThread;
|
||||
friend class PrepareImagesThread;
|
||||
std::unique_ptr<PrepareImagesThread> prepareImagesThread;
|
||||
|
||||
class PrepareDataThread;
|
||||
friend class PrepareDataThread;
|
||||
std::unique_ptr<PrepareDataThread> prepareDataThread;
|
||||
|
||||
void filesToSharePrepared();
|
||||
#endif
|
||||
|
||||
void deleteTemporaryFiles();
|
||||
void sharingFinished (bool, const String&);
|
||||
};
|
||||
|
||||
} // namespace juce
|
70
deps/juce/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.cpp
vendored
Normal file
70
deps/juce/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.cpp
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
DirectoryContentsDisplayComponent::DirectoryContentsDisplayComponent (DirectoryContentsList& l)
|
||||
: directoryContentsList (l)
|
||||
{
|
||||
}
|
||||
|
||||
DirectoryContentsDisplayComponent::~DirectoryContentsDisplayComponent()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
FileBrowserListener::~FileBrowserListener()
|
||||
{
|
||||
}
|
||||
|
||||
void DirectoryContentsDisplayComponent::addListener (FileBrowserListener* l) { listeners.add (l); }
|
||||
void DirectoryContentsDisplayComponent::removeListener (FileBrowserListener* l) { listeners.remove (l); }
|
||||
|
||||
void DirectoryContentsDisplayComponent::sendSelectionChangeMessage()
|
||||
{
|
||||
Component::BailOutChecker checker (dynamic_cast<Component*> (this));
|
||||
listeners.callChecked (checker, [] (FileBrowserListener& l) { l.selectionChanged(); });
|
||||
}
|
||||
|
||||
void DirectoryContentsDisplayComponent::sendMouseClickMessage (const File& file, const MouseEvent& e)
|
||||
{
|
||||
if (directoryContentsList.getDirectory().exists())
|
||||
{
|
||||
Component::BailOutChecker checker (dynamic_cast<Component*> (this));
|
||||
listeners.callChecked (checker, [&] (FileBrowserListener& l) { l.fileClicked (file, e); });
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryContentsDisplayComponent::sendDoubleClickMessage (const File& file)
|
||||
{
|
||||
if (directoryContentsList.getDirectory().exists())
|
||||
{
|
||||
Component::BailOutChecker checker (dynamic_cast<Component*> (this));
|
||||
listeners.callChecked (checker, [&] (FileBrowserListener& l) { l.fileDoubleClicked (file); });
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
116
deps/juce/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.h
vendored
Normal file
116
deps/juce/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.h
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for components that display a list of the files in a directory.
|
||||
|
||||
@see DirectoryContentsList
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DirectoryContentsDisplayComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a DirectoryContentsDisplayComponent for a given list of files. */
|
||||
DirectoryContentsDisplayComponent (DirectoryContentsList& listToShow);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~DirectoryContentsDisplayComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** The list that this component is displaying */
|
||||
DirectoryContentsList& directoryContentsList;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of files the user has got selected.
|
||||
@see getSelectedFile
|
||||
*/
|
||||
virtual int getNumSelectedFiles() const = 0;
|
||||
|
||||
/** Returns one of the files that the user has currently selected.
|
||||
The index should be in the range 0 to (getNumSelectedFiles() - 1).
|
||||
@see getNumSelectedFiles
|
||||
*/
|
||||
virtual File getSelectedFile (int index) const = 0;
|
||||
|
||||
/** Deselects any selected files. */
|
||||
virtual void deselectAllFiles() = 0;
|
||||
|
||||
/** Scrolls this view to the top. */
|
||||
virtual void scrollToTop() = 0;
|
||||
|
||||
/** If the specified file is in the list, it will become the only selected item
|
||||
(and if the file isn't in the list, all other items will be deselected). */
|
||||
virtual void setSelectedFile (const File&) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a listener to be told when files are selected or clicked.
|
||||
@see removeListener
|
||||
*/
|
||||
void addListener (FileBrowserListener* listener);
|
||||
|
||||
/** Removes a listener.
|
||||
@see addListener
|
||||
*/
|
||||
void removeListener (FileBrowserListener* listener);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the list.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
highlightColourId = 0x1000540, /**< The colour to use to fill a highlighted row of the list. */
|
||||
textColourId = 0x1000541, /**< The colour for the text. */
|
||||
highlightedTextColourId = 0x1000542 /**< The colour with which to draw the text in highlighted sections. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void sendSelectionChangeMessage();
|
||||
/** @internal */
|
||||
void sendDoubleClickMessage (const File&);
|
||||
/** @internal */
|
||||
void sendMouseClickMessage (const File&, const MouseEvent&);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
ListenerList<FileBrowserListener> listeners;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryContentsDisplayComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
275
deps/juce/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp
vendored
Normal file
275
deps/juce/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp
vendored
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
DirectoryContentsList::DirectoryContentsList (const FileFilter* f, TimeSliceThread& t)
|
||||
: fileFilter (f), thread (t)
|
||||
{
|
||||
}
|
||||
|
||||
DirectoryContentsList::~DirectoryContentsList()
|
||||
{
|
||||
stopSearching();
|
||||
}
|
||||
|
||||
void DirectoryContentsList::setIgnoresHiddenFiles (const bool shouldIgnoreHiddenFiles)
|
||||
{
|
||||
setTypeFlags (shouldIgnoreHiddenFiles ? (fileTypeFlags | File::ignoreHiddenFiles)
|
||||
: (fileTypeFlags & ~File::ignoreHiddenFiles));
|
||||
}
|
||||
|
||||
bool DirectoryContentsList::ignoresHiddenFiles() const
|
||||
{
|
||||
return (fileTypeFlags & File::ignoreHiddenFiles) != 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DirectoryContentsList::setDirectory (const File& directory,
|
||||
const bool includeDirectories,
|
||||
const bool includeFiles)
|
||||
{
|
||||
jassert (includeDirectories || includeFiles); // you have to specify at least one of these!
|
||||
|
||||
if (directory != root)
|
||||
{
|
||||
clear();
|
||||
root = directory;
|
||||
changed();
|
||||
|
||||
// (this forces a refresh when setTypeFlags() is called, rather than triggering two refreshes)
|
||||
fileTypeFlags &= ~(File::findDirectories | File::findFiles);
|
||||
}
|
||||
|
||||
auto newFlags = fileTypeFlags;
|
||||
|
||||
if (includeDirectories) newFlags |= File::findDirectories;
|
||||
else newFlags &= ~File::findDirectories;
|
||||
|
||||
if (includeFiles) newFlags |= File::findFiles;
|
||||
else newFlags &= ~File::findFiles;
|
||||
|
||||
setTypeFlags (newFlags);
|
||||
}
|
||||
|
||||
void DirectoryContentsList::setTypeFlags (const int newFlags)
|
||||
{
|
||||
if (fileTypeFlags != newFlags)
|
||||
{
|
||||
fileTypeFlags = newFlags;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryContentsList::stopSearching()
|
||||
{
|
||||
shouldStop = true;
|
||||
thread.removeTimeSliceClient (this);
|
||||
fileFindHandle = nullptr;
|
||||
}
|
||||
|
||||
void DirectoryContentsList::clear()
|
||||
{
|
||||
stopSearching();
|
||||
|
||||
if (! files.isEmpty())
|
||||
{
|
||||
files.clear();
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryContentsList::refresh()
|
||||
{
|
||||
stopSearching();
|
||||
wasEmpty = files.isEmpty();
|
||||
files.clear();
|
||||
|
||||
if (root.isDirectory())
|
||||
{
|
||||
fileFindHandle = std::make_unique<RangedDirectoryIterator> (root, false, "*", fileTypeFlags);
|
||||
shouldStop = false;
|
||||
thread.addTimeSliceClient (this);
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryContentsList::setFileFilter (const FileFilter* newFileFilter)
|
||||
{
|
||||
const ScopedLock sl (fileListLock);
|
||||
fileFilter = newFileFilter;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int DirectoryContentsList::getNumFiles() const noexcept
|
||||
{
|
||||
const ScopedLock sl (fileListLock);
|
||||
return files.size();
|
||||
}
|
||||
|
||||
bool DirectoryContentsList::getFileInfo (const int index, FileInfo& result) const
|
||||
{
|
||||
const ScopedLock sl (fileListLock);
|
||||
|
||||
if (auto* info = files [index])
|
||||
{
|
||||
result = *info;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
File DirectoryContentsList::getFile (const int index) const
|
||||
{
|
||||
const ScopedLock sl (fileListLock);
|
||||
|
||||
if (auto* info = files [index])
|
||||
return root.getChildFile (info->filename);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool DirectoryContentsList::contains (const File& targetFile) const
|
||||
{
|
||||
const ScopedLock sl (fileListLock);
|
||||
|
||||
for (int i = files.size(); --i >= 0;)
|
||||
if (root.getChildFile (files.getUnchecked(i)->filename) == targetFile)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DirectoryContentsList::isStillLoading() const
|
||||
{
|
||||
return fileFindHandle != nullptr;
|
||||
}
|
||||
|
||||
void DirectoryContentsList::changed()
|
||||
{
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int DirectoryContentsList::useTimeSlice()
|
||||
{
|
||||
auto startTime = Time::getApproximateMillisecondCounter();
|
||||
bool hasChanged = false;
|
||||
|
||||
for (int i = 100; --i >= 0;)
|
||||
{
|
||||
if (! checkNextFile (hasChanged))
|
||||
{
|
||||
if (hasChanged)
|
||||
changed();
|
||||
|
||||
return 500;
|
||||
}
|
||||
|
||||
if (shouldStop || (Time::getApproximateMillisecondCounter() > startTime + 150))
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasChanged)
|
||||
changed();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool DirectoryContentsList::checkNextFile (bool& hasChanged)
|
||||
{
|
||||
if (fileFindHandle != nullptr)
|
||||
{
|
||||
if (*fileFindHandle != RangedDirectoryIterator())
|
||||
{
|
||||
const auto entry = *(*fileFindHandle)++;
|
||||
|
||||
if (addFile (entry.getFile(),
|
||||
entry.isDirectory(),
|
||||
entry.getFileSize(),
|
||||
entry.getModificationTime(),
|
||||
entry.getCreationTime(),
|
||||
entry.isReadOnly()))
|
||||
{
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fileFindHandle = nullptr;
|
||||
|
||||
if (! wasEmpty && files.isEmpty())
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool DirectoryContentsList::addFile (const File& file, const bool isDir,
|
||||
const int64 fileSize,
|
||||
Time modTime, Time creationTime,
|
||||
const bool isReadOnly)
|
||||
{
|
||||
const ScopedLock sl (fileListLock);
|
||||
|
||||
if (fileFilter == nullptr
|
||||
|| ((! isDir) && fileFilter->isFileSuitable (file))
|
||||
|| (isDir && fileFilter->isDirectorySuitable (file)))
|
||||
{
|
||||
auto info = std::make_unique<FileInfo>();
|
||||
|
||||
info->filename = file.getFileName();
|
||||
info->fileSize = fileSize;
|
||||
info->modificationTime = modTime;
|
||||
info->creationTime = creationTime;
|
||||
info->isDirectory = isDir;
|
||||
info->isReadOnly = isReadOnly;
|
||||
|
||||
for (int i = files.size(); --i >= 0;)
|
||||
if (files.getUnchecked(i)->filename == info->filename)
|
||||
return false;
|
||||
|
||||
files.add (std::move (info));
|
||||
|
||||
std::sort (files.begin(), files.end(), [] (const FileInfo* a, const FileInfo* b)
|
||||
{
|
||||
// #if JUCE_WINDOWS
|
||||
if (a->isDirectory != b->isDirectory)
|
||||
return a->isDirectory;
|
||||
// #endif
|
||||
|
||||
return a->filename.compareNatural (b->filename) < 0;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
226
deps/juce/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.h
vendored
Normal file
226
deps/juce/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.h
vendored
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class to asynchronously scan for details about the files in a directory.
|
||||
|
||||
This keeps a list of files and some information about them, using a background
|
||||
thread to scan for more files. As files are found, it broadcasts change messages
|
||||
to tell any listeners.
|
||||
|
||||
@see FileListComponent, FileBrowserComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DirectoryContentsList : public ChangeBroadcaster,
|
||||
private TimeSliceClient
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a directory list.
|
||||
|
||||
To set the directory it should point to, use setDirectory(), which will
|
||||
also start it scanning for files on the background thread.
|
||||
|
||||
When the background thread finds and adds new files to this list, the
|
||||
ChangeBroadcaster class will send a change message, so you can register
|
||||
listeners and update them when the list changes.
|
||||
|
||||
@param fileFilter an optional filter to select which files are
|
||||
included in the list. If this is nullptr, then all files
|
||||
and directories are included. Make sure that the filter
|
||||
doesn't get deleted during the lifetime of this object
|
||||
@param threadToUse a thread object that this list can use
|
||||
to scan for files as a background task. Make sure
|
||||
that the thread you give it has been started, or you
|
||||
won't get any files!
|
||||
*/
|
||||
DirectoryContentsList (const FileFilter* fileFilter,
|
||||
TimeSliceThread& threadToUse);
|
||||
|
||||
/** Destructor. */
|
||||
~DirectoryContentsList() override;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the directory that's currently being used. */
|
||||
const File& getDirectory() const noexcept { return root; }
|
||||
|
||||
/** Sets the directory to look in for files.
|
||||
|
||||
If the directory that's passed in is different to the current one, this will
|
||||
also start the background thread scanning it for files.
|
||||
*/
|
||||
void setDirectory (const File& directory,
|
||||
bool includeDirectories,
|
||||
bool includeFiles);
|
||||
|
||||
/** Returns true if this list contains directories.
|
||||
@see setDirectory
|
||||
*/
|
||||
bool isFindingDirectories() const noexcept { return (fileTypeFlags & File::findDirectories) != 0; }
|
||||
|
||||
/** Returns true if this list contains files.
|
||||
@see setDirectory
|
||||
*/
|
||||
bool isFindingFiles() const noexcept { return (fileTypeFlags & File::findFiles) != 0; }
|
||||
|
||||
/** Clears the list, and stops the thread scanning for files. */
|
||||
void clear();
|
||||
|
||||
/** Clears the list and restarts scanning the directory for files. */
|
||||
void refresh();
|
||||
|
||||
/** True if the background thread hasn't yet finished scanning for files. */
|
||||
bool isStillLoading() const;
|
||||
|
||||
/** Tells the list whether or not to ignore hidden files.
|
||||
By default these are ignored.
|
||||
*/
|
||||
void setIgnoresHiddenFiles (bool shouldIgnoreHiddenFiles);
|
||||
|
||||
/** Returns true if hidden files are ignored.
|
||||
@see setIgnoresHiddenFiles
|
||||
*/
|
||||
bool ignoresHiddenFiles() const;
|
||||
|
||||
/** Replaces the current FileFilter.
|
||||
This can be nullptr to have no filter. The DirectoryContentList does not take
|
||||
ownership of this object - it just keeps a pointer to it, so you must manage its
|
||||
lifetime.
|
||||
Note that this only replaces the filter, it doesn't refresh the list - you'll
|
||||
probably want to call refresh() after calling this.
|
||||
*/
|
||||
void setFileFilter (const FileFilter* newFileFilter);
|
||||
|
||||
//==============================================================================
|
||||
/** Contains cached information about one of the files in a DirectoryContentsList.
|
||||
*/
|
||||
struct FileInfo
|
||||
{
|
||||
//==============================================================================
|
||||
/** The filename.
|
||||
|
||||
This isn't a full pathname, it's just the last part of the path, same as you'd
|
||||
get from File::getFileName().
|
||||
|
||||
To get the full pathname, use DirectoryContentsList::getDirectory().getChildFile (filename).
|
||||
*/
|
||||
String filename;
|
||||
|
||||
/** File size in bytes. */
|
||||
int64 fileSize;
|
||||
|
||||
/** File modification time.
|
||||
As supplied by File::getLastModificationTime().
|
||||
*/
|
||||
Time modificationTime;
|
||||
|
||||
/** File creation time.
|
||||
As supplied by File::getCreationTime().
|
||||
*/
|
||||
Time creationTime;
|
||||
|
||||
/** True if the file is a directory. */
|
||||
bool isDirectory;
|
||||
|
||||
/** True if the file is read-only. */
|
||||
bool isReadOnly;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of files currently available in the list.
|
||||
|
||||
The info about one of these files can be retrieved with getFileInfo() or getFile().
|
||||
|
||||
Obviously as the background thread runs and scans the directory for files, this
|
||||
number will change.
|
||||
|
||||
@see getFileInfo, getFile
|
||||
*/
|
||||
int getNumFiles() const noexcept;
|
||||
|
||||
/** Returns the cached information about one of the files in the list.
|
||||
|
||||
If the index is in-range, this will return true and will copy the file's details
|
||||
to the structure that is passed-in.
|
||||
|
||||
If it returns false, then the index wasn't in range, and the structure won't
|
||||
be affected.
|
||||
|
||||
@see getNumFiles, getFile
|
||||
*/
|
||||
bool getFileInfo (int index, FileInfo& resultInfo) const;
|
||||
|
||||
/** Returns one of the files in the list.
|
||||
|
||||
@param index should be less than getNumFiles(). If this is out-of-range, the
|
||||
return value will be a default File() object
|
||||
@see getNumFiles, getFileInfo
|
||||
*/
|
||||
File getFile (int index) const;
|
||||
|
||||
/** Returns the file filter being used.
|
||||
The filter is specified in the constructor.
|
||||
*/
|
||||
const FileFilter* getFilter() const noexcept { return fileFilter; }
|
||||
|
||||
/** Returns true if the list contains the specified file. */
|
||||
bool contains (const File&) const;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
TimeSliceThread& getTimeSliceThread() const noexcept { return thread; }
|
||||
|
||||
private:
|
||||
File root;
|
||||
const FileFilter* fileFilter = nullptr;
|
||||
TimeSliceThread& thread;
|
||||
int fileTypeFlags = File::ignoreHiddenFiles | File::findFiles;
|
||||
|
||||
CriticalSection fileListLock;
|
||||
OwnedArray<FileInfo> files;
|
||||
|
||||
std::unique_ptr<RangedDirectoryIterator> fileFindHandle;
|
||||
std::atomic<bool> shouldStop { true };
|
||||
|
||||
bool wasEmpty = true;
|
||||
|
||||
int useTimeSlice() override;
|
||||
void stopSearching();
|
||||
void changed();
|
||||
bool checkNextFile (bool& hasChanged);
|
||||
bool addFile (const File&, bool isDir, int64 fileSize, Time modTime,
|
||||
Time creationTime, bool isReadOnly);
|
||||
void setTypeFlags (int);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryContentsList)
|
||||
};
|
||||
|
||||
} // namespace juce
|
625
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp
vendored
Normal file
625
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp
vendored
Normal file
@ -0,0 +1,625 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
FileBrowserComponent::FileBrowserComponent (int flags_,
|
||||
const File& initialFileOrDirectory,
|
||||
const FileFilter* fileFilter_,
|
||||
FilePreviewComponent* previewComp_)
|
||||
: FileFilter ({}),
|
||||
fileFilter (fileFilter_),
|
||||
flags (flags_),
|
||||
previewComp (previewComp_),
|
||||
currentPathBox ("path"),
|
||||
fileLabel ("f", TRANS ("file:")),
|
||||
thread ("JUCE FileBrowser"),
|
||||
wasProcessActive (true)
|
||||
{
|
||||
// You need to specify one or other of the open/save flags..
|
||||
jassert ((flags & (saveMode | openMode)) != 0);
|
||||
jassert ((flags & (saveMode | openMode)) != (saveMode | openMode));
|
||||
|
||||
// You need to specify at least one of these flags..
|
||||
jassert ((flags & (canSelectFiles | canSelectDirectories)) != 0);
|
||||
|
||||
String filename;
|
||||
|
||||
if (initialFileOrDirectory == File())
|
||||
{
|
||||
currentRoot = File::getCurrentWorkingDirectory();
|
||||
}
|
||||
else if (initialFileOrDirectory.isDirectory())
|
||||
{
|
||||
currentRoot = initialFileOrDirectory;
|
||||
}
|
||||
else
|
||||
{
|
||||
chosenFiles.add (initialFileOrDirectory);
|
||||
currentRoot = initialFileOrDirectory.getParentDirectory();
|
||||
filename = initialFileOrDirectory.getFileName();
|
||||
}
|
||||
|
||||
fileList.reset (new DirectoryContentsList (this, thread));
|
||||
fileList->setDirectory (currentRoot, true, true);
|
||||
|
||||
if ((flags & useTreeView) != 0)
|
||||
{
|
||||
auto tree = new FileTreeComponent (*fileList);
|
||||
fileListComponent.reset (tree);
|
||||
|
||||
if ((flags & canSelectMultipleItems) != 0)
|
||||
tree->setMultiSelectEnabled (true);
|
||||
|
||||
addAndMakeVisible (tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto list = new FileListComponent (*fileList);
|
||||
fileListComponent.reset (list);
|
||||
list->setOutlineThickness (1);
|
||||
|
||||
if ((flags & canSelectMultipleItems) != 0)
|
||||
list->setMultipleSelectionEnabled (true);
|
||||
|
||||
addAndMakeVisible (list);
|
||||
}
|
||||
|
||||
fileListComponent->addListener (this);
|
||||
|
||||
addAndMakeVisible (currentPathBox);
|
||||
currentPathBox.setEditableText (true);
|
||||
resetRecentPaths();
|
||||
currentPathBox.onChange = [this] { updateSelectedPath(); };
|
||||
|
||||
addAndMakeVisible (filenameBox);
|
||||
filenameBox.setMultiLine (false);
|
||||
filenameBox.setSelectAllWhenFocused (true);
|
||||
filenameBox.setText (filename, false);
|
||||
filenameBox.onTextChange = [this] { sendListenerChangeMessage(); };
|
||||
filenameBox.onReturnKey = [this] { changeFilename(); };
|
||||
filenameBox.onFocusLost = [this]
|
||||
{
|
||||
if (! isSaveMode())
|
||||
selectionChanged();
|
||||
};
|
||||
|
||||
filenameBox.setReadOnly ((flags & (filenameBoxIsReadOnly | canSelectMultipleItems)) != 0);
|
||||
|
||||
addAndMakeVisible (fileLabel);
|
||||
fileLabel.attachToComponent (&filenameBox, true);
|
||||
|
||||
if (previewComp != nullptr)
|
||||
addAndMakeVisible (previewComp);
|
||||
|
||||
lookAndFeelChanged();
|
||||
|
||||
setRoot (currentRoot);
|
||||
|
||||
if (filename.isNotEmpty())
|
||||
setFileName (filename);
|
||||
|
||||
thread.startThread (4);
|
||||
|
||||
startTimer (2000);
|
||||
}
|
||||
|
||||
FileBrowserComponent::~FileBrowserComponent()
|
||||
{
|
||||
fileListComponent.reset();
|
||||
fileList.reset();
|
||||
thread.stopThread (10000);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileBrowserComponent::addListener (FileBrowserListener* const newListener)
|
||||
{
|
||||
listeners.add (newListener);
|
||||
}
|
||||
|
||||
void FileBrowserComponent::removeListener (FileBrowserListener* const listener)
|
||||
{
|
||||
listeners.remove (listener);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool FileBrowserComponent::isSaveMode() const noexcept
|
||||
{
|
||||
return (flags & saveMode) != 0;
|
||||
}
|
||||
|
||||
int FileBrowserComponent::getNumSelectedFiles() const noexcept
|
||||
{
|
||||
if (chosenFiles.isEmpty() && currentFileIsValid())
|
||||
return 1;
|
||||
|
||||
return chosenFiles.size();
|
||||
}
|
||||
|
||||
File FileBrowserComponent::getSelectedFile (int index) const noexcept
|
||||
{
|
||||
if ((flags & canSelectDirectories) != 0 && filenameBox.getText().isEmpty())
|
||||
return currentRoot;
|
||||
|
||||
if (! filenameBox.isReadOnly())
|
||||
return currentRoot.getChildFile (filenameBox.getText());
|
||||
|
||||
return chosenFiles[index];
|
||||
}
|
||||
|
||||
bool FileBrowserComponent::currentFileIsValid() const
|
||||
{
|
||||
auto f = getSelectedFile (0);
|
||||
|
||||
if ((flags & canSelectDirectories) == 0 && f.isDirectory())
|
||||
return false;
|
||||
|
||||
return isSaveMode() || f.exists();
|
||||
}
|
||||
|
||||
File FileBrowserComponent::getHighlightedFile() const noexcept
|
||||
{
|
||||
return fileListComponent->getSelectedFile (0);
|
||||
}
|
||||
|
||||
void FileBrowserComponent::deselectAllFiles()
|
||||
{
|
||||
fileListComponent->deselectAllFiles();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool FileBrowserComponent::isFileSuitable (const File& file) const
|
||||
{
|
||||
return (flags & canSelectFiles) != 0
|
||||
&& (fileFilter == nullptr || fileFilter->isFileSuitable (file));
|
||||
}
|
||||
|
||||
bool FileBrowserComponent::isDirectorySuitable (const File&) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileBrowserComponent::isFileOrDirSuitable (const File& f) const
|
||||
{
|
||||
if (f.isDirectory())
|
||||
return (flags & canSelectDirectories) != 0
|
||||
&& (fileFilter == nullptr || fileFilter->isDirectorySuitable (f));
|
||||
|
||||
return (flags & canSelectFiles) != 0 && f.exists()
|
||||
&& (fileFilter == nullptr || fileFilter->isFileSuitable (f));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const File& FileBrowserComponent::getRoot() const
|
||||
{
|
||||
return currentRoot;
|
||||
}
|
||||
|
||||
void FileBrowserComponent::setRoot (const File& newRootDirectory)
|
||||
{
|
||||
bool callListeners = false;
|
||||
|
||||
if (currentRoot != newRootDirectory)
|
||||
{
|
||||
callListeners = true;
|
||||
fileListComponent->scrollToTop();
|
||||
|
||||
String path (newRootDirectory.getFullPathName());
|
||||
|
||||
if (path.isEmpty())
|
||||
path = File::getSeparatorString();
|
||||
|
||||
StringArray rootNames, rootPaths;
|
||||
getRoots (rootNames, rootPaths);
|
||||
|
||||
if (! rootPaths.contains (path, true))
|
||||
{
|
||||
bool alreadyListed = false;
|
||||
|
||||
for (int i = currentPathBox.getNumItems(); --i >= 0;)
|
||||
{
|
||||
if (currentPathBox.getItemText (i).equalsIgnoreCase (path))
|
||||
{
|
||||
alreadyListed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! alreadyListed)
|
||||
currentPathBox.addItem (path, currentPathBox.getNumItems() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
currentRoot = newRootDirectory;
|
||||
fileList->setDirectory (currentRoot, true, true);
|
||||
|
||||
if (auto* tree = dynamic_cast<FileTreeComponent*> (fileListComponent.get()))
|
||||
tree->refresh();
|
||||
|
||||
auto currentRootName = currentRoot.getFullPathName();
|
||||
|
||||
if (currentRootName.isEmpty())
|
||||
currentRootName = File::getSeparatorString();
|
||||
|
||||
currentPathBox.setText (currentRootName, dontSendNotification);
|
||||
|
||||
goUpButton->setEnabled (currentRoot.getParentDirectory().isDirectory()
|
||||
&& currentRoot.getParentDirectory() != currentRoot);
|
||||
|
||||
if (callListeners)
|
||||
{
|
||||
Component::BailOutChecker checker (this);
|
||||
listeners.callChecked (checker, [&] (FileBrowserListener& l) { l.browserRootChanged (currentRoot); });
|
||||
}
|
||||
}
|
||||
|
||||
void FileBrowserComponent::setFileName (const String& newName)
|
||||
{
|
||||
filenameBox.setText (newName, true);
|
||||
|
||||
fileListComponent->setSelectedFile (currentRoot.getChildFile (newName));
|
||||
}
|
||||
|
||||
void FileBrowserComponent::resetRecentPaths()
|
||||
{
|
||||
currentPathBox.clear();
|
||||
|
||||
StringArray rootNames, rootPaths;
|
||||
getRoots (rootNames, rootPaths);
|
||||
|
||||
for (int i = 0; i < rootNames.size(); ++i)
|
||||
{
|
||||
if (rootNames[i].isEmpty())
|
||||
currentPathBox.addSeparator();
|
||||
else
|
||||
currentPathBox.addItem (rootNames[i], i + 1);
|
||||
}
|
||||
|
||||
currentPathBox.addSeparator();
|
||||
}
|
||||
|
||||
void FileBrowserComponent::goUp()
|
||||
{
|
||||
setRoot (getRoot().getParentDirectory());
|
||||
}
|
||||
|
||||
void FileBrowserComponent::refresh()
|
||||
{
|
||||
fileList->refresh();
|
||||
}
|
||||
|
||||
void FileBrowserComponent::setFileFilter (const FileFilter* const newFileFilter)
|
||||
{
|
||||
if (fileFilter != newFileFilter)
|
||||
{
|
||||
fileFilter = newFileFilter;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
String FileBrowserComponent::getActionVerb() const
|
||||
{
|
||||
return isSaveMode() ? ((flags & canSelectDirectories) != 0 ? TRANS("Choose")
|
||||
: TRANS("Save"))
|
||||
: TRANS("Open");
|
||||
}
|
||||
|
||||
void FileBrowserComponent::setFilenameBoxLabel (const String& name)
|
||||
{
|
||||
fileLabel.setText (name, dontSendNotification);
|
||||
}
|
||||
|
||||
FilePreviewComponent* FileBrowserComponent::getPreviewComponent() const noexcept
|
||||
{
|
||||
return previewComp;
|
||||
}
|
||||
|
||||
DirectoryContentsDisplayComponent* FileBrowserComponent::getDisplayComponent() const noexcept
|
||||
{
|
||||
return fileListComponent.get();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileBrowserComponent::resized()
|
||||
{
|
||||
getLookAndFeel()
|
||||
.layoutFileBrowserComponent (*this, fileListComponent.get(), previewComp,
|
||||
¤tPathBox, &filenameBox, goUpButton.get());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileBrowserComponent::lookAndFeelChanged()
|
||||
{
|
||||
goUpButton.reset (getLookAndFeel().createFileBrowserGoUpButton());
|
||||
|
||||
if (auto* buttonPtr = goUpButton.get())
|
||||
{
|
||||
addAndMakeVisible (*buttonPtr);
|
||||
buttonPtr->onClick = [this] { goUp(); };
|
||||
buttonPtr->setTooltip (TRANS ("Go up to parent directory"));
|
||||
}
|
||||
|
||||
currentPathBox.setColour (ComboBox::backgroundColourId, findColour (currentPathBoxBackgroundColourId));
|
||||
currentPathBox.setColour (ComboBox::textColourId, findColour (currentPathBoxTextColourId));
|
||||
currentPathBox.setColour (ComboBox::arrowColourId, findColour (currentPathBoxArrowColourId));
|
||||
|
||||
filenameBox.setColour (TextEditor::backgroundColourId, findColour (filenameBoxBackgroundColourId));
|
||||
filenameBox.applyColourToAllText (findColour (filenameBoxTextColourId));
|
||||
|
||||
resized();
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileBrowserComponent::sendListenerChangeMessage()
|
||||
{
|
||||
Component::BailOutChecker checker (this);
|
||||
|
||||
if (previewComp != nullptr)
|
||||
previewComp->selectedFileChanged (getSelectedFile (0));
|
||||
|
||||
// You shouldn't delete the browser when the file gets changed!
|
||||
jassert (! checker.shouldBailOut());
|
||||
|
||||
listeners.callChecked (checker, [] (FileBrowserListener& l) { l.selectionChanged(); });
|
||||
}
|
||||
|
||||
void FileBrowserComponent::selectionChanged()
|
||||
{
|
||||
StringArray newFilenames;
|
||||
bool resetChosenFiles = true;
|
||||
|
||||
for (int i = 0; i < fileListComponent->getNumSelectedFiles(); ++i)
|
||||
{
|
||||
const File f (fileListComponent->getSelectedFile (i));
|
||||
|
||||
if (isFileOrDirSuitable (f))
|
||||
{
|
||||
if (resetChosenFiles)
|
||||
{
|
||||
chosenFiles.clear();
|
||||
resetChosenFiles = false;
|
||||
}
|
||||
|
||||
chosenFiles.add (f);
|
||||
newFilenames.add (f.getRelativePathFrom (getRoot()));
|
||||
}
|
||||
}
|
||||
|
||||
if (newFilenames.size() > 0)
|
||||
filenameBox.setText (newFilenames.joinIntoString (", "), false);
|
||||
|
||||
sendListenerChangeMessage();
|
||||
}
|
||||
|
||||
void FileBrowserComponent::fileClicked (const File& f, const MouseEvent& e)
|
||||
{
|
||||
Component::BailOutChecker checker (this);
|
||||
listeners.callChecked (checker, [&] (FileBrowserListener& l) { l.fileClicked (f, e); });
|
||||
}
|
||||
|
||||
void FileBrowserComponent::fileDoubleClicked (const File& f)
|
||||
{
|
||||
if (f.isDirectory())
|
||||
{
|
||||
setRoot (f);
|
||||
|
||||
if ((flags & canSelectDirectories) != 0 && (flags & doNotClearFileNameOnRootChange) == 0)
|
||||
filenameBox.setText ({});
|
||||
}
|
||||
else
|
||||
{
|
||||
Component::BailOutChecker checker (this);
|
||||
listeners.callChecked (checker, [&] (FileBrowserListener& l) { l.fileDoubleClicked (f); });
|
||||
}
|
||||
}
|
||||
|
||||
void FileBrowserComponent::browserRootChanged (const File&) {}
|
||||
|
||||
bool FileBrowserComponent::keyPressed (const KeyPress& key)
|
||||
{
|
||||
#if JUCE_LINUX || JUCE_BSD || JUCE_WINDOWS
|
||||
if (key.getModifiers().isCommandDown()
|
||||
&& (key.getKeyCode() == 'H' || key.getKeyCode() == 'h'))
|
||||
{
|
||||
fileList->setIgnoresHiddenFiles (! fileList->ignoresHiddenFiles());
|
||||
fileList->refresh();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
ignoreUnused (key);
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileBrowserComponent::changeFilename()
|
||||
{
|
||||
if (filenameBox.getText().containsChar (File::getSeparatorChar()))
|
||||
{
|
||||
auto f = currentRoot.getChildFile (filenameBox.getText());
|
||||
|
||||
if (f.isDirectory())
|
||||
{
|
||||
setRoot (f);
|
||||
chosenFiles.clear();
|
||||
|
||||
if ((flags & doNotClearFileNameOnRootChange) == 0)
|
||||
filenameBox.setText ({});
|
||||
}
|
||||
else
|
||||
{
|
||||
setRoot (f.getParentDirectory());
|
||||
chosenFiles.clear();
|
||||
chosenFiles.add (f);
|
||||
filenameBox.setText (f.getFileName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fileDoubleClicked (getSelectedFile (0));
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileBrowserComponent::updateSelectedPath()
|
||||
{
|
||||
auto newText = currentPathBox.getText().trim().unquoted();
|
||||
|
||||
if (newText.isNotEmpty())
|
||||
{
|
||||
auto index = currentPathBox.getSelectedId() - 1;
|
||||
|
||||
StringArray rootNames, rootPaths;
|
||||
getRoots (rootNames, rootPaths);
|
||||
|
||||
if (rootPaths[index].isNotEmpty())
|
||||
{
|
||||
setRoot (File (rootPaths[index]));
|
||||
}
|
||||
else
|
||||
{
|
||||
File f (newText);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (f.isDirectory())
|
||||
{
|
||||
setRoot (f);
|
||||
break;
|
||||
}
|
||||
|
||||
if (f.getParentDirectory() == f)
|
||||
break;
|
||||
|
||||
f = f.getParentDirectory();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileBrowserComponent::getDefaultRoots (StringArray& rootNames, StringArray& rootPaths)
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
Array<File> roots;
|
||||
File::findFileSystemRoots (roots);
|
||||
rootPaths.clear();
|
||||
|
||||
for (int i = 0; i < roots.size(); ++i)
|
||||
{
|
||||
const File& drive = roots.getReference(i);
|
||||
|
||||
String name (drive.getFullPathName());
|
||||
rootPaths.add (name);
|
||||
|
||||
if (drive.isOnHardDisk())
|
||||
{
|
||||
String volume (drive.getVolumeLabel());
|
||||
|
||||
if (volume.isEmpty())
|
||||
volume = TRANS("Hard Drive");
|
||||
|
||||
name << " [" << volume << ']';
|
||||
}
|
||||
else if (drive.isOnCDRomDrive())
|
||||
{
|
||||
name << " [" << TRANS("CD/DVD drive") << ']';
|
||||
}
|
||||
|
||||
rootNames.add (name);
|
||||
}
|
||||
|
||||
rootPaths.add ({});
|
||||
rootNames.add ({});
|
||||
|
||||
rootPaths.add (File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName());
|
||||
rootNames.add (TRANS("Documents"));
|
||||
rootPaths.add (File::getSpecialLocation (File::userMusicDirectory).getFullPathName());
|
||||
rootNames.add (TRANS("Music"));
|
||||
rootPaths.add (File::getSpecialLocation (File::userPicturesDirectory).getFullPathName());
|
||||
rootNames.add (TRANS("Pictures"));
|
||||
rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName());
|
||||
rootNames.add (TRANS("Desktop"));
|
||||
|
||||
#elif JUCE_MAC
|
||||
rootPaths.add (File::getSpecialLocation (File::userHomeDirectory).getFullPathName());
|
||||
rootNames.add (TRANS("Home folder"));
|
||||
rootPaths.add (File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName());
|
||||
rootNames.add (TRANS("Documents"));
|
||||
rootPaths.add (File::getSpecialLocation (File::userMusicDirectory).getFullPathName());
|
||||
rootNames.add (TRANS("Music"));
|
||||
rootPaths.add (File::getSpecialLocation (File::userPicturesDirectory).getFullPathName());
|
||||
rootNames.add (TRANS("Pictures"));
|
||||
rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName());
|
||||
rootNames.add (TRANS("Desktop"));
|
||||
|
||||
rootPaths.add ({});
|
||||
rootNames.add ({});
|
||||
|
||||
for (auto& volume : File ("/Volumes").findChildFiles (File::findDirectories, false))
|
||||
{
|
||||
if (volume.isDirectory() && ! volume.getFileName().startsWithChar ('.'))
|
||||
{
|
||||
rootPaths.add (volume.getFullPathName());
|
||||
rootNames.add (volume.getFileName());
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
rootPaths.add ("/");
|
||||
rootNames.add ("/");
|
||||
rootPaths.add (File::getSpecialLocation (File::userHomeDirectory).getFullPathName());
|
||||
rootNames.add (TRANS("Home folder"));
|
||||
rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName());
|
||||
rootNames.add (TRANS("Desktop"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void FileBrowserComponent::getRoots (StringArray& rootNames, StringArray& rootPaths)
|
||||
{
|
||||
getDefaultRoots (rootNames, rootPaths);
|
||||
}
|
||||
|
||||
void FileBrowserComponent::timerCallback()
|
||||
{
|
||||
const auto isProcessActive = isForegroundOrEmbeddedProcess (this);
|
||||
|
||||
if (wasProcessActive != isProcessActive)
|
||||
{
|
||||
wasProcessActive = isProcessActive;
|
||||
|
||||
if (isProcessActive && fileList != nullptr)
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> FileBrowserComponent::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
|
||||
}
|
||||
|
||||
} // namespace juce
|
296
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h
vendored
Normal file
296
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h
vendored
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component for browsing and selecting a file or directory to open or save.
|
||||
|
||||
This contains a FileListComponent and adds various boxes and controls for
|
||||
navigating and selecting a file. It can work in different modes so that it can
|
||||
be used for loading or saving a file, or for choosing a directory.
|
||||
|
||||
@see FileChooserDialogBox, FileChooser, FileListComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FileBrowserComponent : public Component,
|
||||
private FileBrowserListener,
|
||||
private FileFilter,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Various options for the browser.
|
||||
|
||||
A combination of these is passed into the FileBrowserComponent constructor.
|
||||
*/
|
||||
enum FileChooserFlags
|
||||
{
|
||||
openMode = 1, /**< specifies that the component should allow the user to
|
||||
choose an existing file with the intention of opening it. */
|
||||
saveMode = 2, /**< specifies that the component should allow the user to specify
|
||||
the name of a file that will be used to save something. */
|
||||
canSelectFiles = 4, /**< specifies that the user can select files (can be used in
|
||||
conjunction with canSelectDirectories). */
|
||||
canSelectDirectories = 8, /**< specifies that the user can select directories (can be used in
|
||||
conjunction with canSelectFiles). */
|
||||
canSelectMultipleItems = 16, /**< specifies that the user can select multiple items. */
|
||||
useTreeView = 32, /**< specifies that a tree-view should be shown instead of a file list. */
|
||||
filenameBoxIsReadOnly = 64, /**< specifies that the user can't type directly into the filename box. */
|
||||
warnAboutOverwriting = 128, /**< specifies that the dialog should warn about overwriting existing files (if possible). */
|
||||
doNotClearFileNameOnRootChange = 256 /**< specifies that the file name should not be cleared upon root change. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a FileBrowserComponent.
|
||||
|
||||
@param flags A combination of flags from the FileChooserFlags enumeration, used to
|
||||
specify the component's behaviour. The flags must contain either openMode
|
||||
or saveMode, and canSelectFiles and/or canSelectDirectories.
|
||||
@param initialFileOrDirectory The file or directory that should be selected when the component begins.
|
||||
If this is File(), a default directory will be chosen.
|
||||
@param fileFilter an optional filter to use to determine which files are shown.
|
||||
If this is nullptr then all files are displayed. Note that a pointer
|
||||
is kept internally to this object, so make sure that it is not deleted
|
||||
before the FileBrowserComponent object is deleted.
|
||||
@param previewComp an optional preview component that will be used to show previews of
|
||||
files that the user selects
|
||||
*/
|
||||
FileBrowserComponent (int flags,
|
||||
const File& initialFileOrDirectory,
|
||||
const FileFilter* fileFilter,
|
||||
FilePreviewComponent* previewComp);
|
||||
|
||||
/** Destructor. */
|
||||
~FileBrowserComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of files that the user has got selected.
|
||||
If multiple select isn't active, this will only be 0 or 1. To get the complete
|
||||
list of files they've chosen, pass an index to getCurrentFile().
|
||||
*/
|
||||
int getNumSelectedFiles() const noexcept;
|
||||
|
||||
/** Returns one of the files that the user has chosen.
|
||||
If the box has multi-select enabled, the index lets you specify which of the files
|
||||
to get - see getNumSelectedFiles() to find out how many files were chosen.
|
||||
@see getHighlightedFile
|
||||
*/
|
||||
File getSelectedFile (int index) const noexcept;
|
||||
|
||||
/** Deselects any files that are currently selected. */
|
||||
void deselectAllFiles();
|
||||
|
||||
/** Returns true if the currently selected file(s) are usable.
|
||||
|
||||
This can be used to decide whether the user can press "ok" for the
|
||||
current file. What it does depends on the mode, so for example in an "open"
|
||||
mode, this only returns true if a file has been selected and if it exists.
|
||||
In a "save" mode, a non-existent file would also be valid.
|
||||
*/
|
||||
bool currentFileIsValid() const;
|
||||
|
||||
/** This returns the last item in the view that the user has highlighted.
|
||||
This may be different from getCurrentFile(), which returns the value
|
||||
that is shown in the filename box, and if there are multiple selections,
|
||||
this will only return one of them.
|
||||
@see getSelectedFile
|
||||
*/
|
||||
File getHighlightedFile() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the directory whose contents are currently being shown in the listbox. */
|
||||
const File& getRoot() const;
|
||||
|
||||
/** Changes the directory that's being shown in the listbox. */
|
||||
void setRoot (const File& newRootDirectory);
|
||||
|
||||
/** Changes the name that is currently shown in the filename box. */
|
||||
void setFileName (const String& newName);
|
||||
|
||||
/** Equivalent to pressing the "up" button to browse the parent directory. */
|
||||
void goUp();
|
||||
|
||||
/** Refreshes the directory that's currently being listed. */
|
||||
void refresh();
|
||||
|
||||
/** Changes the filter that's being used to sift the files. */
|
||||
void setFileFilter (const FileFilter* newFileFilter);
|
||||
|
||||
/** Returns a verb to describe what should happen when the file is accepted.
|
||||
|
||||
E.g. if browsing in "load file" mode, this will be "Open", if in "save file"
|
||||
mode, it'll be "Save", etc.
|
||||
*/
|
||||
virtual String getActionVerb() const;
|
||||
|
||||
/** Returns true if the saveMode flag was set when this component was created. */
|
||||
bool isSaveMode() const noexcept;
|
||||
|
||||
/** Sets the label that will be displayed next to the filename entry box.
|
||||
By default this is just "file", but you might want to change it to something more
|
||||
appropriate for your app.
|
||||
*/
|
||||
void setFilenameBoxLabel (const String& name);
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a listener to be told when the user selects and clicks on files.
|
||||
@see removeListener
|
||||
*/
|
||||
void addListener (FileBrowserListener* listener);
|
||||
|
||||
/** Removes a listener.
|
||||
@see addListener
|
||||
*/
|
||||
void removeListener (FileBrowserListener* listener);
|
||||
|
||||
/** Returns a platform-specific list of names and paths for some suggested places the user
|
||||
might want to use as root folders.
|
||||
The list returned contains empty strings to indicate section breaks.
|
||||
@see getRoots()
|
||||
*/
|
||||
static void getDefaultRoots (StringArray& rootNames, StringArray& rootPaths);
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||
various file-browser layout and drawing methods.
|
||||
*/
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
// These return a pointer to an internally cached drawable - make sure you don't keep
|
||||
// a copy of this pointer anywhere, as it may become invalid in the future.
|
||||
virtual const Drawable* getDefaultFolderImage() = 0;
|
||||
virtual const Drawable* getDefaultDocumentFileImage() = 0;
|
||||
|
||||
virtual AttributedString createFileChooserHeaderText (const String& title,
|
||||
const String& instructions) = 0;
|
||||
|
||||
virtual void drawFileBrowserRow (Graphics&, int width, int height,
|
||||
const File& file,
|
||||
const String& filename,
|
||||
Image* optionalIcon,
|
||||
const String& fileSizeDescription,
|
||||
const String& fileTimeDescription,
|
||||
bool isDirectory,
|
||||
bool isItemSelected,
|
||||
int itemIndex,
|
||||
DirectoryContentsDisplayComponent&) = 0;
|
||||
|
||||
virtual Button* createFileBrowserGoUpButton() = 0;
|
||||
|
||||
virtual void layoutFileBrowserComponent (FileBrowserComponent& browserComp,
|
||||
DirectoryContentsDisplayComponent* fileListComponent,
|
||||
FilePreviewComponent* previewComp,
|
||||
ComboBox* currentPathBox,
|
||||
TextEditor* filenameBox,
|
||||
Button* goUpButton) = 0;
|
||||
};
|
||||
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the FileBrowserComponent.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
currentPathBoxBackgroundColourId = 0x1000640, /**< The colour to use to fill the background of the current path ComboBox. */
|
||||
currentPathBoxTextColourId = 0x1000641, /**< The colour to use for the text of the current path ComboBox. */
|
||||
currentPathBoxArrowColourId = 0x1000642, /**< The colour to use to draw the arrow of the current path ComboBox. */
|
||||
filenameBoxBackgroundColourId = 0x1000643, /**< The colour to use to fill the background of the filename TextEditor. */
|
||||
filenameBoxTextColourId = 0x1000644 /**< The colour to use for the text of the filename TextEditor. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&) override;
|
||||
/** @internal */
|
||||
void selectionChanged() override;
|
||||
/** @internal */
|
||||
void fileClicked (const File&, const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void fileDoubleClicked (const File&) override;
|
||||
/** @internal */
|
||||
void browserRootChanged (const File&) override;
|
||||
/** @internal */
|
||||
bool isFileSuitable (const File&) const override;
|
||||
/** @internal */
|
||||
bool isDirectorySuitable (const File&) const override;
|
||||
/** @internal */
|
||||
FilePreviewComponent* getPreviewComponent() const noexcept;
|
||||
/** @internal */
|
||||
DirectoryContentsDisplayComponent* getDisplayComponent() const noexcept;
|
||||
|
||||
protected:
|
||||
/** Returns a list of names and paths for the default places the user might want to look.
|
||||
|
||||
By default this just calls getDefaultRoots(), but you may want to override it to
|
||||
return a custom list.
|
||||
*/
|
||||
virtual void getRoots (StringArray& rootNames, StringArray& rootPaths);
|
||||
|
||||
/** Updates the items in the dropdown list of recent paths with the values from getRoots(). */
|
||||
void resetRecentPaths();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
std::unique_ptr<DirectoryContentsList> fileList;
|
||||
const FileFilter* fileFilter;
|
||||
|
||||
int flags;
|
||||
File currentRoot;
|
||||
Array<File> chosenFiles;
|
||||
ListenerList<FileBrowserListener> listeners;
|
||||
|
||||
std::unique_ptr<DirectoryContentsDisplayComponent> fileListComponent;
|
||||
FilePreviewComponent* previewComp;
|
||||
ComboBox currentPathBox;
|
||||
TextEditor filenameBox;
|
||||
Label fileLabel;
|
||||
std::unique_ptr<Button> goUpButton;
|
||||
TimeSliceThread thread;
|
||||
bool wasProcessActive;
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void timerCallback() override;
|
||||
void sendListenerChangeMessage();
|
||||
bool isFileOrDirSuitable (const File&) const;
|
||||
void updateSelectedPath();
|
||||
void changeFilename();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileBrowserComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
58
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileBrowserListener.h
vendored
Normal file
58
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileBrowserListener.h
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A listener for user selection events in a file browser.
|
||||
|
||||
This is used by a FileBrowserComponent or FileListComponent.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FileBrowserListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
virtual ~FileBrowserListener();
|
||||
|
||||
//==============================================================================
|
||||
/** Callback when the user selects a different file in the browser. */
|
||||
virtual void selectionChanged() = 0;
|
||||
|
||||
/** Callback when the user clicks on a file in the browser. */
|
||||
virtual void fileClicked (const File& file, const MouseEvent& e) = 0;
|
||||
|
||||
/** Callback when the user double-clicks on a file in the browser. */
|
||||
virtual void fileDoubleClicked (const File& file) = 0;
|
||||
|
||||
/** Callback when the browser's root folder changes. */
|
||||
virtual void browserRootChanged (const File& newRoot) = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
268
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp
vendored
Normal file
268
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp
vendored
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class FileChooser::NonNative : public FileChooser::Pimpl
|
||||
{
|
||||
public:
|
||||
NonNative (FileChooser& fileChooser, int flags, FilePreviewComponent* preview)
|
||||
: owner (fileChooser),
|
||||
selectsDirectories ((flags & FileBrowserComponent::canSelectDirectories) != 0),
|
||||
selectsFiles ((flags & FileBrowserComponent::canSelectFiles) != 0),
|
||||
warnAboutOverwrite ((flags & FileBrowserComponent::warnAboutOverwriting) != 0),
|
||||
|
||||
filter (selectsFiles ? owner.filters : String(), selectsDirectories ? "*" : String(), {}),
|
||||
browserComponent (flags, owner.startingFile, &filter, preview),
|
||||
dialogBox (owner.title, {}, browserComponent, warnAboutOverwrite,
|
||||
browserComponent.findColour (AlertWindow::backgroundColourId), owner.parent)
|
||||
{}
|
||||
|
||||
~NonNative() override
|
||||
{
|
||||
dialogBox.exitModalState (0);
|
||||
}
|
||||
|
||||
void launch() override
|
||||
{
|
||||
dialogBox.centreWithDefaultSize (nullptr);
|
||||
dialogBox.enterModalState (true, ModalCallbackFunction::create ([this] (int r) { modalStateFinished (r); }), true);
|
||||
}
|
||||
|
||||
void runModally() override
|
||||
{
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
modalStateFinished (dialogBox.show() ? 1 : 0);
|
||||
#else
|
||||
jassertfalse;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
void modalStateFinished (int returnValue)
|
||||
{
|
||||
Array<URL> result;
|
||||
|
||||
if (returnValue != 0)
|
||||
{
|
||||
for (int i = 0; i < browserComponent.getNumSelectedFiles(); ++i)
|
||||
result.add (URL (browserComponent.getSelectedFile (i)));
|
||||
}
|
||||
|
||||
owner.finished (result);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
FileChooser& owner;
|
||||
bool selectsDirectories, selectsFiles, warnAboutOverwrite;
|
||||
|
||||
WildcardFileFilter filter;
|
||||
FileBrowserComponent browserComponent;
|
||||
FileChooserDialogBox dialogBox;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NonNative)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
FileChooser::FileChooser (const String& chooserBoxTitle,
|
||||
const File& currentFileOrDirectory,
|
||||
const String& fileFilters,
|
||||
const bool useNativeBox,
|
||||
const bool treatFilePackagesAsDirectories,
|
||||
Component* parentComponentToUse)
|
||||
: title (chooserBoxTitle),
|
||||
filters (fileFilters),
|
||||
startingFile (currentFileOrDirectory),
|
||||
parent (parentComponentToUse),
|
||||
useNativeDialogBox (useNativeBox && isPlatformDialogAvailable()),
|
||||
treatFilePackagesAsDirs (treatFilePackagesAsDirectories)
|
||||
{
|
||||
#ifndef JUCE_MAC
|
||||
ignoreUnused (treatFilePackagesAsDirs);
|
||||
#endif
|
||||
|
||||
if (! fileFilters.containsNonWhitespaceChars())
|
||||
filters = "*";
|
||||
}
|
||||
|
||||
FileChooser::~FileChooser()
|
||||
{
|
||||
asyncCallback = nullptr;
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool FileChooser::browseForFileToOpen (FilePreviewComponent* previewComp)
|
||||
{
|
||||
return showDialog (FileBrowserComponent::openMode
|
||||
| FileBrowserComponent::canSelectFiles,
|
||||
previewComp);
|
||||
}
|
||||
|
||||
bool FileChooser::browseForMultipleFilesToOpen (FilePreviewComponent* previewComp)
|
||||
{
|
||||
return showDialog (FileBrowserComponent::openMode
|
||||
| FileBrowserComponent::canSelectFiles
|
||||
| FileBrowserComponent::canSelectMultipleItems,
|
||||
previewComp);
|
||||
}
|
||||
|
||||
bool FileChooser::browseForMultipleFilesOrDirectories (FilePreviewComponent* previewComp)
|
||||
{
|
||||
return showDialog (FileBrowserComponent::openMode
|
||||
| FileBrowserComponent::canSelectFiles
|
||||
| FileBrowserComponent::canSelectDirectories
|
||||
| FileBrowserComponent::canSelectMultipleItems,
|
||||
previewComp);
|
||||
}
|
||||
|
||||
bool FileChooser::browseForFileToSave (const bool warnAboutOverwrite)
|
||||
{
|
||||
return showDialog (FileBrowserComponent::saveMode
|
||||
| FileBrowserComponent::canSelectFiles
|
||||
| (warnAboutOverwrite ? FileBrowserComponent::warnAboutOverwriting : 0),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
bool FileChooser::browseForDirectory()
|
||||
{
|
||||
return showDialog (FileBrowserComponent::openMode
|
||||
| FileBrowserComponent::canSelectDirectories,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
bool FileChooser::showDialog (const int flags, FilePreviewComponent* const previewComp)
|
||||
{
|
||||
FocusRestorer focusRestorer;
|
||||
|
||||
pimpl = createPimpl (flags, previewComp);
|
||||
pimpl->runModally();
|
||||
|
||||
// ensure that the finished function was invoked
|
||||
jassert (pimpl == nullptr);
|
||||
|
||||
return (results.size() > 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void FileChooser::launchAsync (int flags, std::function<void (const FileChooser&)> callback,
|
||||
FilePreviewComponent* previewComp)
|
||||
{
|
||||
// You must specify a callback when using launchAsync
|
||||
jassert (callback);
|
||||
|
||||
// you cannot run two file chooser dialog boxes at the same time
|
||||
jassert (asyncCallback == nullptr);
|
||||
|
||||
asyncCallback = std::move (callback);
|
||||
|
||||
pimpl = createPimpl (flags, previewComp);
|
||||
pimpl->launch();
|
||||
}
|
||||
|
||||
std::shared_ptr<FileChooser::Pimpl> FileChooser::createPimpl (int flags, FilePreviewComponent* previewComp)
|
||||
{
|
||||
results.clear();
|
||||
|
||||
// the preview component needs to be the right size before you pass it in here..
|
||||
jassert (previewComp == nullptr || (previewComp->getWidth() > 10
|
||||
&& previewComp->getHeight() > 10));
|
||||
|
||||
if (pimpl != nullptr)
|
||||
{
|
||||
// you cannot run two file chooser dialog boxes at the same time
|
||||
jassertfalse;
|
||||
pimpl.reset();
|
||||
}
|
||||
|
||||
// You've set the flags for both saveMode and openMode!
|
||||
jassert (! (((flags & FileBrowserComponent::saveMode) != 0)
|
||||
&& ((flags & FileBrowserComponent::openMode) != 0)));
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
const bool selectsFiles = (flags & FileBrowserComponent::canSelectFiles) != 0;
|
||||
const bool selectsDirectories = (flags & FileBrowserComponent::canSelectDirectories) != 0;
|
||||
|
||||
if (useNativeDialogBox && ! (selectsFiles && selectsDirectories))
|
||||
#else
|
||||
if (useNativeDialogBox)
|
||||
#endif
|
||||
{
|
||||
return showPlatformDialog (*this, flags, previewComp);
|
||||
}
|
||||
|
||||
return std::make_unique<NonNative> (*this, flags, previewComp);
|
||||
}
|
||||
|
||||
Array<File> FileChooser::getResults() const noexcept
|
||||
{
|
||||
Array<File> files;
|
||||
|
||||
for (auto url : getURLResults())
|
||||
if (url.isLocalFile())
|
||||
files.add (url.getLocalFile());
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
File FileChooser::getResult() const
|
||||
{
|
||||
auto fileResults = getResults();
|
||||
|
||||
// if you've used a multiple-file select, you should use the getResults() method
|
||||
// to retrieve all the files that were chosen.
|
||||
jassert (fileResults.size() <= 1);
|
||||
|
||||
return fileResults.getFirst();
|
||||
}
|
||||
|
||||
URL FileChooser::getURLResult() const
|
||||
{
|
||||
// if you've used a multiple-file select, you should use the getResults() method
|
||||
// to retrieve all the files that were chosen.
|
||||
jassert (results.size() <= 1);
|
||||
|
||||
return results.getFirst();
|
||||
}
|
||||
|
||||
void FileChooser::finished (const Array<URL>& asyncResults)
|
||||
{
|
||||
std::function<void (const FileChooser&)> callback;
|
||||
std::swap (callback, asyncCallback);
|
||||
|
||||
results = asyncResults;
|
||||
|
||||
pimpl.reset();
|
||||
|
||||
if (callback)
|
||||
callback (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
FilePreviewComponent::FilePreviewComponent() {}
|
||||
FilePreviewComponent::~FilePreviewComponent() {}
|
||||
|
||||
} // namespace juce
|
336
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileChooser.h
vendored
Normal file
336
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileChooser.h
vendored
Normal file
@ -0,0 +1,336 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates a dialog box to choose a file or directory to load or save.
|
||||
|
||||
@code
|
||||
std::unique_ptr<FileChooser> myChooser;
|
||||
|
||||
void loadMooseFile()
|
||||
{
|
||||
myChooser = std::make_unique<FileChooser> ("Please select the moose you want to load...",
|
||||
File::getSpecialLocation (File::userHomeDirectory),
|
||||
"*.moose");
|
||||
|
||||
auto folderChooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories;
|
||||
|
||||
myChooser->launchAsync (folderChooserFlags, [this] (const FileChooser& chooser)
|
||||
{
|
||||
File mooseFile (chooser.getResult());
|
||||
|
||||
loadMoose (mooseFile);
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FileChooser
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a FileChooser.
|
||||
|
||||
After creating one of these, use one of the browseFor... methods to display it.
|
||||
|
||||
@param dialogBoxTitle a text string to display in the dialog box to
|
||||
tell the user what's going on
|
||||
@param initialFileOrDirectory the file or directory that should be selected
|
||||
when the dialog box opens. If this parameter is
|
||||
set to File(), a sensible default directory will
|
||||
be used instead. When using native dialogs, not
|
||||
all platforms will actually select the file. For
|
||||
example, on macOS, when initialFileOrDirectory is
|
||||
a file, only the parent directory of
|
||||
initialFileOrDirectory will be used as the initial
|
||||
directory of the native file chooser.
|
||||
|
||||
Note: On iOS when saving a file, a user will not
|
||||
be able to change a file name, so it may be a good
|
||||
idea to include at least a valid file name in
|
||||
initialFileOrDirectory. When no filename is found,
|
||||
"Untitled" will be used.
|
||||
|
||||
Also, if you pass an already existing file on iOS,
|
||||
that file will be automatically copied to the
|
||||
destination chosen by user and if it can be previewed,
|
||||
its preview will be presented in the dialog too. You
|
||||
will still be able to write into this file copy, since
|
||||
its URL will be returned by getURLResult(). This can be
|
||||
useful when you want to save e.g. an image, so that
|
||||
you can pass a (temporary) file with low quality
|
||||
preview and after the user picks the destination,
|
||||
you can write a high quality image into the copied
|
||||
file. If you create such a temporary file, you need
|
||||
to delete it yourself, once it is not needed anymore.
|
||||
|
||||
@param filePatternsAllowed a set of file patterns to specify which files can be
|
||||
selected - each pattern should be separated by a comma or
|
||||
semi-colon, e.g. "*" or "*.jpg;*.gif". The native MacOS
|
||||
file browser only supports wildcard that specify
|
||||
extensions, so "*.jpg" is OK but "myfilename*" will not
|
||||
work. An empty string means that all files are allowed
|
||||
@param useOSNativeDialogBox if true, then a native dialog box will be used
|
||||
if possible; if false, then a Juce-based
|
||||
browser dialog box will always be used
|
||||
@param treatFilePackagesAsDirectories if true, then the file chooser will allow the
|
||||
selection of files inside packages when
|
||||
invoked on OS X and when using native dialog
|
||||
boxes.
|
||||
@param parentComponent An optional component which should be the parent
|
||||
for the file chooser. If this is a nullptr then the
|
||||
FileChooser will be a top-level window. AUv3s on iOS
|
||||
must specify this parameter as opening a top-level window
|
||||
in an AUv3 is forbidden due to sandbox restrictions.
|
||||
|
||||
@see browseForFileToOpen, browseForFileToSave, browseForDirectory
|
||||
*/
|
||||
FileChooser (const String& dialogBoxTitle,
|
||||
const File& initialFileOrDirectory = File(),
|
||||
const String& filePatternsAllowed = String(),
|
||||
bool useOSNativeDialogBox = true,
|
||||
bool treatFilePackagesAsDirectories = false,
|
||||
Component* parentComponent = nullptr);
|
||||
|
||||
/** Destructor. */
|
||||
~FileChooser();
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Shows a dialog box to choose a file to open.
|
||||
|
||||
This will display the dialog box modally, using an "open file" mode, so that
|
||||
it won't allow non-existent files or directories to be chosen.
|
||||
|
||||
@param previewComponent an optional component to display inside the dialog
|
||||
box to show special info about the files that the user
|
||||
is browsing. The component will not be deleted by this
|
||||
object, so the caller must take care of it.
|
||||
@returns true if the user selected a file, in which case, use the getResult()
|
||||
method to find out what it was. Returns false if they cancelled instead.
|
||||
@see browseForFileToSave, browseForDirectory
|
||||
*/
|
||||
bool browseForFileToOpen (FilePreviewComponent* previewComponent = nullptr);
|
||||
|
||||
/** Same as browseForFileToOpen, but allows the user to select multiple files.
|
||||
|
||||
The files that are returned can be obtained by calling getResults(). See
|
||||
browseForFileToOpen() for more info about the behaviour of this method.
|
||||
*/
|
||||
bool browseForMultipleFilesToOpen (FilePreviewComponent* previewComponent = nullptr);
|
||||
|
||||
/** Shows a dialog box to choose a file to save.
|
||||
|
||||
This will display the dialog box modally, using an "save file" mode, so it
|
||||
will allow non-existent files to be chosen, but not directories.
|
||||
|
||||
@param warnAboutOverwritingExistingFiles if true, the dialog box will ask
|
||||
the user if they're sure they want to overwrite a file that already
|
||||
exists
|
||||
@returns true if the user chose a file and pressed 'ok', in which case, use
|
||||
the getResult() method to find out what the file was. Returns false
|
||||
if they cancelled instead.
|
||||
@see browseForFileToOpen, browseForDirectory
|
||||
*/
|
||||
bool browseForFileToSave (bool warnAboutOverwritingExistingFiles);
|
||||
|
||||
/** Shows a dialog box to choose a directory.
|
||||
|
||||
This will display the dialog box modally, using an "open directory" mode, so it
|
||||
will only allow directories to be returned, not files.
|
||||
|
||||
@returns true if the user chose a directory and pressed 'ok', in which case, use
|
||||
the getResult() method to find out what they chose. Returns false
|
||||
if they cancelled instead.
|
||||
@see browseForFileToOpen, browseForFileToSave
|
||||
*/
|
||||
bool browseForDirectory();
|
||||
|
||||
/** Same as browseForFileToOpen, but allows the user to select multiple files and directories.
|
||||
|
||||
The files that are returned can be obtained by calling getResults(). See
|
||||
browseForFileToOpen() for more info about the behaviour of this method.
|
||||
*/
|
||||
bool browseForMultipleFilesOrDirectories (FilePreviewComponent* previewComponent = nullptr);
|
||||
|
||||
//==============================================================================
|
||||
/** Runs a dialog box for the given set of option flags.
|
||||
The flag values used are those in FileBrowserComponent::FileChooserFlags.
|
||||
|
||||
@returns true if the user chose a directory and pressed 'ok', in which case, use
|
||||
the getResult() method to find out what they chose. Returns false
|
||||
if they cancelled instead.
|
||||
@see FileBrowserComponent::FileChooserFlags
|
||||
*/
|
||||
bool showDialog (int flags, FilePreviewComponent* previewComponent);
|
||||
#endif
|
||||
|
||||
/** Use this method to launch the file browser window asynchronously.
|
||||
|
||||
This will create a file browser dialog based on the settings in this
|
||||
structure and will launch it modally, returning immediately.
|
||||
|
||||
You must specify a callback which is called when the file browser is
|
||||
cancelled or a file is selected. To abort the file selection, simply
|
||||
delete the FileChooser object.
|
||||
|
||||
You must ensure that the lifetime of the callback object is longer than
|
||||
the lifetime of the file-chooser.
|
||||
*/
|
||||
void launchAsync (int flags,
|
||||
std::function<void (const FileChooser&)>,
|
||||
FilePreviewComponent* previewComponent = nullptr);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the last file that was chosen by one of the browseFor methods.
|
||||
|
||||
After calling the appropriate browseFor... method, this method lets you
|
||||
find out what file or directory they chose.
|
||||
|
||||
Note that the file returned is only valid if the browse method returned true (i.e.
|
||||
if the user pressed 'ok' rather than cancelling).
|
||||
|
||||
On mobile platforms, the file browser may return a URL instead of a local file.
|
||||
Therefore, on mobile platforms, you should call getURLResult() instead.
|
||||
|
||||
If you're using a multiple-file select, then use the getResults() method instead,
|
||||
to obtain the list of all files chosen.
|
||||
|
||||
@see getURLResult, getResults
|
||||
*/
|
||||
File getResult() const;
|
||||
|
||||
/** Returns a list of all the files that were chosen during the last call to a
|
||||
browse method.
|
||||
|
||||
On mobile platforms, the file browser may return a URL instead of a local file.
|
||||
Therefore, on mobile platforms, you should call getURLResults() instead.
|
||||
|
||||
This array may be empty if no files were chosen, or can contain multiple entries
|
||||
if multiple files were chosen.
|
||||
|
||||
@see getURLResults, getResult
|
||||
*/
|
||||
Array<File> getResults() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the last document that was chosen by one of the browseFor methods.
|
||||
|
||||
Use this method if you are using the FileChooser on a mobile platform which
|
||||
may return a URL to a remote document. If a local file is chosen then you can
|
||||
convert this file to a JUCE File class via the URL::getLocalFile method.
|
||||
|
||||
Note: On iOS you must use the returned URL object directly (you are also
|
||||
allowed to copy- or move-construct another URL from the returned URL), rather
|
||||
than just storing the path as a String and then creating a new URL from that
|
||||
String. This is because the returned URL contains internally a security
|
||||
bookmark that is required to access the files pointed by it. Then, once you stop
|
||||
dealing with the file pointed by the URL, you should dispose that URL object,
|
||||
so that the security bookmark can be released by the system (only a limited
|
||||
number of such URLs is allowed).
|
||||
|
||||
@see getResult, URL::getLocalFile
|
||||
*/
|
||||
URL getURLResult() const;
|
||||
|
||||
/** Returns a list of all the files that were chosen during the last call to a
|
||||
browse method.
|
||||
|
||||
Use this method if you are using the FileChooser on a mobile platform which
|
||||
may return a URL to a remote document. If a local file is chosen then you can
|
||||
convert this file to a JUCE File class via the URL::getLocalFile method.
|
||||
|
||||
This array may be empty if no files were chosen, or can contain multiple entries
|
||||
if multiple files were chosen.
|
||||
|
||||
Note: On iOS you must use the returned URL object directly (you are also
|
||||
allowed to copy- or move-construct another URL from the returned URL), rather
|
||||
than just storing the path as a String and then creating a new URL from that
|
||||
String. This is because the returned URL contains internally a security
|
||||
bookmark that is required to access the files pointed by it. Then, once you stop
|
||||
dealing with the file pointed by the URL, you should dispose that URL object,
|
||||
so that the security bookmark can be released by the system (only a limited
|
||||
number of such URLs is allowed).
|
||||
|
||||
@see getResults, URL::getLocalFile
|
||||
*/
|
||||
const Array<URL>& getURLResults() const noexcept { return results; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns if a native filechooser is currently available on this platform.
|
||||
|
||||
Note: On iOS this will only return true if you have iCloud permissions
|
||||
and code-signing enabled in the Projucer and have added iCloud containers
|
||||
to your app in Apple's online developer portal. Additionally, the user must
|
||||
have installed the iCloud app on their device and used the app at least once.
|
||||
*/
|
||||
static bool isPlatformDialogAvailable();
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
class Native;
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String title, filters;
|
||||
File startingFile;
|
||||
Component* parent;
|
||||
Array<URL> results;
|
||||
const bool useNativeDialogBox;
|
||||
const bool treatFilePackagesAsDirs;
|
||||
std::function<void (const FileChooser&)> asyncCallback;
|
||||
|
||||
//==============================================================================
|
||||
void finished (const Array<URL>&);
|
||||
|
||||
//==============================================================================
|
||||
struct Pimpl
|
||||
{
|
||||
virtual ~Pimpl() = default;
|
||||
|
||||
virtual void launch() = 0;
|
||||
virtual void runModally() = 0;
|
||||
};
|
||||
|
||||
std::shared_ptr<Pimpl> pimpl;
|
||||
|
||||
//==============================================================================
|
||||
std::shared_ptr<Pimpl> createPimpl (int, FilePreviewComponent*);
|
||||
static std::shared_ptr<Pimpl> showPlatformDialog (FileChooser&, int, FilePreviewComponent*);
|
||||
|
||||
class NonNative;
|
||||
friend class NonNative;
|
||||
friend class Native;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooser)
|
||||
};
|
||||
|
||||
} // namespace juce
|
262
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp
vendored
Normal file
262
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp
vendored
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
class FileChooserDialogBox::ContentComponent : public Component
|
||||
{
|
||||
public:
|
||||
ContentComponent (const String& name, const String& desc, FileBrowserComponent& chooser)
|
||||
: Component (name),
|
||||
chooserComponent (chooser),
|
||||
okButton (chooser.getActionVerb()),
|
||||
cancelButton (TRANS ("Cancel")),
|
||||
newFolderButton (TRANS ("New Folder")),
|
||||
instructions (desc)
|
||||
{
|
||||
addAndMakeVisible (chooserComponent);
|
||||
|
||||
addAndMakeVisible (okButton);
|
||||
okButton.addShortcut (KeyPress (KeyPress::returnKey));
|
||||
|
||||
addAndMakeVisible (cancelButton);
|
||||
cancelButton.addShortcut (KeyPress (KeyPress::escapeKey));
|
||||
|
||||
addChildComponent (newFolderButton);
|
||||
|
||||
setInterceptsMouseClicks (false, true);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
text.draw (g, getLocalBounds().reduced (6)
|
||||
.removeFromTop ((int) text.getHeight()).toFloat());
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
const int buttonHeight = 26;
|
||||
|
||||
auto area = getLocalBounds();
|
||||
|
||||
text.createLayout (getLookAndFeel().createFileChooserHeaderText (getName(), instructions),
|
||||
(float) getWidth() - 12.0f);
|
||||
|
||||
area.removeFromTop (roundToInt (text.getHeight()) + 10);
|
||||
|
||||
chooserComponent.setBounds (area.removeFromTop (area.getHeight() - buttonHeight - 20));
|
||||
auto buttonArea = area.reduced (16, 10);
|
||||
|
||||
okButton.changeWidthToFitText (buttonHeight);
|
||||
okButton.setBounds (buttonArea.removeFromRight (okButton.getWidth() + 16));
|
||||
|
||||
buttonArea.removeFromRight (16);
|
||||
|
||||
cancelButton.changeWidthToFitText (buttonHeight);
|
||||
cancelButton.setBounds (buttonArea.removeFromRight (cancelButton.getWidth()));
|
||||
|
||||
newFolderButton.changeWidthToFitText (buttonHeight);
|
||||
newFolderButton.setBounds (buttonArea.removeFromLeft (newFolderButton.getWidth()));
|
||||
}
|
||||
|
||||
FileBrowserComponent& chooserComponent;
|
||||
TextButton okButton, cancelButton, newFolderButton;
|
||||
String instructions;
|
||||
TextLayout text;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
FileChooserDialogBox::FileChooserDialogBox (const String& name,
|
||||
const String& instructions,
|
||||
FileBrowserComponent& chooserComponent,
|
||||
bool shouldWarn,
|
||||
Colour backgroundColour,
|
||||
Component* parentComp)
|
||||
: ResizableWindow (name, backgroundColour, parentComp == nullptr),
|
||||
warnAboutOverwritingExistingFiles (shouldWarn)
|
||||
{
|
||||
content = new ContentComponent (name, instructions, chooserComponent);
|
||||
setContentOwned (content, false);
|
||||
|
||||
setResizable (true, true);
|
||||
setResizeLimits (300, 300, 1200, 1000);
|
||||
|
||||
content->okButton.onClick = [this] { okButtonPressed(); };
|
||||
content->cancelButton.onClick = [this] { closeButtonPressed(); };
|
||||
content->newFolderButton.onClick = [this] { createNewFolder(); };
|
||||
|
||||
content->chooserComponent.addListener (this);
|
||||
|
||||
FileChooserDialogBox::selectionChanged();
|
||||
|
||||
if (parentComp != nullptr)
|
||||
parentComp->addAndMakeVisible (this);
|
||||
else
|
||||
setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
|
||||
}
|
||||
|
||||
FileChooserDialogBox::~FileChooserDialogBox()
|
||||
{
|
||||
content->chooserComponent.removeListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool FileChooserDialogBox::show (int w, int h)
|
||||
{
|
||||
return showAt (-1, -1, w, h);
|
||||
}
|
||||
|
||||
bool FileChooserDialogBox::showAt (int x, int y, int w, int h)
|
||||
{
|
||||
if (w <= 0) w = getDefaultWidth();
|
||||
if (h <= 0) h = 500;
|
||||
|
||||
if (x < 0 || y < 0)
|
||||
centreWithSize (w, h);
|
||||
else
|
||||
setBounds (x, y, w, h);
|
||||
|
||||
const bool ok = (runModalLoop() != 0);
|
||||
setVisible (false);
|
||||
return ok;
|
||||
}
|
||||
#endif
|
||||
|
||||
void FileChooserDialogBox::centreWithDefaultSize (Component* componentToCentreAround)
|
||||
{
|
||||
centreAroundComponent (componentToCentreAround, getDefaultWidth(), 500);
|
||||
}
|
||||
|
||||
int FileChooserDialogBox::getDefaultWidth() const
|
||||
{
|
||||
if (auto* previewComp = content->chooserComponent.getPreviewComponent())
|
||||
return 400 + previewComp->getWidth();
|
||||
|
||||
return 600;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileChooserDialogBox::closeButtonPressed()
|
||||
{
|
||||
setVisible (false);
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::selectionChanged()
|
||||
{
|
||||
content->okButton.setEnabled (content->chooserComponent.currentFileIsValid());
|
||||
|
||||
content->newFolderButton.setVisible (content->chooserComponent.isSaveMode()
|
||||
&& content->chooserComponent.getRoot().isDirectory());
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::fileDoubleClicked (const File&)
|
||||
{
|
||||
selectionChanged();
|
||||
content->okButton.triggerClick();
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::fileClicked (const File&, const MouseEvent&) {}
|
||||
void FileChooserDialogBox::browserRootChanged (const File&) {}
|
||||
|
||||
void FileChooserDialogBox::okToOverwriteFileCallback (int result, FileChooserDialogBox* box)
|
||||
{
|
||||
if (result != 0 && box != nullptr)
|
||||
box->exitModalState (1);
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::okButtonPressed()
|
||||
{
|
||||
if (warnAboutOverwritingExistingFiles
|
||||
&& content->chooserComponent.isSaveMode()
|
||||
&& content->chooserComponent.getSelectedFile(0).exists())
|
||||
{
|
||||
AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
|
||||
TRANS("File already exists"),
|
||||
TRANS("There's already a file called: FLNM")
|
||||
.replace ("FLNM", content->chooserComponent.getSelectedFile(0).getFullPathName())
|
||||
+ "\n\n"
|
||||
+ TRANS("Are you sure you want to overwrite it?"),
|
||||
TRANS("Overwrite"),
|
||||
TRANS("Cancel"),
|
||||
this,
|
||||
ModalCallbackFunction::forComponent (okToOverwriteFileCallback, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
exitModalState (1);
|
||||
}
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::createNewFolderCallback (int result, FileChooserDialogBox* box,
|
||||
Component::SafePointer<AlertWindow> alert)
|
||||
{
|
||||
if (result != 0 && alert != nullptr && box != nullptr)
|
||||
{
|
||||
alert->setVisible (false);
|
||||
box->createNewFolderConfirmed (alert->getTextEditorContents ("Folder Name"));
|
||||
}
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::createNewFolder()
|
||||
{
|
||||
auto parent = content->chooserComponent.getRoot();
|
||||
|
||||
if (parent.isDirectory())
|
||||
{
|
||||
auto* aw = new AlertWindow (TRANS("New Folder"),
|
||||
TRANS("Please enter the name for the folder"),
|
||||
MessageBoxIconType::NoIcon, this);
|
||||
|
||||
aw->addTextEditor ("Folder Name", String(), String(), false);
|
||||
aw->addButton (TRANS("Create Folder"), 1, KeyPress (KeyPress::returnKey));
|
||||
aw->addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey));
|
||||
|
||||
aw->enterModalState (true,
|
||||
ModalCallbackFunction::forComponent (createNewFolderCallback, this,
|
||||
Component::SafePointer<AlertWindow> (aw)),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::createNewFolderConfirmed (const String& nameFromDialog)
|
||||
{
|
||||
auto name = File::createLegalFileName (nameFromDialog);
|
||||
|
||||
if (! name.isEmpty())
|
||||
{
|
||||
auto parent = content->chooserComponent.getRoot();
|
||||
|
||||
if (! parent.getChildFile (name).createDirectory())
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
TRANS ("New Folder"),
|
||||
TRANS ("Couldn't create the folder!"));
|
||||
|
||||
content->chooserComponent.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
167
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h
vendored
Normal file
167
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A file open/save dialog box.
|
||||
|
||||
This is a Juce-based file dialog box; to use a native file chooser, see the
|
||||
FileChooser class.
|
||||
|
||||
@code
|
||||
{
|
||||
wildcardFilter = std::make_unique<WildcardFileFilter> ("*.foo", String(), "Foo files");
|
||||
|
||||
browser = std::make_unique<FileBrowserComponent> (FileBrowserComponent::canSelectFiles,
|
||||
File(),
|
||||
wildcardFilter.get(),
|
||||
nullptr);
|
||||
|
||||
dialogBox = std::make_unique<FileChooserDialogBox> ("Open some kind of file",
|
||||
"Please choose some kind of file that you want to open...",
|
||||
*browser,
|
||||
false,
|
||||
Colours::lightgrey);
|
||||
|
||||
auto onFileSelected = [this] (int r)
|
||||
{
|
||||
modalStateFinished (r);
|
||||
|
||||
auto selectedFile = browser->getSelectedFile (0);
|
||||
|
||||
...etc...
|
||||
};
|
||||
|
||||
dialogBox->centreWithDefaultSize (nullptr);
|
||||
dialogBox->enterModalState (true,
|
||||
ModalCallbackFunction::create (onFileSelected),
|
||||
true);
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see FileChooser
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FileChooserDialogBox : public ResizableWindow,
|
||||
private FileBrowserListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a file chooser box.
|
||||
|
||||
@param title the main title to show at the top of the box
|
||||
@param instructions an optional longer piece of text to show below the title in
|
||||
a smaller font, describing in more detail what's required.
|
||||
@param browserComponent a FileBrowserComponent that will be shown inside this dialog
|
||||
box. Make sure you delete this after (but not before!) the
|
||||
dialog box has been deleted.
|
||||
@param warnAboutOverwritingExistingFiles if true, then the user will be asked to confirm
|
||||
if they try to select a file that already exists. (This
|
||||
flag is only used when saving files)
|
||||
@param backgroundColour the background colour for the top level window
|
||||
@param parentComponent an optional component which should be the parent
|
||||
for the file chooser. If this is a nullptr then the
|
||||
dialog box will be a top-level window. AUv3s on iOS
|
||||
must specify this parameter as opening a top-level window
|
||||
in an AUv3 is forbidden due to sandbox restrictions.
|
||||
|
||||
@see FileBrowserComponent, FilePreviewComponent
|
||||
*/
|
||||
FileChooserDialogBox (const String& title,
|
||||
const String& instructions,
|
||||
FileBrowserComponent& browserComponent,
|
||||
bool warnAboutOverwritingExistingFiles,
|
||||
Colour backgroundColour,
|
||||
Component* parentComponent = nullptr);
|
||||
|
||||
/** Destructor. */
|
||||
~FileChooserDialogBox() override;
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Displays and runs the dialog box modally.
|
||||
|
||||
This will show the box with the specified size, returning true if the user
|
||||
pressed 'ok', or false if they cancelled.
|
||||
|
||||
Leave the width or height as 0 to use the default size
|
||||
*/
|
||||
bool show (int width = 0, int height = 0);
|
||||
|
||||
/** Displays and runs the dialog box modally.
|
||||
|
||||
This will show the box with the specified size at the specified location,
|
||||
returning true if the user pressed 'ok', or false if they cancelled.
|
||||
|
||||
Leave the width or height as 0 to use the default size.
|
||||
*/
|
||||
bool showAt (int x, int y, int width, int height);
|
||||
#endif
|
||||
|
||||
/** Sets the size of this dialog box to its default and positions it either in the
|
||||
centre of the screen, or centred around a component that is provided.
|
||||
*/
|
||||
void centreWithDefaultSize (Component* componentToCentreAround = nullptr);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the box.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
titleTextColourId = 0x1000850, /**< The colour to use to draw the box's title. */
|
||||
};
|
||||
|
||||
private:
|
||||
class ContentComponent;
|
||||
ContentComponent* content;
|
||||
const bool warnAboutOverwritingExistingFiles;
|
||||
|
||||
void closeButtonPressed();
|
||||
void selectionChanged() override;
|
||||
void fileClicked (const File&, const MouseEvent&) override;
|
||||
void fileDoubleClicked (const File&) override;
|
||||
void browserRootChanged (const File&) override;
|
||||
int getDefaultWidth() const;
|
||||
|
||||
void okButtonPressed();
|
||||
void createNewFolder();
|
||||
void createNewFolderConfirmed (const String& name);
|
||||
|
||||
static void okToOverwriteFileCallback (int result, FileChooserDialogBox*);
|
||||
static void createNewFolderCallback (int result, FileChooserDialogBox*, Component::SafePointer<AlertWindow>);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooserDialogBox)
|
||||
};
|
||||
|
||||
} // namespace juce
|
320
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileListComponent.cpp
vendored
Normal file
320
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileListComponent.cpp
vendored
Normal file
@ -0,0 +1,320 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
Image juce_createIconForFile (const File& file);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
FileListComponent::FileListComponent (DirectoryContentsList& listToShow)
|
||||
: ListBox ({}, nullptr),
|
||||
DirectoryContentsDisplayComponent (listToShow),
|
||||
lastDirectory (listToShow.getDirectory())
|
||||
{
|
||||
setTitle ("Files");
|
||||
setModel (this);
|
||||
directoryContentsList.addChangeListener (this);
|
||||
}
|
||||
|
||||
FileListComponent::~FileListComponent()
|
||||
{
|
||||
directoryContentsList.removeChangeListener (this);
|
||||
}
|
||||
|
||||
int FileListComponent::getNumSelectedFiles() const
|
||||
{
|
||||
return getNumSelectedRows();
|
||||
}
|
||||
|
||||
File FileListComponent::getSelectedFile (int index) const
|
||||
{
|
||||
return directoryContentsList.getFile (getSelectedRow (index));
|
||||
}
|
||||
|
||||
void FileListComponent::deselectAllFiles()
|
||||
{
|
||||
deselectAllRows();
|
||||
}
|
||||
|
||||
void FileListComponent::scrollToTop()
|
||||
{
|
||||
getVerticalScrollBar().setCurrentRangeStart (0);
|
||||
}
|
||||
|
||||
void FileListComponent::setSelectedFile (const File& f)
|
||||
{
|
||||
for (int i = directoryContentsList.getNumFiles(); --i >= 0;)
|
||||
{
|
||||
if (directoryContentsList.getFile (i) == f)
|
||||
{
|
||||
fileWaitingToBeSelected = File();
|
||||
|
||||
selectRow (i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
deselectAllRows();
|
||||
fileWaitingToBeSelected = f;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileListComponent::changeListenerCallback (ChangeBroadcaster*)
|
||||
{
|
||||
updateContent();
|
||||
|
||||
if (lastDirectory != directoryContentsList.getDirectory())
|
||||
{
|
||||
fileWaitingToBeSelected = File();
|
||||
lastDirectory = directoryContentsList.getDirectory();
|
||||
deselectAllRows();
|
||||
}
|
||||
|
||||
if (fileWaitingToBeSelected != File())
|
||||
setSelectedFile (fileWaitingToBeSelected);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class FileListComponent::ItemComponent : public Component,
|
||||
private TimeSliceClient,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
ItemComponent (FileListComponent& fc, TimeSliceThread& t)
|
||||
: owner (fc), thread (t)
|
||||
{
|
||||
}
|
||||
|
||||
~ItemComponent() override
|
||||
{
|
||||
thread.removeTimeSliceClient (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
getLookAndFeel().drawFileBrowserRow (g, getWidth(), getHeight(),
|
||||
file, file.getFileName(),
|
||||
&icon, fileSize, modTime,
|
||||
isDirectory, highlighted,
|
||||
index, owner);
|
||||
}
|
||||
|
||||
bool isInDragToScrollViewport() const noexcept
|
||||
{
|
||||
if (auto* vp = owner.getViewport())
|
||||
return vp->isScrollOnDragEnabled() && (vp->canScrollVertically() || vp->canScrollHorizontally());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent& e) override
|
||||
{
|
||||
selectRowOnMouseUp = false;
|
||||
isDraggingToScroll = false;
|
||||
|
||||
if (isEnabled())
|
||||
{
|
||||
if (owner.getRowSelectedOnMouseDown() && ! (owner.isRowSelected(index) || isInDragToScrollViewport())) {
|
||||
//performSelection (e, false);
|
||||
owner.selectRowsBasedOnModifierKeys (index, e.mods, true);
|
||||
owner.sendMouseClickMessage (file, e);
|
||||
|
||||
}
|
||||
else
|
||||
selectRowOnMouseUp = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void mouseDrag (const MouseEvent& e) override
|
||||
{
|
||||
if (! isDraggingToScroll)
|
||||
if (auto* vp = owner.getViewport())
|
||||
isDraggingToScroll = vp->isCurrentlyScrollingOnDrag();
|
||||
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent& e) override
|
||||
{
|
||||
if (isEnabled() && selectRowOnMouseUp && ! (/*isDragging || */ isDraggingToScroll)) {
|
||||
owner.selectRowsBasedOnModifierKeys (index, e.mods, true);
|
||||
owner.sendMouseClickMessage (file, e);
|
||||
//performSelection (e, true);
|
||||
}
|
||||
}
|
||||
|
||||
void mouseDoubleClick (const MouseEvent&) override
|
||||
{
|
||||
owner.sendDoubleClickMessage (file);
|
||||
}
|
||||
|
||||
void update (const File& root, const DirectoryContentsList::FileInfo* fileInfo,
|
||||
int newIndex, bool nowHighlighted)
|
||||
{
|
||||
thread.removeTimeSliceClient (this);
|
||||
|
||||
if (nowHighlighted != highlighted || newIndex != index)
|
||||
{
|
||||
index = newIndex;
|
||||
highlighted = nowHighlighted;
|
||||
repaint();
|
||||
}
|
||||
|
||||
File newFile;
|
||||
String newFileSize, newModTime;
|
||||
|
||||
if (fileInfo != nullptr)
|
||||
{
|
||||
newFile = root.getChildFile (fileInfo->filename);
|
||||
newFileSize = File::descriptionOfSizeInBytes (fileInfo->fileSize);
|
||||
newModTime = fileInfo->modificationTime.formatted ("%d %b '%y %H:%M");
|
||||
}
|
||||
|
||||
if (newFile != file
|
||||
|| fileSize != newFileSize
|
||||
|| modTime != newModTime)
|
||||
{
|
||||
file = newFile;
|
||||
fileSize = newFileSize;
|
||||
modTime = newModTime;
|
||||
icon = Image();
|
||||
isDirectory = fileInfo != nullptr && fileInfo->isDirectory;
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
if (file != File() && icon.isNull() && ! isDirectory)
|
||||
{
|
||||
updateIcon (true);
|
||||
|
||||
if (! icon.isValid())
|
||||
thread.addTimeSliceClient (this);
|
||||
}
|
||||
}
|
||||
|
||||
int useTimeSlice() override
|
||||
{
|
||||
updateIcon (false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
FileListComponent& owner;
|
||||
TimeSliceThread& thread;
|
||||
File file;
|
||||
String fileSize, modTime;
|
||||
Image icon;
|
||||
int index = 0;
|
||||
bool highlighted = false, isDirectory = false;
|
||||
bool selectRowOnMouseUp = false;
|
||||
bool isDraggingToScroll = false;
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
|
||||
{
|
||||
return createIgnoredAccessibilityHandler (*this);
|
||||
}
|
||||
|
||||
void updateIcon (const bool onlyUpdateIfCached)
|
||||
{
|
||||
if (icon.isNull())
|
||||
{
|
||||
auto hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
|
||||
auto im = ImageCache::getFromHashCode (hashCode);
|
||||
|
||||
if (im.isNull() && ! onlyUpdateIfCached)
|
||||
{
|
||||
im = juce_createIconForFile (file);
|
||||
|
||||
if (im.isValid())
|
||||
ImageCache::addImageToCache (im, hashCode);
|
||||
}
|
||||
|
||||
if (im.isValid())
|
||||
{
|
||||
icon = im;
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
int FileListComponent::getNumRows()
|
||||
{
|
||||
return directoryContentsList.getNumFiles();
|
||||
}
|
||||
|
||||
String FileListComponent::getNameForRow (int rowNumber)
|
||||
{
|
||||
return directoryContentsList.getFile (rowNumber).getFileName();
|
||||
}
|
||||
|
||||
void FileListComponent::paintListBoxItem (int, Graphics&, int, int, bool)
|
||||
{
|
||||
}
|
||||
|
||||
Component* FileListComponent::refreshComponentForRow (int row, bool isSelected, Component* existingComponentToUpdate)
|
||||
{
|
||||
jassert (existingComponentToUpdate == nullptr || dynamic_cast<ItemComponent*> (existingComponentToUpdate) != nullptr);
|
||||
|
||||
auto comp = static_cast<ItemComponent*> (existingComponentToUpdate);
|
||||
|
||||
if (comp == nullptr)
|
||||
comp = new ItemComponent (*this, directoryContentsList.getTimeSliceThread());
|
||||
|
||||
DirectoryContentsList::FileInfo fileInfo;
|
||||
comp->update (directoryContentsList.getDirectory(),
|
||||
directoryContentsList.getFileInfo (row, fileInfo) ? &fileInfo : nullptr,
|
||||
row, isSelected);
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
||||
void FileListComponent::selectedRowsChanged (int /*lastRowSelected*/)
|
||||
{
|
||||
sendSelectionChangeMessage();
|
||||
}
|
||||
|
||||
void FileListComponent::deleteKeyPressed (int /*currentSelectedRow*/)
|
||||
{
|
||||
}
|
||||
|
||||
void FileListComponent::returnKeyPressed (int currentSelectedRow)
|
||||
{
|
||||
sendDoubleClickMessage (directoryContentsList.getFile (currentSelectedRow));
|
||||
}
|
||||
|
||||
} // namespace juce
|
95
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileListComponent.h
vendored
Normal file
95
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileListComponent.h
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that displays the files in a directory as a listbox.
|
||||
|
||||
This implements the DirectoryContentsDisplayComponent base class so that
|
||||
it can be used in a FileBrowserComponent.
|
||||
|
||||
To attach a listener to it, use its DirectoryContentsDisplayComponent base
|
||||
class and the FileBrowserListener class.
|
||||
|
||||
@see DirectoryContentsList, FileTreeComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FileListComponent : public ListBox,
|
||||
public DirectoryContentsDisplayComponent,
|
||||
private ListBoxModel,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a listbox to show the contents of a specified directory. */
|
||||
FileListComponent (DirectoryContentsList& listToShow);
|
||||
|
||||
/** Destructor. */
|
||||
~FileListComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of files the user has got selected.
|
||||
@see getSelectedFile
|
||||
*/
|
||||
int getNumSelectedFiles() const override;
|
||||
|
||||
/** Returns one of the files that the user has currently selected.
|
||||
The index should be in the range 0 to (getNumSelectedFiles() - 1).
|
||||
@see getNumSelectedFiles
|
||||
*/
|
||||
File getSelectedFile (int index = 0) const override;
|
||||
|
||||
/** Deselects any files that are currently selected. */
|
||||
void deselectAllFiles() override;
|
||||
|
||||
/** Scrolls to the top of the list. */
|
||||
void scrollToTop() override;
|
||||
|
||||
/** If the specified file is in the list, it will become the only selected item
|
||||
(and if the file isn't in the list, all other items will be deselected). */
|
||||
void setSelectedFile (const File&) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
File lastDirectory, fileWaitingToBeSelected;
|
||||
class ItemComponent;
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override;
|
||||
int getNumRows() override;
|
||||
String getNameForRow (int rowNumber) override;
|
||||
void paintListBoxItem (int, Graphics&, int, int, bool) override;
|
||||
Component* refreshComponentForRow (int rowNumber, bool isRowSelected, Component*) override;
|
||||
void selectedRowsChanged (int row) override;
|
||||
void deleteKeyPressed (int currentSelectedRow) override;
|
||||
void returnKeyPressed (int currentSelectedRow) override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
66
deps/juce/modules/juce_gui_basics/filebrowser/juce_FilePreviewComponent.h
vendored
Normal file
66
deps/juce/modules/juce_gui_basics/filebrowser/juce_FilePreviewComponent.h
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 components that live inside a file chooser dialog box and
|
||||
show previews of the files that get selected.
|
||||
|
||||
One of these allows special extra information to be displayed for files
|
||||
in a dialog box as the user selects them. Each time the current file or
|
||||
directory is changed, the selectedFileChanged() method will be called
|
||||
to allow it to update itself appropriately.
|
||||
|
||||
@see FileChooser, ImagePreviewComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FilePreviewComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a FilePreviewComponent. */
|
||||
FilePreviewComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~FilePreviewComponent() override;
|
||||
|
||||
/** Called to indicate that the user's currently selected file has changed.
|
||||
|
||||
@param newSelectedFile the newly selected file or directory, which may be
|
||||
a default File() object if none is selected.
|
||||
*/
|
||||
virtual void selectedFileChanged (const File& newSelectedFile) = 0;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePreviewComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
275
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp
vendored
Normal file
275
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp
vendored
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
FileSearchPathListComponent::FileSearchPathListComponent()
|
||||
: addButton ("+"),
|
||||
removeButton ("-"),
|
||||
changeButton (TRANS ("change...")),
|
||||
upButton ({}, DrawableButton::ImageOnButtonBackground),
|
||||
downButton ({}, DrawableButton::ImageOnButtonBackground)
|
||||
{
|
||||
listBox.setModel (this);
|
||||
addAndMakeVisible (listBox);
|
||||
listBox.setColour (ListBox::backgroundColourId, Colours::black.withAlpha (0.02f));
|
||||
listBox.setColour (ListBox::outlineColourId, Colours::black.withAlpha (0.1f));
|
||||
listBox.setOutlineThickness (1);
|
||||
|
||||
addAndMakeVisible (addButton);
|
||||
addButton.onClick = [this] { addPath(); };
|
||||
addButton.setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnRight | Button::ConnectedOnBottom | Button::ConnectedOnTop);
|
||||
|
||||
addAndMakeVisible (removeButton);
|
||||
removeButton.onClick = [this] { deleteSelected(); };
|
||||
removeButton.setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnRight | Button::ConnectedOnBottom | Button::ConnectedOnTop);
|
||||
|
||||
addAndMakeVisible (changeButton);
|
||||
changeButton.onClick = [this] { editSelected(); };
|
||||
|
||||
addAndMakeVisible (upButton);
|
||||
upButton.onClick = [this] { moveSelection (-1); };
|
||||
|
||||
auto arrowColour = findColour (ListBox::textColourId);
|
||||
|
||||
{
|
||||
Path arrowPath;
|
||||
arrowPath.addArrow ({ 50.0f, 100.0f, 50.0f, 0.0f }, 40.0f, 100.0f, 50.0f);
|
||||
DrawablePath arrowImage;
|
||||
arrowImage.setFill (arrowColour);
|
||||
arrowImage.setPath (arrowPath);
|
||||
|
||||
upButton.setImages (&arrowImage);
|
||||
}
|
||||
|
||||
addAndMakeVisible (downButton);
|
||||
downButton.onClick = [this] { moveSelection (1); };
|
||||
|
||||
{
|
||||
Path arrowPath;
|
||||
arrowPath.addArrow ({ 50.0f, 0.0f, 50.0f, 100.0f }, 40.0f, 100.0f, 50.0f);
|
||||
DrawablePath arrowImage;
|
||||
arrowImage.setFill (arrowColour);
|
||||
arrowImage.setPath (arrowPath);
|
||||
|
||||
downButton.setImages (&arrowImage);
|
||||
}
|
||||
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
FileSearchPathListComponent::~FileSearchPathListComponent()
|
||||
{
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::updateButtons()
|
||||
{
|
||||
const bool anythingSelected = listBox.getNumSelectedRows() > 0;
|
||||
|
||||
removeButton.setEnabled (anythingSelected);
|
||||
changeButton.setEnabled (anythingSelected);
|
||||
upButton.setEnabled (anythingSelected);
|
||||
downButton.setEnabled (anythingSelected);
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::changed()
|
||||
{
|
||||
listBox.updateContent();
|
||||
listBox.repaint();
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileSearchPathListComponent::setPath (const FileSearchPath& newPath)
|
||||
{
|
||||
if (newPath.toString() != path.toString())
|
||||
{
|
||||
path = newPath;
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::setDefaultBrowseTarget (const File& newDefaultDirectory)
|
||||
{
|
||||
defaultBrowseTarget = newDefaultDirectory;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int FileSearchPathListComponent::getNumRows()
|
||||
{
|
||||
return path.getNumPaths();
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected)
|
||||
{
|
||||
if (rowIsSelected)
|
||||
g.fillAll (findColour (TextEditor::highlightColourId));
|
||||
|
||||
g.setColour (findColour (ListBox::textColourId));
|
||||
Font f ((float) height * 0.7f);
|
||||
f.setHorizontalScale (0.9f);
|
||||
g.setFont (f);
|
||||
|
||||
g.drawText (path[rowNumber].getFullPathName(),
|
||||
4, 0, width - 6, height,
|
||||
Justification::centredLeft, true);
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::deleteKeyPressed (int row)
|
||||
{
|
||||
if (isPositiveAndBelow (row, path.getNumPaths()))
|
||||
{
|
||||
path.remove (row);
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::returnKeyPressed (int row)
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> (TRANS("Change folder..."), path[row], "*");
|
||||
auto chooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories;
|
||||
|
||||
chooser->launchAsync (chooserFlags, [this, row] (const FileChooser& fc)
|
||||
{
|
||||
if (fc.getResult() == File{})
|
||||
return;
|
||||
|
||||
path.remove (row);
|
||||
path.add (fc.getResult(), row);
|
||||
changed();
|
||||
});
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::listBoxItemDoubleClicked (int row, const MouseEvent&)
|
||||
{
|
||||
returnKeyPressed (row);
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::selectedRowsChanged (int)
|
||||
{
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::resized()
|
||||
{
|
||||
const int buttonH = 22;
|
||||
const int buttonY = getHeight() - buttonH - 4;
|
||||
listBox.setBounds (2, 2, getWidth() - 4, buttonY - 5);
|
||||
|
||||
addButton.setBounds (2, buttonY, buttonH, buttonH);
|
||||
removeButton.setBounds (addButton.getRight(), buttonY, buttonH, buttonH);
|
||||
|
||||
changeButton.changeWidthToFitText (buttonH);
|
||||
downButton.setSize (buttonH * 2, buttonH);
|
||||
upButton.setSize (buttonH * 2, buttonH);
|
||||
|
||||
downButton.setTopRightPosition (getWidth() - 2, buttonY);
|
||||
upButton.setTopRightPosition (downButton.getX() - 4, buttonY);
|
||||
changeButton.setTopRightPosition (upButton.getX() - 8, buttonY);
|
||||
}
|
||||
|
||||
bool FileSearchPathListComponent::isInterestedInFileDrag (const StringArray&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::filesDropped (const StringArray& filenames, int, int mouseY)
|
||||
{
|
||||
for (int i = filenames.size(); --i >= 0;)
|
||||
{
|
||||
const File f (filenames[i]);
|
||||
|
||||
if (f.isDirectory())
|
||||
{
|
||||
auto row = listBox.getRowContainingPosition (0, mouseY - listBox.getY());
|
||||
path.add (f, row);
|
||||
changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::addPath()
|
||||
{
|
||||
auto start = defaultBrowseTarget;
|
||||
|
||||
if (start == File())
|
||||
start = path[0];
|
||||
|
||||
if (start == File())
|
||||
start = File::getCurrentWorkingDirectory();
|
||||
|
||||
chooser = std::make_unique<FileChooser> (TRANS("Add a folder..."), start, "*");
|
||||
auto chooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories;
|
||||
|
||||
chooser->launchAsync (chooserFlags, [this] (const FileChooser& fc)
|
||||
{
|
||||
if (fc.getResult() == File{})
|
||||
return;
|
||||
|
||||
path.add (fc.getResult(), listBox.getSelectedRow());
|
||||
changed();
|
||||
});
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::deleteSelected()
|
||||
{
|
||||
deleteKeyPressed (listBox.getSelectedRow());
|
||||
changed();
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::editSelected()
|
||||
{
|
||||
returnKeyPressed (listBox.getSelectedRow());
|
||||
changed();
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::moveSelection (int delta)
|
||||
{
|
||||
jassert (delta == -1 || delta == 1);
|
||||
auto currentRow = listBox.getSelectedRow();
|
||||
|
||||
if (isPositiveAndBelow (currentRow, path.getNumPaths()))
|
||||
{
|
||||
auto newRow = jlimit (0, path.getNumPaths() - 1, currentRow + delta);
|
||||
|
||||
if (currentRow != newRow)
|
||||
{
|
||||
auto f = path[currentRow];
|
||||
path.remove (currentRow);
|
||||
path.add (f, newRow);
|
||||
listBox.selectRow (newRow);
|
||||
changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace juce
|
120
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.h
vendored
Normal file
120
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.h
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Shows a set of file paths in a list, allowing them to be added, removed or
|
||||
re-ordered.
|
||||
|
||||
@see FileSearchPath
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FileSearchPathListComponent : public Component,
|
||||
public SettableTooltipClient,
|
||||
public FileDragAndDropTarget,
|
||||
private ListBoxModel
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty FileSearchPathListComponent. */
|
||||
FileSearchPathListComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~FileSearchPathListComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the path as it is currently shown. */
|
||||
const FileSearchPath& getPath() const noexcept { return path; }
|
||||
|
||||
/** Changes the current path. */
|
||||
void setPath (const FileSearchPath& newPath);
|
||||
|
||||
/** Sets a file or directory to be the default starting point for the browser to show.
|
||||
|
||||
This is only used if the current file hasn't been set.
|
||||
*/
|
||||
void setDefaultBrowseTarget (const File& newDefaultDirectory);
|
||||
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the label.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1004100, /**< The background colour to fill the component with.
|
||||
Make this transparent if you don't want the background to be filled. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
int getNumRows() override;
|
||||
/** @internal */
|
||||
void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override;
|
||||
/** @internal */
|
||||
void deleteKeyPressed (int lastRowSelected) override;
|
||||
/** @internal */
|
||||
void returnKeyPressed (int lastRowSelected) override;
|
||||
/** @internal */
|
||||
void listBoxItemDoubleClicked (int row, const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void selectedRowsChanged (int lastRowSelected) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
bool isInterestedInFileDrag (const StringArray&) override;
|
||||
/** @internal */
|
||||
void filesDropped (const StringArray& files, int, int) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
FileSearchPath path;
|
||||
File defaultBrowseTarget;
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
ListBox listBox;
|
||||
TextButton addButton, removeButton, changeButton;
|
||||
DrawableButton upButton, downButton;
|
||||
|
||||
void changed();
|
||||
void updateButtons();
|
||||
|
||||
void addPath();
|
||||
void deleteSelected();
|
||||
void editSelected();
|
||||
void moveSelection (int);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileSearchPathListComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
334
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp
vendored
Normal file
334
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp
vendored
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class FileListTreeItem : public TreeViewItem,
|
||||
private TimeSliceClient,
|
||||
private AsyncUpdater,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
FileListTreeItem (FileTreeComponent& treeComp,
|
||||
DirectoryContentsList* parentContents,
|
||||
int indexInContents,
|
||||
const File& f,
|
||||
TimeSliceThread& t)
|
||||
: file (f),
|
||||
owner (treeComp),
|
||||
parentContentsList (parentContents),
|
||||
indexInContentsList (indexInContents),
|
||||
subContentsList (nullptr, false),
|
||||
thread (t)
|
||||
{
|
||||
DirectoryContentsList::FileInfo fileInfo;
|
||||
|
||||
if (parentContents != nullptr
|
||||
&& parentContents->getFileInfo (indexInContents, fileInfo))
|
||||
{
|
||||
fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize);
|
||||
modTime = fileInfo.modificationTime.formatted ("%d %b '%y %H:%M");
|
||||
isDirectory = fileInfo.isDirectory;
|
||||
}
|
||||
else
|
||||
{
|
||||
isDirectory = true;
|
||||
}
|
||||
}
|
||||
|
||||
~FileListTreeItem() override
|
||||
{
|
||||
thread.removeTimeSliceClient (this);
|
||||
clearSubItems();
|
||||
removeSubContentsList();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool mightContainSubItems() override { return isDirectory; }
|
||||
String getUniqueName() const override { return file.getFullPathName(); }
|
||||
int getItemHeight() const override { return owner.getItemHeight(); }
|
||||
|
||||
var getDragSourceDescription() override { return owner.getDragAndDropDescription(); }
|
||||
|
||||
void itemOpennessChanged (bool isNowOpen) override
|
||||
{
|
||||
if (isNowOpen)
|
||||
{
|
||||
clearSubItems();
|
||||
|
||||
isDirectory = file.isDirectory();
|
||||
|
||||
if (isDirectory)
|
||||
{
|
||||
if (subContentsList == nullptr && parentContentsList != nullptr)
|
||||
{
|
||||
auto l = new DirectoryContentsList (parentContentsList->getFilter(), thread);
|
||||
|
||||
l->setDirectory (file,
|
||||
parentContentsList->isFindingDirectories(),
|
||||
parentContentsList->isFindingFiles());
|
||||
|
||||
setSubContentsList (l, true);
|
||||
}
|
||||
|
||||
changeListenerCallback (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeSubContentsList()
|
||||
{
|
||||
if (subContentsList != nullptr)
|
||||
{
|
||||
subContentsList->removeChangeListener (this);
|
||||
subContentsList.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void setSubContentsList (DirectoryContentsList* newList, const bool canDeleteList)
|
||||
{
|
||||
removeSubContentsList();
|
||||
|
||||
subContentsList = OptionalScopedPointer<DirectoryContentsList> (newList, canDeleteList);
|
||||
newList->addChangeListener (this);
|
||||
}
|
||||
|
||||
bool selectFile (const File& target)
|
||||
{
|
||||
if (file == target)
|
||||
{
|
||||
setSelected (true, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target.isAChildOf (file))
|
||||
{
|
||||
setOpen (true);
|
||||
|
||||
for (int maxRetries = 500; --maxRetries > 0;)
|
||||
{
|
||||
for (int i = 0; i < getNumSubItems(); ++i)
|
||||
if (auto* f = dynamic_cast<FileListTreeItem*> (getSubItem (i)))
|
||||
if (f->selectFile (target))
|
||||
return true;
|
||||
|
||||
// if we've just opened and the contents are still loading, wait for it..
|
||||
if (subContentsList != nullptr && subContentsList->isStillLoading())
|
||||
{
|
||||
Thread::sleep (10);
|
||||
rebuildItemsFromContentList();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
rebuildItemsFromContentList();
|
||||
}
|
||||
|
||||
void rebuildItemsFromContentList()
|
||||
{
|
||||
clearSubItems();
|
||||
|
||||
if (isOpen() && subContentsList != nullptr)
|
||||
{
|
||||
for (int i = 0; i < subContentsList->getNumFiles(); ++i)
|
||||
addSubItem (new FileListTreeItem (owner, subContentsList, i,
|
||||
subContentsList->getFile(i), thread));
|
||||
}
|
||||
}
|
||||
|
||||
void paintItem (Graphics& g, int width, int height) override
|
||||
{
|
||||
ScopedLock lock (iconUpdate);
|
||||
|
||||
if (file != File())
|
||||
{
|
||||
updateIcon (true);
|
||||
|
||||
if (icon.isNull())
|
||||
thread.addTimeSliceClient (this);
|
||||
}
|
||||
|
||||
owner.getLookAndFeel().drawFileBrowserRow (g, width, height,
|
||||
file, file.getFileName(),
|
||||
&icon, fileSize, modTime,
|
||||
isDirectory, isSelected(),
|
||||
indexInContentsList, owner);
|
||||
}
|
||||
|
||||
String getAccessibilityName() override
|
||||
{
|
||||
return file.getFileName();
|
||||
}
|
||||
|
||||
void itemClicked (const MouseEvent& e) override
|
||||
{
|
||||
owner.sendMouseClickMessage (file, e);
|
||||
}
|
||||
|
||||
void itemDoubleClicked (const MouseEvent& e) override
|
||||
{
|
||||
TreeViewItem::itemDoubleClicked (e);
|
||||
|
||||
owner.sendDoubleClickMessage (file);
|
||||
}
|
||||
|
||||
void itemSelectionChanged (bool) override
|
||||
{
|
||||
owner.sendSelectionChangeMessage();
|
||||
}
|
||||
|
||||
int useTimeSlice() override
|
||||
{
|
||||
updateIcon (false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
owner.repaint();
|
||||
}
|
||||
|
||||
const File file;
|
||||
|
||||
private:
|
||||
FileTreeComponent& owner;
|
||||
DirectoryContentsList* parentContentsList;
|
||||
int indexInContentsList;
|
||||
OptionalScopedPointer<DirectoryContentsList> subContentsList;
|
||||
bool isDirectory;
|
||||
TimeSliceThread& thread;
|
||||
CriticalSection iconUpdate;
|
||||
Image icon;
|
||||
String fileSize, modTime;
|
||||
|
||||
void updateIcon (const bool onlyUpdateIfCached)
|
||||
{
|
||||
if (icon.isNull())
|
||||
{
|
||||
auto hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
|
||||
auto im = ImageCache::getFromHashCode (hashCode);
|
||||
|
||||
if (im.isNull() && ! onlyUpdateIfCached)
|
||||
{
|
||||
im = juce_createIconForFile (file);
|
||||
|
||||
if (im.isValid())
|
||||
ImageCache::addImageToCache (im, hashCode);
|
||||
}
|
||||
|
||||
if (im.isValid())
|
||||
{
|
||||
{
|
||||
ScopedLock lock (iconUpdate);
|
||||
icon = im;
|
||||
}
|
||||
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListTreeItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow)
|
||||
: DirectoryContentsDisplayComponent (listToShow),
|
||||
itemHeight (22)
|
||||
{
|
||||
setRootItemVisible (false);
|
||||
refresh();
|
||||
}
|
||||
|
||||
FileTreeComponent::~FileTreeComponent()
|
||||
{
|
||||
deleteRootItem();
|
||||
}
|
||||
|
||||
void FileTreeComponent::refresh()
|
||||
{
|
||||
deleteRootItem();
|
||||
|
||||
auto root = new FileListTreeItem (*this, nullptr, 0, directoryContentsList.getDirectory(),
|
||||
directoryContentsList.getTimeSliceThread());
|
||||
|
||||
root->setSubContentsList (&directoryContentsList, false);
|
||||
setRootItem (root);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
File FileTreeComponent::getSelectedFile (const int index) const
|
||||
{
|
||||
if (auto* item = dynamic_cast<const FileListTreeItem*> (getSelectedItem (index)))
|
||||
return item->file;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void FileTreeComponent::deselectAllFiles()
|
||||
{
|
||||
clearSelectedItems();
|
||||
}
|
||||
|
||||
void FileTreeComponent::scrollToTop()
|
||||
{
|
||||
getViewport()->getVerticalScrollBar().setCurrentRangeStart (0);
|
||||
}
|
||||
|
||||
void FileTreeComponent::setDragAndDropDescription (const String& description)
|
||||
{
|
||||
dragAndDropDescription = description;
|
||||
}
|
||||
|
||||
void FileTreeComponent::setSelectedFile (const File& target)
|
||||
{
|
||||
if (auto* t = dynamic_cast<FileListTreeItem*> (getRootItem()))
|
||||
if (! t->selectFile (target))
|
||||
clearSelectedItems();
|
||||
}
|
||||
|
||||
void FileTreeComponent::setItemHeight (int newHeight)
|
||||
{
|
||||
if (itemHeight != newHeight)
|
||||
{
|
||||
itemHeight = newHeight;
|
||||
|
||||
if (auto* root = getRootItem())
|
||||
root->treeHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
105
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.h
vendored
Normal file
105
deps/juce/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.h
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that displays the files in a directory as a treeview.
|
||||
|
||||
This implements the DirectoryContentsDisplayComponent base class so that
|
||||
it can be used in a FileBrowserComponent.
|
||||
|
||||
To attach a listener to it, use its DirectoryContentsDisplayComponent base
|
||||
class and the FileBrowserListener class.
|
||||
|
||||
@see DirectoryContentsList, FileListComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FileTreeComponent : public TreeView,
|
||||
public DirectoryContentsDisplayComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a listbox to show the contents of a specified directory.
|
||||
*/
|
||||
FileTreeComponent (DirectoryContentsList& listToShow);
|
||||
|
||||
/** Destructor. */
|
||||
~FileTreeComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of files the user has got selected.
|
||||
@see getSelectedFile
|
||||
*/
|
||||
int getNumSelectedFiles() const override { return TreeView::getNumSelectedItems(); }
|
||||
|
||||
/** Returns one of the files that the user has currently selected.
|
||||
The index should be in the range 0 to (getNumSelectedFiles() - 1).
|
||||
@see getNumSelectedFiles
|
||||
*/
|
||||
File getSelectedFile (int index = 0) const override;
|
||||
|
||||
/** Deselects any files that are currently selected. */
|
||||
void deselectAllFiles() override;
|
||||
|
||||
/** Scrolls the list to the top. */
|
||||
void scrollToTop() override;
|
||||
|
||||
/** If the specified file is in the list, it will become the only selected item
|
||||
(and if the file isn't in the list, all other items will be deselected). */
|
||||
void setSelectedFile (const File&) override;
|
||||
|
||||
/** Updates the files in the list. */
|
||||
void refresh();
|
||||
|
||||
/** Setting a name for this allows tree items to be dragged.
|
||||
|
||||
The string that you pass in here will be returned by the getDragSourceDescription()
|
||||
of the items in the tree. For more info, see TreeViewItem::getDragSourceDescription().
|
||||
*/
|
||||
void setDragAndDropDescription (const String& description);
|
||||
|
||||
/** Returns the last value that was set by setDragAndDropDescription().
|
||||
*/
|
||||
const String& getDragAndDropDescription() const noexcept { return dragAndDropDescription; }
|
||||
|
||||
/** Changes the height of the treeview items. */
|
||||
void setItemHeight (int newHeight);
|
||||
|
||||
/** Returns the height of the treeview items. */
|
||||
int getItemHeight() const noexcept { return itemHeight; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String dragAndDropDescription;
|
||||
int itemHeight;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileTreeComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
268
deps/juce/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.cpp
vendored
Normal file
268
deps/juce/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.cpp
vendored
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
FilenameComponent::FilenameComponent (const String& name,
|
||||
const File& currentFile,
|
||||
bool canEditFilename,
|
||||
bool isDirectory,
|
||||
bool isForSaving,
|
||||
const String& fileBrowserWildcard,
|
||||
const String& suffix,
|
||||
const String& textWhenNothingSelected)
|
||||
: Component (name),
|
||||
isDir (isDirectory),
|
||||
isSaving (isForSaving),
|
||||
wildcard (fileBrowserWildcard),
|
||||
enforcedSuffix (suffix)
|
||||
{
|
||||
addAndMakeVisible (filenameBox);
|
||||
filenameBox.setEditableText (canEditFilename);
|
||||
filenameBox.setTextWhenNothingSelected (textWhenNothingSelected);
|
||||
filenameBox.setTextWhenNoChoicesAvailable (TRANS ("(no recently selected files)"));
|
||||
filenameBox.onChange = [this] { setCurrentFile (getCurrentFile(), true); };
|
||||
|
||||
setBrowseButtonText ("...");
|
||||
|
||||
setCurrentFile (currentFile, true, dontSendNotification);
|
||||
}
|
||||
|
||||
FilenameComponent::~FilenameComponent()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FilenameComponent::paintOverChildren (Graphics& g)
|
||||
{
|
||||
if (isFileDragOver)
|
||||
{
|
||||
g.setColour (Colours::red.withAlpha (0.2f));
|
||||
g.drawRect (getLocalBounds(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
void FilenameComponent::resized()
|
||||
{
|
||||
getLookAndFeel().layoutFilenameComponent (*this, &filenameBox, browseButton.get());
|
||||
}
|
||||
|
||||
std::unique_ptr<ComponentTraverser> FilenameComponent::createKeyboardFocusTraverser()
|
||||
{
|
||||
// This prevents the sub-components from grabbing focus if the
|
||||
// FilenameComponent has been set to refuse focus.
|
||||
return getWantsKeyboardFocus() ? Component::createKeyboardFocusTraverser() : nullptr;
|
||||
}
|
||||
|
||||
void FilenameComponent::setBrowseButtonText (const String& newBrowseButtonText)
|
||||
{
|
||||
browseButtonText = newBrowseButtonText;
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
void FilenameComponent::lookAndFeelChanged()
|
||||
{
|
||||
browseButton.reset();
|
||||
browseButton.reset (getLookAndFeel().createFilenameComponentBrowseButton (browseButtonText));
|
||||
addAndMakeVisible (browseButton.get());
|
||||
browseButton->setConnectedEdges (Button::ConnectedOnLeft);
|
||||
browseButton->onClick = [this] { showChooser(); };
|
||||
resized();
|
||||
}
|
||||
|
||||
void FilenameComponent::setTooltip (const String& newTooltip)
|
||||
{
|
||||
SettableTooltipClient::setTooltip (newTooltip);
|
||||
filenameBox.setTooltip (newTooltip);
|
||||
}
|
||||
|
||||
void FilenameComponent::setDefaultBrowseTarget (const File& newDefaultDirectory)
|
||||
{
|
||||
defaultBrowseFile = newDefaultDirectory;
|
||||
}
|
||||
|
||||
File FilenameComponent::getLocationToBrowse()
|
||||
{
|
||||
if (lastFilename.isEmpty() && defaultBrowseFile != File())
|
||||
return defaultBrowseFile;
|
||||
|
||||
return getCurrentFile();
|
||||
}
|
||||
|
||||
void FilenameComponent::showChooser()
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> (isDir ? TRANS ("Choose a new directory")
|
||||
: TRANS ("Choose a new file"),
|
||||
getLocationToBrowse(),
|
||||
wildcard);
|
||||
|
||||
auto chooserFlags = isDir ? FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories
|
||||
: FileBrowserComponent::canSelectFiles | (isSaving ? FileBrowserComponent::saveMode
|
||||
: FileBrowserComponent::openMode);
|
||||
|
||||
chooser->launchAsync (chooserFlags, [this] (const FileChooser&)
|
||||
{
|
||||
if (chooser->getResult() == File{})
|
||||
return;
|
||||
|
||||
setCurrentFile (chooser->getResult(), true);
|
||||
});
|
||||
}
|
||||
|
||||
bool FilenameComponent::isInterestedInFileDrag (const StringArray&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void FilenameComponent::filesDropped (const StringArray& filenames, int, int)
|
||||
{
|
||||
isFileDragOver = false;
|
||||
repaint();
|
||||
|
||||
const File f (filenames[0]);
|
||||
|
||||
if (f.exists() && (f.isDirectory() == isDir))
|
||||
setCurrentFile (f, true);
|
||||
}
|
||||
|
||||
void FilenameComponent::fileDragEnter (const StringArray&, int, int)
|
||||
{
|
||||
isFileDragOver = true;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void FilenameComponent::fileDragExit (const StringArray&)
|
||||
{
|
||||
isFileDragOver = false;
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String FilenameComponent::getCurrentFileText() const
|
||||
{
|
||||
return filenameBox.getText();
|
||||
}
|
||||
|
||||
File FilenameComponent::getCurrentFile() const
|
||||
{
|
||||
auto f = File::getCurrentWorkingDirectory().getChildFile (getCurrentFileText());
|
||||
|
||||
if (enforcedSuffix.isNotEmpty())
|
||||
f = f.withFileExtension (enforcedSuffix);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void FilenameComponent::setCurrentFile (File newFile,
|
||||
const bool addToRecentlyUsedList,
|
||||
NotificationType notification)
|
||||
{
|
||||
if (enforcedSuffix.isNotEmpty())
|
||||
newFile = newFile.withFileExtension (enforcedSuffix);
|
||||
|
||||
if (newFile.getFullPathName() != lastFilename)
|
||||
{
|
||||
lastFilename = newFile.getFullPathName();
|
||||
|
||||
if (addToRecentlyUsedList)
|
||||
addRecentlyUsedFile (newFile);
|
||||
|
||||
filenameBox.setText (lastFilename, dontSendNotification);
|
||||
|
||||
if (notification != dontSendNotification)
|
||||
{
|
||||
triggerAsyncUpdate();
|
||||
|
||||
if (notification == sendNotificationSync)
|
||||
handleUpdateNowIfNeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FilenameComponent::setFilenameIsEditable (const bool shouldBeEditable)
|
||||
{
|
||||
filenameBox.setEditableText (shouldBeEditable);
|
||||
}
|
||||
|
||||
StringArray FilenameComponent::getRecentlyUsedFilenames() const
|
||||
{
|
||||
StringArray names;
|
||||
|
||||
for (int i = 0; i < filenameBox.getNumItems(); ++i)
|
||||
names.add (filenameBox.getItemText (i));
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
void FilenameComponent::setRecentlyUsedFilenames (const StringArray& filenames)
|
||||
{
|
||||
if (filenames != getRecentlyUsedFilenames())
|
||||
{
|
||||
filenameBox.clear();
|
||||
|
||||
for (int i = 0; i < jmin (filenames.size(), maxRecentFiles); ++i)
|
||||
filenameBox.addItem (filenames[i], i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void FilenameComponent::setMaxNumberOfRecentFiles (const int newMaximum)
|
||||
{
|
||||
maxRecentFiles = jmax (1, newMaximum);
|
||||
|
||||
setRecentlyUsedFilenames (getRecentlyUsedFilenames());
|
||||
}
|
||||
|
||||
void FilenameComponent::addRecentlyUsedFile (const File& file)
|
||||
{
|
||||
auto files = getRecentlyUsedFilenames();
|
||||
|
||||
if (file.getFullPathName().isNotEmpty())
|
||||
{
|
||||
files.removeString (file.getFullPathName(), true);
|
||||
files.insert (0, file.getFullPathName());
|
||||
|
||||
setRecentlyUsedFilenames (files);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FilenameComponent::addListener (FilenameComponentListener* const listener)
|
||||
{
|
||||
listeners.add (listener);
|
||||
}
|
||||
|
||||
void FilenameComponent::removeListener (FilenameComponentListener* const listener)
|
||||
{
|
||||
listeners.remove (listener);
|
||||
}
|
||||
|
||||
void FilenameComponent::handleAsyncUpdate()
|
||||
{
|
||||
Component::BailOutChecker checker (this);
|
||||
listeners.callChecked (checker, [this] (FilenameComponentListener& l) { l.filenameComponentChanged (this); });
|
||||
}
|
||||
|
||||
} // namespace juce
|
236
deps/juce/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h
vendored
Normal file
236
deps/juce/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h
vendored
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Listens for events happening to a FilenameComponent.
|
||||
|
||||
Use FilenameComponent::addListener() and FilenameComponent::removeListener() to
|
||||
register one of these objects for event callbacks when the filename is changed.
|
||||
|
||||
@see FilenameComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FilenameComponentListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~FilenameComponentListener() = default;
|
||||
|
||||
/** This method is called after the FilenameComponent's file has been changed. */
|
||||
virtual void filenameComponentChanged (FilenameComponent* fileComponentThatHasChanged) = 0;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Shows a filename as an editable text box, with a 'browse' button and a
|
||||
drop-down list for recently selected files.
|
||||
|
||||
A handy component for dialogue boxes where you want the user to be able to
|
||||
select a file or directory.
|
||||
|
||||
Attach an FilenameComponentListener using the addListener() method, and it will
|
||||
get called each time the user changes the filename, either by browsing for a file
|
||||
and clicking 'ok', or by typing a new filename into the box and pressing return.
|
||||
|
||||
@see FileChooser, ComboBox
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FilenameComponent : public Component,
|
||||
public SettableTooltipClient,
|
||||
public FileDragAndDropTarget,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a FilenameComponent.
|
||||
|
||||
@param name the name for this component.
|
||||
@param currentFile the file to initially show in the box
|
||||
@param canEditFilename if true, the user can manually edit the filename; if false,
|
||||
they can only change it by browsing for a new file
|
||||
@param isDirectory if true, the file will be treated as a directory, and
|
||||
an appropriate directory browser used
|
||||
@param isForSaving if true, the file browser will allow non-existent files to
|
||||
be picked, as the file is assumed to be used for saving rather
|
||||
than loading
|
||||
@param fileBrowserWildcard a wildcard pattern to use in the file browser - e.g. "*.txt;*.foo".
|
||||
If an empty string is passed in, then the pattern is assumed to be "*"
|
||||
@param enforcedSuffix if this is non-empty, it is treated as a suffix that will be added
|
||||
to any filenames that are entered or chosen
|
||||
@param textWhenNothingSelected the message to display in the box before any filename is entered. (This
|
||||
will only appear if the initial file isn't valid)
|
||||
*/
|
||||
FilenameComponent (const String& name,
|
||||
const File& currentFile,
|
||||
bool canEditFilename,
|
||||
bool isDirectory,
|
||||
bool isForSaving,
|
||||
const String& fileBrowserWildcard,
|
||||
const String& enforcedSuffix,
|
||||
const String& textWhenNothingSelected);
|
||||
|
||||
/** Destructor. */
|
||||
~FilenameComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the currently displayed filename. */
|
||||
File getCurrentFile() const;
|
||||
|
||||
/** Returns the raw text that the user has entered. */
|
||||
String getCurrentFileText() const;
|
||||
|
||||
/** Changes the current filename.
|
||||
|
||||
@param newFile the new filename to use
|
||||
@param addToRecentlyUsedList if true, the filename will also be added to the
|
||||
drop-down list of recent files.
|
||||
@param notification whether to send a notification of the change to listeners.
|
||||
A notification will only be sent if the filename has changed.
|
||||
*/
|
||||
void setCurrentFile (File newFile,
|
||||
bool addToRecentlyUsedList,
|
||||
NotificationType notification = sendNotificationAsync);
|
||||
|
||||
/** Changes whether the use can type into the filename box.
|
||||
*/
|
||||
void setFilenameIsEditable (bool shouldBeEditable);
|
||||
|
||||
/** Sets a file or directory to be the default starting point for the browser to show.
|
||||
|
||||
This is only used if the current file hasn't been set.
|
||||
*/
|
||||
void setDefaultBrowseTarget (const File& newDefaultDirectory);
|
||||
|
||||
/** This can be overridden to return a custom location that you want the dialog box
|
||||
to show when the browse button is pushed.
|
||||
The default implementation of this method will return either the current file
|
||||
(if one has been chosen) or the location that was set by setDefaultBrowseTarget().
|
||||
*/
|
||||
virtual File getLocationToBrowse();
|
||||
|
||||
/** Returns all the entries on the recent files list.
|
||||
|
||||
This can be used in conjunction with setRecentlyUsedFilenames() for saving the
|
||||
state of this list.
|
||||
|
||||
@see setRecentlyUsedFilenames
|
||||
*/
|
||||
StringArray getRecentlyUsedFilenames() const;
|
||||
|
||||
/** Sets all the entries on the recent files list.
|
||||
|
||||
This can be used in conjunction with getRecentlyUsedFilenames() for saving the
|
||||
state of this list.
|
||||
|
||||
@see getRecentlyUsedFilenames, addRecentlyUsedFile
|
||||
*/
|
||||
void setRecentlyUsedFilenames (const StringArray& filenames);
|
||||
|
||||
/** Adds an entry to the recently-used files dropdown list.
|
||||
|
||||
If the file is already in the list, it will be moved to the top. A limit
|
||||
is also placed on the number of items that are kept in the list.
|
||||
|
||||
@see getRecentlyUsedFilenames, setRecentlyUsedFilenames, setMaxNumberOfRecentFiles
|
||||
*/
|
||||
void addRecentlyUsedFile (const File& file);
|
||||
|
||||
/** Changes the limit for the number of files that will be stored in the recent-file list.
|
||||
*/
|
||||
void setMaxNumberOfRecentFiles (int newMaximum);
|
||||
|
||||
/** Changes the text shown on the 'browse' button.
|
||||
|
||||
By default this button just says "..." but you can change it. The button itself
|
||||
can be changed using the look-and-feel classes, so it might not actually have any
|
||||
text on it.
|
||||
*/
|
||||
void setBrowseButtonText (const String& browseButtonText);
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a listener that will be called when the selected file is changed. */
|
||||
void addListener (FilenameComponentListener* listener);
|
||||
|
||||
/** Removes a previously-registered listener. */
|
||||
void removeListener (FilenameComponentListener* listener);
|
||||
|
||||
/** Gives the component a tooltip. */
|
||||
void setTooltip (const String& newTooltip) override;
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes. */
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual Button* createFilenameComponentBrowseButton (const String& text) = 0;
|
||||
virtual void layoutFilenameComponent (FilenameComponent&, ComboBox* filenameBox, Button* browseButton) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paintOverChildren (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
/** @internal */
|
||||
bool isInterestedInFileDrag (const StringArray&) override;
|
||||
/** @internal */
|
||||
void filesDropped (const StringArray&, int, int) override;
|
||||
/** @internal */
|
||||
void fileDragEnter (const StringArray&, int, int) override;
|
||||
/** @internal */
|
||||
void fileDragExit (const StringArray&) override;
|
||||
/** @internal */
|
||||
std::unique_ptr<ComponentTraverser> createKeyboardFocusTraverser() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void handleAsyncUpdate() override;
|
||||
|
||||
void showChooser();
|
||||
|
||||
ComboBox filenameBox;
|
||||
String lastFilename;
|
||||
std::unique_ptr<Button> browseButton;
|
||||
int maxRecentFiles = 30;
|
||||
bool isDir, isSaving, isFileDragOver = false;
|
||||
String wildcard, enforcedSuffix, browseButtonText;
|
||||
ListenerList <FilenameComponentListener> listeners;
|
||||
File defaultBrowseFile;
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilenameComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
126
deps/juce/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.cpp
vendored
Normal file
126
deps/juce/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.cpp
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
ImagePreviewComponent::ImagePreviewComponent()
|
||||
{
|
||||
}
|
||||
|
||||
ImagePreviewComponent::~ImagePreviewComponent()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ImagePreviewComponent::getThumbSize (int& w, int& h) const
|
||||
{
|
||||
auto availableW = proportionOfWidth (0.97f);
|
||||
auto availableH = getHeight() - 13 * 4;
|
||||
|
||||
auto scale = jmin (1.0,
|
||||
availableW / (double) w,
|
||||
availableH / (double) h);
|
||||
|
||||
w = roundToInt (scale * w);
|
||||
h = roundToInt (scale * h);
|
||||
}
|
||||
|
||||
void ImagePreviewComponent::selectedFileChanged (const File& file)
|
||||
{
|
||||
if (fileToLoad != file)
|
||||
{
|
||||
fileToLoad = file;
|
||||
startTimer (100);
|
||||
}
|
||||
}
|
||||
|
||||
void ImagePreviewComponent::timerCallback()
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
currentThumbnail = Image();
|
||||
currentDetails.clear();
|
||||
repaint();
|
||||
|
||||
FileInputStream in (fileToLoad);
|
||||
|
||||
if (in.openedOk() && fileToLoad.existsAsFile())
|
||||
{
|
||||
if (auto format = ImageFileFormat::findImageFormatForStream (in))
|
||||
{
|
||||
currentThumbnail = format->decodeImage (in);
|
||||
|
||||
if (currentThumbnail.isValid())
|
||||
{
|
||||
auto w = currentThumbnail.getWidth();
|
||||
auto h = currentThumbnail.getHeight();
|
||||
|
||||
currentDetails
|
||||
<< fileToLoad.getFileName() << "\n"
|
||||
<< format->getFormatName() << "\n"
|
||||
<< w << " x " << h << " pixels\n"
|
||||
<< File::descriptionOfSizeInBytes (fileToLoad.getSize());
|
||||
|
||||
getThumbSize (w, h);
|
||||
|
||||
currentThumbnail = currentThumbnail.rescaled (w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImagePreviewComponent::paint (Graphics& g)
|
||||
{
|
||||
if (currentThumbnail.isValid())
|
||||
{
|
||||
g.setFont (13.0f);
|
||||
|
||||
auto w = currentThumbnail.getWidth();
|
||||
auto h = currentThumbnail.getHeight();
|
||||
getThumbSize (w, h);
|
||||
|
||||
const int numLines = 4;
|
||||
auto totalH = 13 * numLines + h + 4;
|
||||
auto y = (getHeight() - totalH) / 2;
|
||||
|
||||
g.drawImageWithin (currentThumbnail,
|
||||
(getWidth() - w) / 2, y, w, h,
|
||||
RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize,
|
||||
false);
|
||||
|
||||
g.drawFittedText (currentDetails,
|
||||
0, y + h + 4, getWidth(), 100,
|
||||
Justification::centredTop, numLines);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> ImagePreviewComponent::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::image);
|
||||
}
|
||||
|
||||
} // namespace juce
|
67
deps/juce/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.h
vendored
Normal file
67
deps/juce/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.h
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple preview component that shows thumbnails of image files.
|
||||
|
||||
@see FileChooserDialogBox, FilePreviewComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ImagePreviewComponent : public FilePreviewComponent,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an ImagePreviewComponent. */
|
||||
ImagePreviewComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~ImagePreviewComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void selectedFileChanged (const File& newSelectedFile) override;
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void timerCallback() override;
|
||||
|
||||
private:
|
||||
File fileToLoad;
|
||||
Image currentThumbnail;
|
||||
String currentDetails;
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void getThumbSize (int& w, int& h) const;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePreviewComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
379
deps/juce/modules/juce_gui_basics/juce_gui_basics.cpp
vendored
Normal file
379
deps/juce/modules/juce_gui_basics/juce_gui_basics.cpp
vendored
Normal file
@ -0,0 +1,379 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifdef JUCE_GUI_BASICS_H_INCLUDED
|
||||
/* When you add this cpp file to your project, you mustn't include it in a file where you've
|
||||
already included any other headers - just put it inside a file on its own, possibly with your config
|
||||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
|
||||
header files that the compiler may be using.
|
||||
*/
|
||||
#error "Incorrect use of JUCE cpp file"
|
||||
#endif
|
||||
|
||||
#define NS_FORMAT_FUNCTION(F,A) // To avoid spurious warnings from GCC
|
||||
|
||||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1
|
||||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1
|
||||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1
|
||||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
|
||||
#define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1
|
||||
#define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1
|
||||
#define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1
|
||||
#define JUCE_GUI_BASICS_INCLUDE_SCOPED_THREAD_DPI_AWARENESS_SETTER 1
|
||||
|
||||
#include "juce_gui_basics.h"
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
#import <WebKit/WebKit.h>
|
||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||
|
||||
#if JUCE_SUPPORT_CARBON
|
||||
#import <Carbon/Carbon.h> // still needed for SetSystemUIMode()
|
||||
#endif
|
||||
|
||||
#elif JUCE_IOS
|
||||
#if JUCE_PUSH_NOTIFICATIONS && defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
#endif
|
||||
|
||||
#import <UIKit/UIActivityViewController.h>
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_WINDOWS
|
||||
#include <windowsx.h>
|
||||
#include <vfw.h>
|
||||
#include <commdlg.h>
|
||||
#include <commctrl.h>
|
||||
|
||||
#if ! JUCE_MINGW
|
||||
#include <UIAutomation.h>
|
||||
#include <sapi.h>
|
||||
#endif
|
||||
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include <exdisp.h>
|
||||
#include <exdispid.h>
|
||||
#endif
|
||||
|
||||
#if JUCE_MINGW
|
||||
#include <imm.h>
|
||||
#elif ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
|
||||
#pragma comment(lib, "vfw32.lib")
|
||||
#pragma comment(lib, "imm32.lib")
|
||||
#pragma comment(lib, "comctl32.lib")
|
||||
|
||||
#if JUCE_OPENGL
|
||||
#pragma comment(lib, "OpenGL32.Lib")
|
||||
#pragma comment(lib, "GlU32.Lib")
|
||||
#endif
|
||||
|
||||
#if JUCE_DIRECT2D
|
||||
#pragma comment (lib, "Dwrite.lib")
|
||||
#pragma comment (lib, "D2d1.lib")
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <set>
|
||||
|
||||
//==============================================================================
|
||||
#define JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN \
|
||||
jassert ((MessageManager::getInstanceWithoutCreating() != nullptr \
|
||||
&& MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager()) \
|
||||
|| getPeer() == nullptr);
|
||||
|
||||
namespace juce
|
||||
{
|
||||
bool juce_areThereAnyAlwaysOnTopWindows();
|
||||
|
||||
bool isEmbeddedInForegroundProcess (Component* c);
|
||||
|
||||
#if ! JUCE_WINDOWS
|
||||
bool isEmbeddedInForegroundProcess (Component*) { return false; }
|
||||
#endif
|
||||
|
||||
/* Returns true if this process is in the foreground, or if the viewComponent
|
||||
is embedded into a window owned by the foreground process.
|
||||
*/
|
||||
static bool isForegroundOrEmbeddedProcess (Component* viewComponent)
|
||||
{
|
||||
return Process::isForegroundProcess() || isEmbeddedInForegroundProcess (viewComponent);
|
||||
}
|
||||
}
|
||||
|
||||
#include "accessibility/juce_AccessibilityHandler.cpp"
|
||||
#include "components/juce_Component.cpp"
|
||||
#include "components/juce_ComponentListener.cpp"
|
||||
#include "components/juce_FocusTraverser.cpp"
|
||||
#include "mouse/juce_MouseInputSource.cpp"
|
||||
#include "desktop/juce_Displays.cpp"
|
||||
#include "desktop/juce_Desktop.cpp"
|
||||
#include "components/juce_ModalComponentManager.cpp"
|
||||
#include "mouse/juce_ComponentDragger.cpp"
|
||||
#include "mouse/juce_DragAndDropContainer.cpp"
|
||||
#include "mouse/juce_MouseCursor.cpp"
|
||||
#include "mouse/juce_MouseEvent.cpp"
|
||||
#include "mouse/juce_MouseInactivityDetector.cpp"
|
||||
#include "mouse/juce_MouseListener.cpp"
|
||||
#include "keyboard/juce_CaretComponent.cpp"
|
||||
#include "keyboard/juce_KeyboardFocusTraverser.cpp"
|
||||
#include "keyboard/juce_KeyListener.cpp"
|
||||
#include "keyboard/juce_KeyPress.cpp"
|
||||
#include "keyboard/juce_ModifierKeys.cpp"
|
||||
#include "buttons/juce_ArrowButton.cpp"
|
||||
#include "buttons/juce_Button.cpp"
|
||||
#include "buttons/juce_DrawableButton.cpp"
|
||||
#include "buttons/juce_HyperlinkButton.cpp"
|
||||
#include "buttons/juce_ImageButton.cpp"
|
||||
#include "buttons/juce_ShapeButton.cpp"
|
||||
#include "buttons/juce_TextButton.cpp"
|
||||
#include "buttons/juce_ToggleButton.cpp"
|
||||
#include "buttons/juce_ToolbarButton.cpp"
|
||||
#include "drawables/juce_Drawable.cpp"
|
||||
#include "drawables/juce_DrawableComposite.cpp"
|
||||
#include "drawables/juce_DrawableImage.cpp"
|
||||
#include "drawables/juce_DrawablePath.cpp"
|
||||
#include "drawables/juce_DrawableRectangle.cpp"
|
||||
#include "drawables/juce_DrawableShape.cpp"
|
||||
#include "drawables/juce_DrawableText.cpp"
|
||||
#include "drawables/juce_SVGParser.cpp"
|
||||
#include "filebrowser/juce_DirectoryContentsDisplayComponent.cpp"
|
||||
#include "filebrowser/juce_DirectoryContentsList.cpp"
|
||||
#include "filebrowser/juce_FileBrowserComponent.cpp"
|
||||
#include "filebrowser/juce_FileChooser.cpp"
|
||||
#include "filebrowser/juce_FileChooserDialogBox.cpp"
|
||||
#include "filebrowser/juce_FileListComponent.cpp"
|
||||
#include "filebrowser/juce_FilenameComponent.cpp"
|
||||
#include "filebrowser/juce_FileSearchPathListComponent.cpp"
|
||||
#include "filebrowser/juce_FileTreeComponent.cpp"
|
||||
#include "filebrowser/juce_ImagePreviewComponent.cpp"
|
||||
#include "filebrowser/juce_ContentSharer.cpp"
|
||||
#include "layout/juce_ComponentAnimator.cpp"
|
||||
#include "layout/juce_ComponentBoundsConstrainer.cpp"
|
||||
#include "layout/juce_ComponentBuilder.cpp"
|
||||
#include "layout/juce_ComponentMovementWatcher.cpp"
|
||||
#include "layout/juce_ConcertinaPanel.cpp"
|
||||
#include "layout/juce_GroupComponent.cpp"
|
||||
#include "layout/juce_MultiDocumentPanel.cpp"
|
||||
#include "layout/juce_ResizableBorderComponent.cpp"
|
||||
#include "layout/juce_ResizableCornerComponent.cpp"
|
||||
#include "layout/juce_ResizableEdgeComponent.cpp"
|
||||
#include "layout/juce_ScrollBar.cpp"
|
||||
#include "layout/juce_SidePanel.cpp"
|
||||
#include "layout/juce_StretchableLayoutManager.cpp"
|
||||
#include "layout/juce_StretchableLayoutResizerBar.cpp"
|
||||
#include "layout/juce_StretchableObjectResizer.cpp"
|
||||
#include "layout/juce_TabbedButtonBar.cpp"
|
||||
#include "layout/juce_TabbedComponent.cpp"
|
||||
#include "layout/juce_Viewport.cpp"
|
||||
#include "lookandfeel/juce_LookAndFeel.cpp"
|
||||
#include "lookandfeel/juce_LookAndFeel_V2.cpp"
|
||||
#include "lookandfeel/juce_LookAndFeel_V1.cpp"
|
||||
#include "lookandfeel/juce_LookAndFeel_V3.cpp"
|
||||
#include "lookandfeel/juce_LookAndFeel_V4.cpp"
|
||||
#include "menus/juce_MenuBarComponent.cpp"
|
||||
#include "menus/juce_BurgerMenuComponent.cpp"
|
||||
#include "menus/juce_MenuBarModel.cpp"
|
||||
#include "menus/juce_PopupMenu.cpp"
|
||||
#include "positioning/juce_MarkerList.cpp"
|
||||
#include "positioning/juce_RelativeCoordinate.cpp"
|
||||
#include "positioning/juce_RelativeCoordinatePositioner.cpp"
|
||||
#include "positioning/juce_RelativeParallelogram.cpp"
|
||||
#include "positioning/juce_RelativePoint.cpp"
|
||||
#include "positioning/juce_RelativePointPath.cpp"
|
||||
#include "positioning/juce_RelativeRectangle.cpp"
|
||||
#include "properties/juce_BooleanPropertyComponent.cpp"
|
||||
#include "properties/juce_ButtonPropertyComponent.cpp"
|
||||
#include "properties/juce_ChoicePropertyComponent.cpp"
|
||||
#include "properties/juce_PropertyComponent.cpp"
|
||||
#include "properties/juce_PropertyPanel.cpp"
|
||||
#include "properties/juce_SliderPropertyComponent.cpp"
|
||||
#include "properties/juce_TextPropertyComponent.cpp"
|
||||
#include "properties/juce_MultiChoicePropertyComponent.cpp"
|
||||
#include "widgets/juce_ComboBox.cpp"
|
||||
#include "widgets/juce_ImageComponent.cpp"
|
||||
#include "widgets/juce_Label.cpp"
|
||||
#include "widgets/juce_ListBox.cpp"
|
||||
#include "widgets/juce_ProgressBar.cpp"
|
||||
#include "widgets/juce_Slider.cpp"
|
||||
#include "widgets/juce_TableHeaderComponent.cpp"
|
||||
#include "widgets/juce_TableListBox.cpp"
|
||||
#include "widgets/juce_TextEditor.cpp"
|
||||
#include "widgets/juce_ToolbarItemComponent.cpp"
|
||||
#include "widgets/juce_Toolbar.cpp"
|
||||
#include "widgets/juce_ToolbarItemPalette.cpp"
|
||||
#include "widgets/juce_TreeView.cpp"
|
||||
#include "windows/juce_AlertWindow.cpp"
|
||||
#include "windows/juce_CallOutBox.cpp"
|
||||
#include "windows/juce_ComponentPeer.cpp"
|
||||
#include "windows/juce_DialogWindow.cpp"
|
||||
#include "windows/juce_DocumentWindow.cpp"
|
||||
#include "windows/juce_ResizableWindow.cpp"
|
||||
#include "windows/juce_ThreadWithProgressWindow.cpp"
|
||||
#include "windows/juce_TooltipWindow.cpp"
|
||||
#include "windows/juce_TopLevelWindow.cpp"
|
||||
#include "commands/juce_ApplicationCommandInfo.cpp"
|
||||
#include "commands/juce_ApplicationCommandManager.cpp"
|
||||
#include "commands/juce_ApplicationCommandTarget.cpp"
|
||||
#include "commands/juce_KeyPressMappingSet.cpp"
|
||||
#include "application/juce_Application.cpp"
|
||||
#include "misc/juce_BubbleComponent.cpp"
|
||||
#include "misc/juce_DropShadower.cpp"
|
||||
#include "misc/juce_JUCESplashScreen.cpp"
|
||||
|
||||
#include "layout/juce_FlexBox.cpp"
|
||||
#include "layout/juce_GridItem.cpp"
|
||||
#include "layout/juce_Grid.cpp"
|
||||
|
||||
#if JUCE_IOS || JUCE_WINDOWS
|
||||
#include "native/juce_MultiTouchMapper.h"
|
||||
#endif
|
||||
|
||||
#if JUCE_ANDROID || JUCE_WINDOWS
|
||||
#include "native/accessibility/juce_AccessibilityTextHelpers.h"
|
||||
#endif
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
static const juce::Identifier disableAsyncLayerBackedViewIdentifier { "disableAsyncLayerBackedView" };
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes")
|
||||
|
||||
/** Used by the macOS and iOS peers. */
|
||||
void setComponentAsyncLayerBackedViewDisabled (juce::Component& comp, bool shouldDisableAsyncLayerBackedView)
|
||||
{
|
||||
comp.getProperties().set (disableAsyncLayerBackedViewIdentifier, shouldDisableAsyncLayerBackedView);
|
||||
}
|
||||
|
||||
/** Used by the macOS and iOS peers. */
|
||||
bool getComponentAsyncLayerBackedViewDisabled (juce::Component& comp)
|
||||
{
|
||||
return comp.getProperties()[disableAsyncLayerBackedViewIdentifier];
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace juce
|
||||
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
#include "native/accessibility/juce_mac_AccessibilitySharedCode.mm"
|
||||
|
||||
#if JUCE_IOS
|
||||
#include "native/accessibility/juce_ios_Accessibility.mm"
|
||||
#include "native/juce_ios_UIViewComponentPeer.mm"
|
||||
#include "native/juce_ios_Windowing.mm"
|
||||
#include "native/juce_ios_FileChooser.mm"
|
||||
|
||||
#if JUCE_CONTENT_SHARING
|
||||
#include "native/juce_ios_ContentSharer.cpp"
|
||||
#endif
|
||||
|
||||
#else
|
||||
#include "native/accessibility/juce_mac_Accessibility.mm"
|
||||
#include "native/juce_mac_NSViewComponentPeer.mm"
|
||||
#include "native/juce_mac_Windowing.mm"
|
||||
#include "native/juce_mac_MainMenu.mm"
|
||||
#include "native/juce_mac_FileChooser.mm"
|
||||
#endif
|
||||
|
||||
#include "native/juce_mac_MouseCursor.mm"
|
||||
|
||||
#elif JUCE_WINDOWS
|
||||
|
||||
#if ! JUCE_MINGW
|
||||
#include "native/accessibility/juce_win32_WindowsUIAWrapper.h"
|
||||
#include "native/accessibility/juce_win32_AccessibilityElement.h"
|
||||
#include "native/accessibility/juce_win32_UIAHelpers.h"
|
||||
#include "native/accessibility/juce_win32_UIAProviders.h"
|
||||
#include "native/accessibility/juce_win32_AccessibilityElement.cpp"
|
||||
#include "native/accessibility/juce_win32_Accessibility.cpp"
|
||||
#else
|
||||
namespace juce
|
||||
{
|
||||
namespace WindowsAccessibility
|
||||
{
|
||||
long getUiaRootObjectId() { return -1; }
|
||||
bool handleWmGetObject (AccessibilityHandler*, WPARAM, LPARAM, LRESULT*) { return false; }
|
||||
void revokeUIAMapEntriesForWindow (HWND) {}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "native/juce_win32_Windowing.cpp"
|
||||
#include "native/juce_win32_DragAndDrop.cpp"
|
||||
#include "native/juce_win32_FileChooser.cpp"
|
||||
|
||||
#elif JUCE_LINUX || JUCE_BSD
|
||||
#include "native/x11/juce_linux_X11_Symbols.cpp"
|
||||
#include "native/x11/juce_linux_X11_DragAndDrop.cpp"
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-as-null-pointer-constant")
|
||||
|
||||
#include "native/juce_linux_Windowing.cpp"
|
||||
#include "native/x11/juce_linux_XWindowSystem.cpp"
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
#include "native/juce_linux_FileChooser.cpp"
|
||||
|
||||
#elif JUCE_ANDROID
|
||||
#include "native/accessibility/juce_android_Accessibility.cpp"
|
||||
#include "native/juce_android_Windowing.cpp"
|
||||
#include "native/juce_common_MimeTypes.cpp"
|
||||
#include "native/juce_android_FileChooser.cpp"
|
||||
|
||||
#if JUCE_CONTENT_SHARING
|
||||
#include "native/juce_android_ContentSharer.cpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
namespace juce
|
||||
{
|
||||
#if ! JUCE_NATIVE_ACCESSIBILITY_INCLUDED
|
||||
class AccessibilityHandler::AccessibilityNativeImpl { public: AccessibilityNativeImpl (AccessibilityHandler&) {} };
|
||||
void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent) const {}
|
||||
void AccessibilityHandler::postAnnouncement (const String&, AnnouncementPriority) {}
|
||||
AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const { return nullptr; }
|
||||
void notifyAccessibilityEventInternal (const AccessibilityHandler&, InternalAccessibilityEvent) {}
|
||||
std::unique_ptr<AccessibilityHandler::AccessibilityNativeImpl> AccessibilityHandler::createNativeImpl (AccessibilityHandler&)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
std::unique_ptr<AccessibilityHandler::AccessibilityNativeImpl> AccessibilityHandler::createNativeImpl (AccessibilityHandler& handler)
|
||||
{
|
||||
return std::make_unique<AccessibilityNativeImpl> (handler);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if ! JUCE_WINDOWS
|
||||
juce::ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() { ignoreUnused (previousContext); }
|
||||
juce::ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() {}
|
||||
#endif
|
373
deps/juce/modules/juce_gui_basics/juce_gui_basics.h
vendored
Normal file
373
deps/juce/modules/juce_gui_basics/juce_gui_basics.h
vendored
Normal file
@ -0,0 +1,373 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this module, and is read by
|
||||
the Projucer to automatically generate project code that uses it.
|
||||
For details about the syntax and how to create or use a module, see the
|
||||
JUCE Module Format.md file.
|
||||
|
||||
|
||||
BEGIN_JUCE_MODULE_DECLARATION
|
||||
|
||||
ID: juce_gui_basics
|
||||
vendor: juce
|
||||
version: 6.1.2
|
||||
name: JUCE GUI core classes
|
||||
description: Basic user-interface components and related classes.
|
||||
website: http://www.juce.com/juce
|
||||
license: GPL/Commercial
|
||||
minimumCppStandard: 14
|
||||
|
||||
dependencies: juce_graphics juce_data_structures
|
||||
OSXFrameworks: Cocoa Carbon QuartzCore
|
||||
iOSFrameworks: UIKit CoreServices
|
||||
|
||||
END_JUCE_MODULE_DECLARATION
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
#define JUCE_GUI_BASICS_H_INCLUDED
|
||||
|
||||
#include <juce_graphics/juce_graphics.h>
|
||||
#include <juce_data_structures/juce_data_structures.h>
|
||||
|
||||
//==============================================================================
|
||||
/** Config: JUCE_ENABLE_REPAINT_DEBUGGING
|
||||
If this option is turned on, each area of the screen that gets repainted will
|
||||
flash in a random colour, so that you can see exactly which bits of your
|
||||
components are being drawn.
|
||||
*/
|
||||
#ifndef JUCE_ENABLE_REPAINT_DEBUGGING
|
||||
#define JUCE_ENABLE_REPAINT_DEBUGGING 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_XRANDR
|
||||
Enables Xrandr multi-monitor support (Linux only).
|
||||
Unless you specifically want to disable this, it's best to leave this option turned on.
|
||||
Note that your users do not need to have Xrandr installed for your JUCE app to run, as
|
||||
the availability of Xrandr is queried during runtime.
|
||||
*/
|
||||
#ifndef JUCE_USE_XRANDR
|
||||
#define JUCE_USE_XRANDR 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_XINERAMA
|
||||
Enables Xinerama multi-monitor support (Linux only).
|
||||
Unless you specifically want to disable this, it's best to leave this option turned on.
|
||||
This will be used as a fallback if JUCE_USE_XRANDR not set or libxrandr cannot be found.
|
||||
Note that your users do not need to have Xinerama installed for your JUCE app to run, as
|
||||
the availability of Xinerama is queried during runtime.
|
||||
*/
|
||||
#ifndef JUCE_USE_XINERAMA
|
||||
#define JUCE_USE_XINERAMA 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_XSHM
|
||||
Enables X shared memory for faster rendering on Linux. This is best left turned on
|
||||
unless you have a good reason to disable it.
|
||||
*/
|
||||
#ifndef JUCE_USE_XSHM
|
||||
#define JUCE_USE_XSHM 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_XRENDER
|
||||
Enables XRender to allow semi-transparent windowing on Linux.
|
||||
*/
|
||||
#ifndef JUCE_USE_XRENDER
|
||||
#define JUCE_USE_XRENDER 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_XCURSOR
|
||||
Uses XCursor to allow ARGB cursor on Linux. This is best left turned on unless you have
|
||||
a good reason to disable it.
|
||||
*/
|
||||
#ifndef JUCE_USE_XCURSOR
|
||||
#define JUCE_USE_XCURSOR 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_WIN_PER_MONITOR_DPI_AWARE
|
||||
Enables per-monitor DPI awareness on Windows 8.1 and above.
|
||||
*/
|
||||
#ifndef JUCE_WIN_PER_MONITOR_DPI_AWARE
|
||||
#define JUCE_WIN_PER_MONITOR_DPI_AWARE 1
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
namespace juce
|
||||
{
|
||||
class Component;
|
||||
class LookAndFeel;
|
||||
class MouseInputSource;
|
||||
class MouseInputSourceInternal;
|
||||
class ComponentPeer;
|
||||
class MouseEvent;
|
||||
struct MouseWheelDetails;
|
||||
struct PenDetails;
|
||||
class ToggleButton;
|
||||
class TextButton;
|
||||
class AlertWindow;
|
||||
class TextLayout;
|
||||
class ScrollBar;
|
||||
class ComboBox;
|
||||
class Button;
|
||||
class FilenameComponent;
|
||||
class ResizableWindow;
|
||||
class MenuBarComponent;
|
||||
class GlyphArrangement;
|
||||
class TableHeaderComponent;
|
||||
class Toolbar;
|
||||
class PopupMenu;
|
||||
class ProgressBar;
|
||||
class FileBrowserComponent;
|
||||
class DirectoryContentsDisplayComponent;
|
||||
class FilePreviewComponent;
|
||||
class CallOutBox;
|
||||
class Drawable;
|
||||
class DrawablePath;
|
||||
class DrawableComposite;
|
||||
class CaretComponent;
|
||||
class KeyPressMappingSet;
|
||||
class ApplicationCommandManagerListener;
|
||||
class DrawableButton;
|
||||
class Displays;
|
||||
class AccessibilityHandler;
|
||||
class KeyboardFocusTraverser;
|
||||
|
||||
class FlexBox;
|
||||
class Grid;
|
||||
|
||||
#if JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX
|
||||
Image createSnapshotOfNativeWindow (void* nativeWindowHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
#include "mouse/juce_MouseCursor.h"
|
||||
#include "mouse/juce_MouseListener.h"
|
||||
#include "keyboard/juce_ModifierKeys.h"
|
||||
#include "mouse/juce_MouseInputSource.h"
|
||||
#include "mouse/juce_MouseEvent.h"
|
||||
#include "keyboard/juce_KeyPress.h"
|
||||
#include "keyboard/juce_KeyListener.h"
|
||||
#include "components/juce_ComponentTraverser.h"
|
||||
#include "components/juce_FocusTraverser.h"
|
||||
#include "components/juce_ModalComponentManager.h"
|
||||
#include "components/juce_ComponentListener.h"
|
||||
#include "components/juce_CachedComponentImage.h"
|
||||
#include "components/juce_Component.h"
|
||||
#include "layout/juce_ComponentAnimator.h"
|
||||
#include "desktop/juce_Desktop.h"
|
||||
#include "desktop/juce_Displays.h"
|
||||
#include "layout/juce_ComponentBoundsConstrainer.h"
|
||||
#include "mouse/juce_ComponentDragger.h"
|
||||
#include "mouse/juce_DragAndDropTarget.h"
|
||||
#include "mouse/juce_DragAndDropContainer.h"
|
||||
#include "mouse/juce_FileDragAndDropTarget.h"
|
||||
#include "mouse/juce_SelectedItemSet.h"
|
||||
#include "mouse/juce_MouseInactivityDetector.h"
|
||||
#include "mouse/juce_TextDragAndDropTarget.h"
|
||||
#include "mouse/juce_TooltipClient.h"
|
||||
#include "keyboard/juce_CaretComponent.h"
|
||||
#include "keyboard/juce_KeyboardFocusTraverser.h"
|
||||
#include "keyboard/juce_SystemClipboard.h"
|
||||
#include "keyboard/juce_TextEditorKeyMapper.h"
|
||||
#include "keyboard/juce_TextInputTarget.h"
|
||||
#include "commands/juce_ApplicationCommandID.h"
|
||||
#include "commands/juce_ApplicationCommandInfo.h"
|
||||
#include "commands/juce_ApplicationCommandTarget.h"
|
||||
#include "commands/juce_ApplicationCommandManager.h"
|
||||
#include "commands/juce_KeyPressMappingSet.h"
|
||||
#include "buttons/juce_Button.h"
|
||||
#include "buttons/juce_ArrowButton.h"
|
||||
#include "buttons/juce_DrawableButton.h"
|
||||
#include "buttons/juce_HyperlinkButton.h"
|
||||
#include "buttons/juce_ImageButton.h"
|
||||
#include "buttons/juce_ShapeButton.h"
|
||||
#include "buttons/juce_TextButton.h"
|
||||
#include "buttons/juce_ToggleButton.h"
|
||||
#include "layout/juce_AnimatedPosition.h"
|
||||
#include "layout/juce_AnimatedPositionBehaviours.h"
|
||||
#include "layout/juce_ComponentBuilder.h"
|
||||
#include "layout/juce_ComponentMovementWatcher.h"
|
||||
#include "layout/juce_ConcertinaPanel.h"
|
||||
#include "layout/juce_GroupComponent.h"
|
||||
#include "layout/juce_ResizableBorderComponent.h"
|
||||
#include "layout/juce_ResizableCornerComponent.h"
|
||||
#include "layout/juce_ResizableEdgeComponent.h"
|
||||
#include "layout/juce_ScrollBar.h"
|
||||
#include "layout/juce_StretchableLayoutManager.h"
|
||||
#include "layout/juce_StretchableLayoutResizerBar.h"
|
||||
#include "layout/juce_StretchableObjectResizer.h"
|
||||
#include "layout/juce_TabbedButtonBar.h"
|
||||
#include "layout/juce_TabbedComponent.h"
|
||||
#include "layout/juce_Viewport.h"
|
||||
#include "menus/juce_PopupMenu.h"
|
||||
#include "menus/juce_MenuBarModel.h"
|
||||
#include "menus/juce_MenuBarComponent.h"
|
||||
#include "positioning/juce_RelativeCoordinate.h"
|
||||
#include "positioning/juce_MarkerList.h"
|
||||
#include "positioning/juce_RelativePoint.h"
|
||||
#include "positioning/juce_RelativeRectangle.h"
|
||||
#include "positioning/juce_RelativeCoordinatePositioner.h"
|
||||
#include "positioning/juce_RelativeParallelogram.h"
|
||||
#include "positioning/juce_RelativePointPath.h"
|
||||
#include "drawables/juce_Drawable.h"
|
||||
#include "drawables/juce_DrawableShape.h"
|
||||
#include "drawables/juce_DrawableComposite.h"
|
||||
#include "drawables/juce_DrawableImage.h"
|
||||
#include "drawables/juce_DrawablePath.h"
|
||||
#include "drawables/juce_DrawableRectangle.h"
|
||||
#include "drawables/juce_DrawableText.h"
|
||||
#include "widgets/juce_TextEditor.h"
|
||||
#include "widgets/juce_Label.h"
|
||||
#include "widgets/juce_ImageComponent.h"
|
||||
#include "widgets/juce_ComboBox.h"
|
||||
#include "widgets/juce_ListBox.h"
|
||||
#include "widgets/juce_ProgressBar.h"
|
||||
#include "widgets/juce_Slider.h"
|
||||
#include "widgets/juce_TableHeaderComponent.h"
|
||||
#include "widgets/juce_TableListBox.h"
|
||||
#include "widgets/juce_Toolbar.h"
|
||||
#include "widgets/juce_ToolbarItemComponent.h"
|
||||
#include "widgets/juce_ToolbarItemFactory.h"
|
||||
#include "widgets/juce_ToolbarItemPalette.h"
|
||||
#include "menus/juce_BurgerMenuComponent.h"
|
||||
#include "buttons/juce_ToolbarButton.h"
|
||||
#include "misc/juce_DropShadower.h"
|
||||
#include "misc/juce_JUCESplashScreen.h"
|
||||
#include "widgets/juce_TreeView.h"
|
||||
#include "windows/juce_TopLevelWindow.h"
|
||||
#include "windows/juce_MessageBoxOptions.h"
|
||||
#include "windows/juce_AlertWindow.h"
|
||||
#include "windows/juce_CallOutBox.h"
|
||||
#include "windows/juce_ComponentPeer.h"
|
||||
#include "windows/juce_ResizableWindow.h"
|
||||
#include "windows/juce_DocumentWindow.h"
|
||||
#include "windows/juce_DialogWindow.h"
|
||||
#include "windows/juce_NativeMessageBox.h"
|
||||
#include "windows/juce_ThreadWithProgressWindow.h"
|
||||
#include "windows/juce_TooltipWindow.h"
|
||||
#include "layout/juce_MultiDocumentPanel.h"
|
||||
#include "layout/juce_SidePanel.h"
|
||||
#include "filebrowser/juce_FileBrowserListener.h"
|
||||
#include "filebrowser/juce_DirectoryContentsList.h"
|
||||
#include "filebrowser/juce_DirectoryContentsDisplayComponent.h"
|
||||
#include "filebrowser/juce_FileBrowserComponent.h"
|
||||
#include "filebrowser/juce_FileChooser.h"
|
||||
#include "filebrowser/juce_FileChooserDialogBox.h"
|
||||
#include "filebrowser/juce_FileListComponent.h"
|
||||
#include "filebrowser/juce_FilenameComponent.h"
|
||||
#include "filebrowser/juce_FilePreviewComponent.h"
|
||||
#include "filebrowser/juce_FileSearchPathListComponent.h"
|
||||
#include "filebrowser/juce_FileTreeComponent.h"
|
||||
#include "filebrowser/juce_ImagePreviewComponent.h"
|
||||
#include "filebrowser/juce_ContentSharer.h"
|
||||
#include "properties/juce_PropertyComponent.h"
|
||||
#include "properties/juce_BooleanPropertyComponent.h"
|
||||
#include "properties/juce_ButtonPropertyComponent.h"
|
||||
#include "properties/juce_ChoicePropertyComponent.h"
|
||||
#include "properties/juce_PropertyPanel.h"
|
||||
#include "properties/juce_SliderPropertyComponent.h"
|
||||
#include "properties/juce_TextPropertyComponent.h"
|
||||
#include "properties/juce_MultiChoicePropertyComponent.h"
|
||||
#include "application/juce_Application.h"
|
||||
#include "misc/juce_BubbleComponent.h"
|
||||
#include "lookandfeel/juce_LookAndFeel.h"
|
||||
#include "lookandfeel/juce_LookAndFeel_V2.h"
|
||||
#include "lookandfeel/juce_LookAndFeel_V1.h"
|
||||
#include "lookandfeel/juce_LookAndFeel_V3.h"
|
||||
#include "lookandfeel/juce_LookAndFeel_V4.h"
|
||||
#include "mouse/juce_LassoComponent.h"
|
||||
#include "accessibility/interfaces/juce_AccessibilityCellInterface.h"
|
||||
#include "accessibility/interfaces/juce_AccessibilityTableInterface.h"
|
||||
#include "accessibility/interfaces/juce_AccessibilityTextInterface.h"
|
||||
#include "accessibility/interfaces/juce_AccessibilityValueInterface.h"
|
||||
#include "accessibility/enums/juce_AccessibilityActions.h"
|
||||
#include "accessibility/enums/juce_AccessibilityEvent.h"
|
||||
#include "accessibility/enums/juce_AccessibilityRole.h"
|
||||
#include "accessibility/juce_AccessibilityState.h"
|
||||
#include "accessibility/juce_AccessibilityHandler.h"
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
#if JUCE_GUI_BASICS_INCLUDE_XHEADERS
|
||||
// If you're missing these headers, you need to install the libx11-dev package
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xresource.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xmd.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/XKBlib.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if JUCE_USE_XRANDR
|
||||
// If you're missing this header, you need to install the libxrandr-dev package
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_XINERAMA
|
||||
// If you're missing this header, you need to install the libxinerama-dev package
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_XSHM
|
||||
#include <X11/extensions/XShm.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/ipc.h>
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_XRENDER
|
||||
// If you're missing these headers, you need to install the libxrender-dev and libxcomposite-dev packages
|
||||
#include <X11/extensions/Xrender.h>
|
||||
#include <X11/extensions/Xcomposite.h>
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_XCURSOR
|
||||
// If you're missing this header, you need to install the libxcursor-dev package
|
||||
#include <X11/Xcursor/Xcursor.h>
|
||||
#endif
|
||||
|
||||
#undef SIZEOF
|
||||
#undef KeyPress
|
||||
|
||||
#include "native/x11/juce_linux_XWindowSystem.h"
|
||||
#include "native/x11/juce_linux_X11_Symbols.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if JUCE_GUI_BASICS_INCLUDE_SCOPED_THREAD_DPI_AWARENESS_SETTER && JUCE_WINDOWS
|
||||
#include "native/juce_win32_ScopedThreadDPIAwarenessSetter.h"
|
||||
#endif
|
||||
|
||||
#include "layout/juce_FlexItem.h"
|
||||
#include "layout/juce_FlexBox.h"
|
||||
|
||||
#include "layout/juce_GridItem.h"
|
||||
#include "layout/juce_Grid.h"
|
||||
#include "native/juce_ScopedDPIAwarenessDisabler.h"
|
26
deps/juce/modules/juce_gui_basics/juce_gui_basics.mm
vendored
Normal file
26
deps/juce/modules/juce_gui_basics/juce_gui_basics.mm
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "juce_gui_basics.cpp"
|
64
deps/juce/modules/juce_gui_basics/keyboard/juce_CaretComponent.cpp
vendored
Normal file
64
deps/juce/modules/juce_gui_basics/keyboard/juce_CaretComponent.cpp
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
CaretComponent::CaretComponent (Component* const keyFocusOwner)
|
||||
: owner (keyFocusOwner)
|
||||
{
|
||||
setPaintingIsUnclipped (true);
|
||||
setInterceptsMouseClicks (false, false);
|
||||
}
|
||||
|
||||
CaretComponent::~CaretComponent()
|
||||
{
|
||||
}
|
||||
|
||||
void CaretComponent::paint (Graphics& g)
|
||||
{
|
||||
g.setColour (findColour (caretColourId, true));
|
||||
g.fillRect (getLocalBounds());
|
||||
}
|
||||
|
||||
void CaretComponent::timerCallback()
|
||||
{
|
||||
setVisible (shouldBeShown() && ! isVisible());
|
||||
}
|
||||
|
||||
void CaretComponent::setCaretPosition (const Rectangle<int>& characterArea)
|
||||
{
|
||||
startTimer (380);
|
||||
setVisible (shouldBeShown());
|
||||
setBounds (characterArea.withWidth (2));
|
||||
}
|
||||
|
||||
bool CaretComponent::shouldBeShown() const
|
||||
{
|
||||
return owner == nullptr || (owner->hasKeyboardFocus (false)
|
||||
&& ! owner->isCurrentlyBlockedByAnotherModalComponent());
|
||||
}
|
||||
|
||||
} // namespace juce
|
81
deps/juce/modules/juce_gui_basics/keyboard/juce_CaretComponent.h
vendored
Normal file
81
deps/juce/modules/juce_gui_basics/keyboard/juce_CaretComponent.h
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API CaretComponent : public Component,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates the caret component.
|
||||
The keyFocusOwner is an optional component which the caret will check, making
|
||||
itself visible only when the keyFocusOwner has keyboard focus.
|
||||
*/
|
||||
CaretComponent (Component* keyFocusOwner);
|
||||
|
||||
/** Destructor. */
|
||||
~CaretComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the caret's position to place it next to the given character.
|
||||
The area is the rectangle containing the entire character that the caret is
|
||||
positioned on, so by default a vertical-line caret may choose to just show itself
|
||||
at the left of this area. You can override this method to customise its size.
|
||||
This method will also force the caret to reset its timer and become visible (if
|
||||
appropriate), so that as it moves, you can see where it is.
|
||||
*/
|
||||
virtual void setCaretPosition (const Rectangle<int>& characterArea);
|
||||
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the caret.
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
caretColourId = 0x1000204, /**< The colour with which to draw the caret. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
private:
|
||||
Component* owner;
|
||||
|
||||
bool shouldBeShown() const;
|
||||
void timerCallback() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (CaretComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
34
deps/juce/modules/juce_gui_basics/keyboard/juce_KeyListener.cpp
vendored
Normal file
34
deps/juce/modules/juce_gui_basics/keyboard/juce_KeyListener.cpp
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
bool KeyListener::keyStateChanged (const bool, Component*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
77
deps/juce/modules/juce_gui_basics/keyboard/juce_KeyListener.h
vendored
Normal file
77
deps/juce/modules/juce_gui_basics/keyboard/juce_KeyListener.h
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Receives callbacks when keys are pressed.
|
||||
|
||||
You can add a key listener to a component to be informed when that component
|
||||
gets key events. See the Component::addListener method for more details.
|
||||
|
||||
@see KeyPress, Component::addKeyListener, KeyPressMappingSet
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API KeyListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~KeyListener() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Called to indicate that a key has been pressed.
|
||||
|
||||
If your implementation returns true, then the key event is considered to have
|
||||
been consumed, and will not be passed on to any other components. If it returns
|
||||
false, then the key will be passed to other components that might want to use it.
|
||||
|
||||
@param key the keystroke, including modifier keys
|
||||
@param originatingComponent the component that received the key event
|
||||
@see keyStateChanged, Component::keyPressed
|
||||
*/
|
||||
virtual bool keyPressed (const KeyPress& key,
|
||||
Component* originatingComponent) = 0;
|
||||
|
||||
/** Called when any key is pressed or released.
|
||||
|
||||
When this is called, classes that might be interested in
|
||||
the state of one or more keys can use KeyPress::isKeyCurrentlyDown() to
|
||||
check whether their key has changed.
|
||||
|
||||
If your implementation returns true, then the key event is considered to have
|
||||
been consumed, and will not be passed on to any other components. If it returns
|
||||
false, then the key will be passed to other components that might want to use it.
|
||||
|
||||
@param originatingComponent the component that received the key event
|
||||
@param isKeyDown true if a key is being pressed, false if one is being released
|
||||
@see KeyPress, Component::keyStateChanged
|
||||
*/
|
||||
virtual bool keyStateChanged (bool isKeyDown, Component* originatingComponent);
|
||||
};
|
||||
|
||||
} // namespace juce
|
289
deps/juce/modules/juce_gui_basics/keyboard/juce_KeyPress.cpp
vendored
Normal file
289
deps/juce/modules/juce_gui_basics/keyboard/juce_KeyPress.cpp
vendored
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
KeyPress::KeyPress (int code, ModifierKeys m, juce_wchar textChar) noexcept
|
||||
: keyCode (code), mods (m), textCharacter (textChar)
|
||||
{
|
||||
}
|
||||
|
||||
KeyPress::KeyPress (const int code) noexcept : keyCode (code)
|
||||
{
|
||||
}
|
||||
|
||||
bool KeyPress::operator== (int otherKeyCode) const noexcept
|
||||
{
|
||||
return keyCode == otherKeyCode && ! mods.isAnyModifierKeyDown();
|
||||
}
|
||||
|
||||
bool KeyPress::operator== (const KeyPress& other) const noexcept
|
||||
{
|
||||
return mods.getRawFlags() == other.mods.getRawFlags()
|
||||
&& (textCharacter == other.textCharacter
|
||||
|| textCharacter == 0
|
||||
|| other.textCharacter == 0)
|
||||
&& (keyCode == other.keyCode
|
||||
|| (keyCode < 256
|
||||
&& other.keyCode < 256
|
||||
&& CharacterFunctions::toLowerCase ((juce_wchar) keyCode)
|
||||
== CharacterFunctions::toLowerCase ((juce_wchar) other.keyCode)));
|
||||
}
|
||||
|
||||
bool KeyPress::operator!= (const KeyPress& other) const noexcept { return ! operator== (other); }
|
||||
bool KeyPress::operator!= (int otherKeyCode) const noexcept { return ! operator== (otherKeyCode); }
|
||||
|
||||
bool KeyPress::isCurrentlyDown() const
|
||||
{
|
||||
return isKeyCurrentlyDown (keyCode)
|
||||
&& (ModifierKeys::currentModifiers.getRawFlags() & ModifierKeys::allKeyboardModifiers)
|
||||
== (mods.getRawFlags() & ModifierKeys::allKeyboardModifiers);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
namespace KeyPressHelpers
|
||||
{
|
||||
struct KeyNameAndCode
|
||||
{
|
||||
const char* name;
|
||||
int code;
|
||||
};
|
||||
|
||||
const KeyNameAndCode translations[] =
|
||||
{
|
||||
{ "spacebar", KeyPress::spaceKey },
|
||||
{ "return", KeyPress::returnKey },
|
||||
{ "escape", KeyPress::escapeKey },
|
||||
{ "backspace", KeyPress::backspaceKey },
|
||||
{ "cursor left", KeyPress::leftKey },
|
||||
{ "cursor right", KeyPress::rightKey },
|
||||
{ "cursor up", KeyPress::upKey },
|
||||
{ "cursor down", KeyPress::downKey },
|
||||
{ "page up", KeyPress::pageUpKey },
|
||||
{ "page down", KeyPress::pageDownKey },
|
||||
{ "home", KeyPress::homeKey },
|
||||
{ "end", KeyPress::endKey },
|
||||
{ "delete", KeyPress::deleteKey },
|
||||
{ "insert", KeyPress::insertKey },
|
||||
{ "tab", KeyPress::tabKey },
|
||||
{ "play", KeyPress::playKey },
|
||||
{ "stop", KeyPress::stopKey },
|
||||
{ "fast forward", KeyPress::fastForwardKey },
|
||||
{ "rewind", KeyPress::rewindKey }
|
||||
};
|
||||
|
||||
struct ModifierDescription
|
||||
{
|
||||
const char* name;
|
||||
int flag;
|
||||
};
|
||||
|
||||
static const ModifierDescription modifierNames[] =
|
||||
{
|
||||
{ "ctrl", ModifierKeys::ctrlModifier },
|
||||
{ "control", ModifierKeys::ctrlModifier },
|
||||
{ "ctl", ModifierKeys::ctrlModifier },
|
||||
{ "shift", ModifierKeys::shiftModifier },
|
||||
{ "shft", ModifierKeys::shiftModifier },
|
||||
{ "alt", ModifierKeys::altModifier },
|
||||
{ "option", ModifierKeys::altModifier },
|
||||
{ "command", ModifierKeys::commandModifier },
|
||||
{ "cmd", ModifierKeys::commandModifier }
|
||||
};
|
||||
|
||||
static const char* numberPadPrefix() noexcept { return "numpad "; }
|
||||
|
||||
static int getNumpadKeyCode (const String& desc)
|
||||
{
|
||||
if (desc.containsIgnoreCase (numberPadPrefix()))
|
||||
{
|
||||
auto lastChar = desc.trimEnd().getLastCharacter();
|
||||
|
||||
switch (lastChar)
|
||||
{
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
return (int) (KeyPress::numberPad0 + (int) lastChar - '0');
|
||||
|
||||
case '+': return KeyPress::numberPadAdd;
|
||||
case '-': return KeyPress::numberPadSubtract;
|
||||
case '*': return KeyPress::numberPadMultiply;
|
||||
case '/': return KeyPress::numberPadDivide;
|
||||
case '.': return KeyPress::numberPadDecimalPoint;
|
||||
case '=': return KeyPress::numberPadEquals;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (desc.endsWith ("separator")) return KeyPress::numberPadSeparator;
|
||||
if (desc.endsWith ("delete")) return KeyPress::numberPadDelete;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
struct OSXSymbolReplacement
|
||||
{
|
||||
const char* text;
|
||||
juce_wchar symbol;
|
||||
};
|
||||
|
||||
const OSXSymbolReplacement osxSymbols[] =
|
||||
{
|
||||
{ "shift + ", 0x21e7 },
|
||||
{ "command + ", 0x2318 },
|
||||
{ "option + ", 0x2325 },
|
||||
{ "ctrl + ", 0x2303 },
|
||||
{ "return", 0x21b5 },
|
||||
{ "cursor left", 0x2190 },
|
||||
{ "cursor right", 0x2192 },
|
||||
{ "cursor up", 0x2191 },
|
||||
{ "cursor down", 0x2193 },
|
||||
{ "backspace", 0x232b },
|
||||
{ "delete", 0x2326 },
|
||||
{ "spacebar", 0x2423 }
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
KeyPress KeyPress::createFromDescription (const String& desc)
|
||||
{
|
||||
int modifiers = 0;
|
||||
|
||||
for (int i = 0; i < numElementsInArray (KeyPressHelpers::modifierNames); ++i)
|
||||
if (desc.containsWholeWordIgnoreCase (KeyPressHelpers::modifierNames[i].name))
|
||||
modifiers |= KeyPressHelpers::modifierNames[i].flag;
|
||||
|
||||
int key = 0;
|
||||
|
||||
for (int i = 0; i < numElementsInArray (KeyPressHelpers::translations); ++i)
|
||||
{
|
||||
if (desc.containsWholeWordIgnoreCase (String (KeyPressHelpers::translations[i].name)))
|
||||
{
|
||||
key = KeyPressHelpers::translations[i].code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (key == 0)
|
||||
key = KeyPressHelpers::getNumpadKeyCode (desc);
|
||||
|
||||
if (key == 0)
|
||||
{
|
||||
// see if it's a function key..
|
||||
if (! desc.containsChar ('#')) // avoid mistaking hex-codes like "#f1"
|
||||
{
|
||||
for (int i = 1; i <= 35; ++i)
|
||||
{
|
||||
if (desc.containsWholeWordIgnoreCase ("f" + String (i)))
|
||||
{
|
||||
if (i <= 16) key = F1Key + i - 1;
|
||||
else if (i <= 24) key = F17Key + i - 17;
|
||||
else if (i <= 35) key = F25Key + i - 25;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (key == 0)
|
||||
{
|
||||
// give up and use the hex code..
|
||||
auto hexCode = desc.fromFirstOccurrenceOf ("#", false, false)
|
||||
.retainCharacters ("0123456789abcdefABCDEF")
|
||||
.getHexValue32();
|
||||
|
||||
if (hexCode > 0)
|
||||
key = hexCode;
|
||||
else
|
||||
key = (int) CharacterFunctions::toUpperCase (desc.getLastCharacter());
|
||||
}
|
||||
}
|
||||
|
||||
return KeyPress (key, ModifierKeys (modifiers), 0);
|
||||
}
|
||||
|
||||
String KeyPress::getTextDescription() const
|
||||
{
|
||||
String desc;
|
||||
|
||||
if (keyCode > 0)
|
||||
{
|
||||
// some keyboard layouts use a shift-key to get the slash, but in those cases, we
|
||||
// want to store it as being a slash, not shift+whatever.
|
||||
if (textCharacter == '/' && keyCode != numberPadDivide)
|
||||
return "/";
|
||||
|
||||
if (mods.isCtrlDown()) desc << "ctrl + ";
|
||||
if (mods.isShiftDown()) desc << "shift + ";
|
||||
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
if (mods.isAltDown()) desc << "option + ";
|
||||
if (mods.isCommandDown()) desc << "command + ";
|
||||
#else
|
||||
if (mods.isAltDown()) desc << "alt + ";
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < numElementsInArray (KeyPressHelpers::translations); ++i)
|
||||
if (keyCode == KeyPressHelpers::translations[i].code)
|
||||
return desc + KeyPressHelpers::translations[i].name;
|
||||
|
||||
// not all F keys have consecutive key codes on all platforms
|
||||
if (keyCode >= F1Key && keyCode <= F16Key) desc << 'F' << (1 + keyCode - F1Key);
|
||||
else if (keyCode >= F17Key && keyCode <= F24Key) desc << 'F' << (17 + keyCode - F17Key);
|
||||
else if (keyCode >= F25Key && keyCode <= F35Key) desc << 'F' << (25 + keyCode - F25Key);
|
||||
else if (keyCode >= numberPad0 && keyCode <= numberPad9) desc << KeyPressHelpers::numberPadPrefix() << (keyCode - numberPad0);
|
||||
else if (keyCode >= 33 && keyCode < 176) desc += CharacterFunctions::toUpperCase ((juce_wchar) keyCode);
|
||||
else if (keyCode == numberPadAdd) desc << KeyPressHelpers::numberPadPrefix() << '+';
|
||||
else if (keyCode == numberPadSubtract) desc << KeyPressHelpers::numberPadPrefix() << '-';
|
||||
else if (keyCode == numberPadMultiply) desc << KeyPressHelpers::numberPadPrefix() << '*';
|
||||
else if (keyCode == numberPadDivide) desc << KeyPressHelpers::numberPadPrefix() << '/';
|
||||
else if (keyCode == numberPadSeparator) desc << KeyPressHelpers::numberPadPrefix() << "separator";
|
||||
else if (keyCode == numberPadDecimalPoint) desc << KeyPressHelpers::numberPadPrefix() << '.';
|
||||
else if (keyCode == numberPadEquals) desc << KeyPressHelpers::numberPadPrefix() << '=';
|
||||
else if (keyCode == numberPadDelete) desc << KeyPressHelpers::numberPadPrefix() << "delete";
|
||||
else desc << '#' << String::toHexString (keyCode);
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
String KeyPress::getTextDescriptionWithIcons() const
|
||||
{
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
auto s = getTextDescription();
|
||||
|
||||
for (int i = 0; i < numElementsInArray (KeyPressHelpers::osxSymbols); ++i)
|
||||
s = s.replace (KeyPressHelpers::osxSymbols[i].text,
|
||||
String::charToString (KeyPressHelpers::osxSymbols[i].symbol));
|
||||
|
||||
return s;
|
||||
#else
|
||||
return getTextDescription();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace juce
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user