migrating to the latest JUCE version

This commit is contained in:
2022-11-04 23:11:33 +01:00
committed by Nikolai Rodionov
parent 4257a0f8ba
commit faf8f18333
2796 changed files with 888518 additions and 784244 deletions

View File

@ -1,54 +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
{
AnimatedAppComponent::AnimatedAppComponent()
: lastUpdateTime (Time::getCurrentTime()), totalUpdates (0)
{
setOpaque (true);
}
void AnimatedAppComponent::setFramesPerSecond (int framesPerSecond)
{
jassert (framesPerSecond > 0 && framesPerSecond < 1000);
startTimerHz (framesPerSecond);
}
int AnimatedAppComponent::getMillisecondsSinceLastUpdate() const noexcept
{
return (int) (Time::getCurrentTime() - lastUpdateTime).inMilliseconds();
}
void AnimatedAppComponent::timerCallback()
{
++totalUpdates;
update();
repaint();
lastUpdateTime = Time::getCurrentTime();
}
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
AnimatedAppComponent::AnimatedAppComponent()
: lastUpdateTime (Time::getCurrentTime()), totalUpdates (0)
{
setOpaque (true);
}
void AnimatedAppComponent::setFramesPerSecond (int framesPerSecond)
{
jassert (framesPerSecond > 0 && framesPerSecond < 1000);
startTimerHz (framesPerSecond);
}
int AnimatedAppComponent::getMillisecondsSinceLastUpdate() const noexcept
{
return (int) (Time::getCurrentTime() - lastUpdateTime).inMilliseconds();
}
void AnimatedAppComponent::timerCallback()
{
++totalUpdates;
update();
repaint();
lastUpdateTime = Time::getCurrentTime();
}
} // namespace juce

View File

@ -1,77 +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 base class for writing simple one-page graphical apps.
A subclass can inherit from this and implement just a few methods such as
paint() and mouse-handling. The base class provides some simple abstractions
to take care of continuously repainting itself.
@tags{GUI}
*/
class AnimatedAppComponent : public Component,
private Timer
{
public:
AnimatedAppComponent();
/** Your subclass can call this to start a timer running which will
call update() and repaint the component at the given frequency.
*/
void setFramesPerSecond (int framesPerSecond);
/** Called periodically, at the frequency specified by setFramesPerSecond().
This is a the best place to do things like advancing animation parameters,
checking the mouse position, etc.
*/
virtual void update() = 0;
/** Returns the number of times that update() has been called since the component
started running.
*/
int getFrameCounter() const noexcept { return totalUpdates; }
/** When called from update(), this returns the number of milliseconds since the
last update call.
This might be useful for accurately timing animations, etc.
*/
int getMillisecondsSinceLastUpdate() const noexcept;
private:
//==============================================================================
Time lastUpdateTime;
int totalUpdates;
void timerCallback() override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimatedAppComponent)
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A base class for writing simple one-page graphical apps.
A subclass can inherit from this and implement just a few methods such as
paint() and mouse-handling. The base class provides some simple abstractions
to take care of continuously repainting itself.
@tags{GUI}
*/
class AnimatedAppComponent : public Component,
private Timer
{
public:
AnimatedAppComponent();
/** Your subclass can call this to start a timer running which will
call update() and repaint the component at the given frequency.
*/
void setFramesPerSecond (int framesPerSecond);
/** Called periodically, at the frequency specified by setFramesPerSecond().
This is a the best place to do things like advancing animation parameters,
checking the mouse position, etc.
*/
virtual void update() = 0;
/** Returns the number of times that update() has been called since the component
started running.
*/
int getFrameCounter() const noexcept { return totalUpdates; }
/** When called from update(), this returns the number of milliseconds since the
last update call.
This might be useful for accurately timing animations, etc.
*/
int getMillisecondsSinceLastUpdate() const noexcept;
private:
//==============================================================================
Time lastUpdateTime;
int totalUpdates;
void timerCallback() override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimatedAppComponent)
};
} // namespace juce

View File

@ -1,117 +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
{
//==============================================================================
#if JUCE_MAC || DOXYGEN
/**
Receives events from an Apple IR remote control device (Only available in OSX!).
To use it, just create a subclass of this class, implementing the buttonPressed()
callback, then call start() and stop() to start or stop receiving events.
@tags{GUI}
*/
class JUCE_API AppleRemoteDevice
{
public:
//==============================================================================
AppleRemoteDevice();
virtual ~AppleRemoteDevice();
//==============================================================================
/** The set of buttons that may be pressed.
@see buttonPressed
*/
enum ButtonType
{
menuButton = 0, /**< The menu button (if it's held for a short time). */
playButton, /**< The play button. */
plusButton, /**< The plus or volume-up button. */
minusButton, /**< The minus or volume-down button. */
rightButton, /**< The right button (if it's held for a short time). */
leftButton, /**< The left button (if it's held for a short time). */
rightButton_Long, /**< The right button (if it's held for a long time). */
leftButton_Long, /**< The menu button (if it's held for a long time). */
menuButton_Long, /**< The menu button (if it's held for a long time). */
playButtonSleepMode,
switched
};
//==============================================================================
/** Override this method to receive the callback about a button press.
The callback will happen on the application's message thread.
Some buttons trigger matching up and down events, in which the isDown parameter
will be true and then false. Others only send a single event when the
button is pressed.
*/
virtual void buttonPressed (ButtonType buttonId, bool isDown) = 0;
//==============================================================================
/** Starts the device running and responding to events.
Returns true if it managed to open the device.
@param inExclusiveMode if true, the remote will be grabbed exclusively for this app,
and will not be available to any other part of the system. If
false, it will be shared with other apps.
@see stop
*/
bool start (bool inExclusiveMode);
/** Stops the device running.
@see start
*/
void stop();
/** Returns true if the device has been started successfully.
*/
bool isActive() const;
/** Returns the ID number of the remote, if it has sent one.
*/
int getRemoteId() const { return remoteId; }
//==============================================================================
/** @internal */
void handleCallbackInternal();
private:
void* device;
void* queue;
int remoteId;
bool open (bool openInExclusiveMode);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AppleRemoteDevice)
};
#endif
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
#if JUCE_MAC || DOXYGEN
/**
Receives events from an Apple IR remote control device (Only available in OSX!).
To use it, just create a subclass of this class, implementing the buttonPressed()
callback, then call start() and stop() to start or stop receiving events.
@tags{GUI}
*/
class JUCE_API AppleRemoteDevice
{
public:
//==============================================================================
AppleRemoteDevice();
virtual ~AppleRemoteDevice();
//==============================================================================
/** The set of buttons that may be pressed.
@see buttonPressed
*/
enum ButtonType
{
menuButton = 0, /**< The menu button (if it's held for a short time). */
playButton, /**< The play button. */
plusButton, /**< The plus or volume-up button. */
minusButton, /**< The minus or volume-down button. */
rightButton, /**< The right button (if it's held for a short time). */
leftButton, /**< The left button (if it's held for a short time). */
rightButton_Long, /**< The right button (if it's held for a long time). */
leftButton_Long, /**< The menu button (if it's held for a long time). */
menuButton_Long, /**< The menu button (if it's held for a long time). */
playButtonSleepMode,
switched
};
//==============================================================================
/** Override this method to receive the callback about a button press.
The callback will happen on the application's message thread.
Some buttons trigger matching up and down events, in which the isDown parameter
will be true and then false. Others only send a single event when the
button is pressed.
*/
virtual void buttonPressed (ButtonType buttonId, bool isDown) = 0;
//==============================================================================
/** Starts the device running and responding to events.
Returns true if it managed to open the device.
@param inExclusiveMode if true, the remote will be grabbed exclusively for this app,
and will not be available to any other part of the system. If
false, it will be shared with other apps.
@see stop
*/
bool start (bool inExclusiveMode);
/** Stops the device running.
@see start
*/
void stop();
/** Returns true if the device has been started successfully.
*/
bool isActive() const;
/** Returns the ID number of the remote, if it has sent one.
*/
int getRemoteId() const { return remoteId; }
//==============================================================================
/** @internal */
void handleCallbackInternal();
private:
void* device;
void* queue;
int remoteId;
bool open (bool openInExclusiveMode);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AppleRemoteDevice)
};
#endif
} // namespace juce

View File

@ -1,124 +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
{
BubbleMessageComponent::BubbleMessageComponent (int fadeOutLengthMs)
: fadeOutLength (fadeOutLengthMs), mouseClickCounter (0),
expiryTime (0), deleteAfterUse (false)
{
}
BubbleMessageComponent::~BubbleMessageComponent()
{
}
void BubbleMessageComponent::showAt (const Rectangle<int>& pos,
const AttributedString& text,
const int numMillisecondsBeforeRemoving,
const bool removeWhenMouseClicked,
const bool deleteSelfAfterUse)
{
createLayout (text);
setPosition (pos);
init (numMillisecondsBeforeRemoving, removeWhenMouseClicked, deleteSelfAfterUse);
}
void BubbleMessageComponent::showAt (Component* const component,
const AttributedString& text,
const int numMillisecondsBeforeRemoving,
const bool removeWhenMouseClicked,
const bool deleteSelfAfterUse)
{
createLayout (text);
setPosition (component);
init (numMillisecondsBeforeRemoving, removeWhenMouseClicked, deleteSelfAfterUse);
}
void BubbleMessageComponent::createLayout (const AttributedString& text)
{
textLayout.createLayoutWithBalancedLineLengths (text, 256);
}
void BubbleMessageComponent::init (const int numMillisecondsBeforeRemoving,
const bool removeWhenMouseClicked,
const bool deleteSelfAfterUse)
{
setAlpha (1.0f);
setVisible (true);
deleteAfterUse = deleteSelfAfterUse;
expiryTime = numMillisecondsBeforeRemoving > 0
? (Time::getMillisecondCounter() + (uint32) numMillisecondsBeforeRemoving) : 0;
mouseClickCounter = Desktop::getInstance().getMouseButtonClickCounter();
if (! (removeWhenMouseClicked && isShowing()))
mouseClickCounter += 0xfffff;
startTimer (77);
repaint();
}
const float bubblePaddingX = 20.0f;
const float bubblePaddingY = 14.0f;
void BubbleMessageComponent::getContentSize (int& w, int& h)
{
w = (int) (bubblePaddingX + textLayout.getWidth());
h = (int) (bubblePaddingY + textLayout.getHeight());
}
void BubbleMessageComponent::paintContent (Graphics& g, int w, int h)
{
g.setColour (findColour (TooltipWindow::textColourId));
textLayout.draw (g, Rectangle<float> (bubblePaddingX / 2.0f, bubblePaddingY / 2.0f,
(float) w - bubblePaddingX, (float) h - bubblePaddingY));
}
void BubbleMessageComponent::timerCallback()
{
if (Desktop::getInstance().getMouseButtonClickCounter() > mouseClickCounter)
hide (false);
else if (expiryTime != 0 && Time::getMillisecondCounter() > expiryTime)
hide (true);
}
void BubbleMessageComponent::hide (const bool fadeOut)
{
stopTimer();
if (fadeOut)
Desktop::getInstance().getAnimator().fadeOut (this, fadeOutLength);
else
setVisible (false);
if (deleteAfterUse)
delete this;
}
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
BubbleMessageComponent::BubbleMessageComponent (int fadeOutLengthMs)
: fadeOutLength (fadeOutLengthMs), mouseClickCounter (0),
expiryTime (0), deleteAfterUse (false)
{
}
BubbleMessageComponent::~BubbleMessageComponent()
{
}
void BubbleMessageComponent::showAt (const Rectangle<int>& pos,
const AttributedString& text,
const int numMillisecondsBeforeRemoving,
const bool removeWhenMouseClicked,
const bool deleteSelfAfterUse)
{
createLayout (text);
setPosition (pos);
init (numMillisecondsBeforeRemoving, removeWhenMouseClicked, deleteSelfAfterUse);
}
void BubbleMessageComponent::showAt (Component* const component,
const AttributedString& text,
const int numMillisecondsBeforeRemoving,
const bool removeWhenMouseClicked,
const bool deleteSelfAfterUse)
{
createLayout (text);
setPosition (component);
init (numMillisecondsBeforeRemoving, removeWhenMouseClicked, deleteSelfAfterUse);
}
void BubbleMessageComponent::createLayout (const AttributedString& text)
{
textLayout.createLayoutWithBalancedLineLengths (text, 256);
}
void BubbleMessageComponent::init (const int numMillisecondsBeforeRemoving,
const bool removeWhenMouseClicked,
const bool deleteSelfAfterUse)
{
setAlpha (1.0f);
setVisible (true);
deleteAfterUse = deleteSelfAfterUse;
expiryTime = numMillisecondsBeforeRemoving > 0
? (Time::getMillisecondCounter() + (uint32) numMillisecondsBeforeRemoving) : 0;
mouseClickCounter = Desktop::getInstance().getMouseButtonClickCounter();
if (! (removeWhenMouseClicked && isShowing()))
mouseClickCounter += 0xfffff;
startTimer (77);
repaint();
}
const float bubblePaddingX = 20.0f;
const float bubblePaddingY = 14.0f;
void BubbleMessageComponent::getContentSize (int& w, int& h)
{
w = (int) (bubblePaddingX + textLayout.getWidth());
h = (int) (bubblePaddingY + textLayout.getHeight());
}
void BubbleMessageComponent::paintContent (Graphics& g, int w, int h)
{
g.setColour (findColour (TooltipWindow::textColourId));
textLayout.draw (g, Rectangle<float> (bubblePaddingX / 2.0f, bubblePaddingY / 2.0f,
(float) w - bubblePaddingX, (float) h - bubblePaddingY));
}
void BubbleMessageComponent::timerCallback()
{
if (Desktop::getInstance().getMouseButtonClickCounter() > mouseClickCounter)
hide (false);
else if (expiryTime != 0 && Time::getMillisecondCounter() > expiryTime)
hide (true);
}
void BubbleMessageComponent::hide (const bool fadeOut)
{
stopTimer();
if (fadeOut)
Desktop::getInstance().getAnimator().fadeOut (this, fadeOutLength);
else
setVisible (false);
if (deleteAfterUse)
delete this;
}
} // namespace juce

View File

@ -1,125 +1,125 @@
/*
==============================================================================
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 speech-bubble component that displays a short message.
This can be used to show a message with the tail of the speech bubble
pointing to a particular component or location on the screen.
@see BubbleComponent
@tags{GUI}
*/
class JUCE_API BubbleMessageComponent : public BubbleComponent,
private Timer
{
public:
//==============================================================================
/** Creates a bubble component.
After creating one a BubbleComponent, do the following:
- add it to an appropriate parent component, or put it on the
desktop with Component::addToDesktop (0).
- use the showAt() method to show a message.
- it will make itself invisible after it times-out (and can optionally
also delete itself), or you can reuse it somewhere else by calling
showAt() again.
*/
BubbleMessageComponent (int fadeOutLengthMs = 150);
/** Destructor. */
~BubbleMessageComponent() override;
//==============================================================================
/** Shows a message bubble at a particular position.
This shows the bubble with its stem pointing to the given location
(coordinates being relative to its parent component).
@param position the coords of the object to point to
@param message the text to display
@param numMillisecondsBeforeRemoving how long to leave it on the screen before removing itself
from its parent component. If this is 0 or less, it
will stay there until manually removed.
@param removeWhenMouseClicked if this is true, the bubble will disappear as soon as a
mouse button is pressed (anywhere on the screen)
@param deleteSelfAfterUse if true, then the component will delete itself after
it becomes invisible
*/
void showAt (const Rectangle<int>& position,
const AttributedString& message,
int numMillisecondsBeforeRemoving,
bool removeWhenMouseClicked = true,
bool deleteSelfAfterUse = false);
/** Shows a message bubble next to a particular component.
This shows the bubble with its stem pointing at the given component.
@param component the component that you want to point at
@param message the text to display
@param numMillisecondsBeforeRemoving how long to leave it on the screen before removing itself
from its parent component. If this is 0 or less, it
will stay there until manually removed.
@param removeWhenMouseClicked if this is true, the bubble will disappear as soon as a
mouse button is pressed (anywhere on the screen)
@param deleteSelfAfterUse if true, then the component will delete itself after
it becomes invisible
*/
void showAt (Component* component,
const AttributedString& message,
int numMillisecondsBeforeRemoving,
bool removeWhenMouseClicked = true,
bool deleteSelfAfterUse = false);
//==============================================================================
/** @internal */
void getContentSize (int& w, int& h) override;
/** @internal */
void paintContent (Graphics& g, int w, int h) override;
/** @internal */
void timerCallback() override;
private:
//==============================================================================
int fadeOutLength, mouseClickCounter;
TextLayout textLayout;
int64 expiryTime;
bool deleteAfterUse;
void createLayout (const AttributedString&);
void init (int, bool, bool);
void hide (bool fadeOut);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BubbleMessageComponent)
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A speech-bubble component that displays a short message.
This can be used to show a message with the tail of the speech bubble
pointing to a particular component or location on the screen.
@see BubbleComponent
@tags{GUI}
*/
class JUCE_API BubbleMessageComponent : public BubbleComponent,
private Timer
{
public:
//==============================================================================
/** Creates a bubble component.
After creating one a BubbleComponent, do the following:
- add it to an appropriate parent component, or put it on the
desktop with Component::addToDesktop (0).
- use the showAt() method to show a message.
- it will make itself invisible after it times-out (and can optionally
also delete itself), or you can reuse it somewhere else by calling
showAt() again.
*/
BubbleMessageComponent (int fadeOutLengthMs = 150);
/** Destructor. */
~BubbleMessageComponent() override;
//==============================================================================
/** Shows a message bubble at a particular position.
This shows the bubble with its stem pointing to the given location
(coordinates being relative to its parent component).
@param position the coords of the object to point to
@param message the text to display
@param numMillisecondsBeforeRemoving how long to leave it on the screen before removing itself
from its parent component. If this is 0 or less, it
will stay there until manually removed.
@param removeWhenMouseClicked if this is true, the bubble will disappear as soon as a
mouse button is pressed (anywhere on the screen)
@param deleteSelfAfterUse if true, then the component will delete itself after
it becomes invisible
*/
void showAt (const Rectangle<int>& position,
const AttributedString& message,
int numMillisecondsBeforeRemoving,
bool removeWhenMouseClicked = true,
bool deleteSelfAfterUse = false);
/** Shows a message bubble next to a particular component.
This shows the bubble with its stem pointing at the given component.
@param component the component that you want to point at
@param message the text to display
@param numMillisecondsBeforeRemoving how long to leave it on the screen before removing itself
from its parent component. If this is 0 or less, it
will stay there until manually removed.
@param removeWhenMouseClicked if this is true, the bubble will disappear as soon as a
mouse button is pressed (anywhere on the screen)
@param deleteSelfAfterUse if true, then the component will delete itself after
it becomes invisible
*/
void showAt (Component* component,
const AttributedString& message,
int numMillisecondsBeforeRemoving,
bool removeWhenMouseClicked = true,
bool deleteSelfAfterUse = false);
//==============================================================================
/** @internal */
void getContentSize (int& w, int& h) override;
/** @internal */
void paintContent (Graphics& g, int w, int h) override;
/** @internal */
void timerCallback() override;
private:
//==============================================================================
int fadeOutLength, mouseClickCounter;
TextLayout textLayout;
int64 expiryTime;
bool deleteAfterUse;
void createLayout (const AttributedString&);
void init (int, bool, bool);
void hide (bool fadeOut);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BubbleMessageComponent)
};
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -1,167 +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 component that lets the user choose a colour.
This shows RGB sliders and a colourspace that the user can pick colours from.
This class is also a ChangeBroadcaster, so listeners can register to be told
when the colour changes.
@tags{GUI}
*/
class JUCE_API ColourSelector : public Component,
public ChangeBroadcaster
{
public:
//==============================================================================
/** Options for the type of selector to show. These are passed into the constructor. */
enum ColourSelectorOptions
{
showAlphaChannel = 1 << 0, /**< if set, the colour's alpha channel can be changed as well as its RGB. */
showColourAtTop = 1 << 1, /**< if set, a swatch of the colour is shown at the top of the component. */
editableColour = 1 << 2, /**< if set, the colour shows at the top of the component is editable. */
showSliders = 1 << 3, /**< if set, RGB sliders are shown at the bottom of the component. */
showColourspace = 1 << 4 /**< if set, a big HSV selector is shown. */
};
//==============================================================================
/** Creates a ColourSelector object.
The flags are a combination of values from the ColourSelectorOptions enum, specifying
which of the selector's features should be visible.
The edgeGap value specifies the amount of space to leave around the edge.
gapAroundColourSpaceComponent indicates how much of a gap to put around the
colourspace and hue selector components.
*/
ColourSelector (int flags = (showAlphaChannel | showColourAtTop | showSliders | showColourspace),
int edgeGap = 4,
int gapAroundColourSpaceComponent = 7);
/** Destructor. */
~ColourSelector() override;
//==============================================================================
/** Returns the colour that the user has currently selected.
The ColourSelector class is also a ChangeBroadcaster, so listeners can
register to be told when the colour changes.
@see setCurrentColour
*/
Colour getCurrentColour() const;
/** Changes the colour that is currently being shown.
@param newColour the new colour to show
@param notificationType whether to send a notification of the change to listeners.
A notification will only be sent if the colour has changed.
*/
void setCurrentColour (Colour newColour, NotificationType notificationType = sendNotification);
//==============================================================================
/** Tells the selector how many preset colour swatches you want to have on the component.
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
setSwatchColour(), to return the number of colours you want, and to set and retrieve
their values.
*/
virtual int getNumSwatches() const;
/** Called by the selector to find out the colour of one of the swatches.
Your subclass should return the colour of the swatch with the given index.
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
setSwatchColour(), to return the number of colours you want, and to set and retrieve
their values.
*/
virtual Colour getSwatchColour (int index) const;
/** Called by the selector when the user puts a new colour into one of the swatches.
Your subclass should change the colour of the swatch with the given index.
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
setSwatchColour(), to return the number of colours you want, and to set and retrieve
their values.
*/
virtual void setSwatchColour (int index, const Colour& newColour);
//==============================================================================
/** A set of colour IDs to use to change the colour of various aspects of the keyboard.
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 = 0x1007000, /**< the colour used to fill the component's background. */
labelTextColourId = 0x1007001 /**< the colour used for the labels next to the sliders. */
};
//==============================================================================
// These need to be public otherwise the Projucer's live-build engine will complain
class ColourSpaceView;
class HueSelectorComp;
class ColourPreviewComp;
private:
//==============================================================================
class SwatchComponent;
Colour colour;
float h, s, v;
std::unique_ptr<Slider> sliders[4];
std::unique_ptr<ColourSpaceView> colourSpace;
std::unique_ptr<HueSelectorComp> hueSelector;
std::unique_ptr<ColourPreviewComp> previewComponent;
OwnedArray<SwatchComponent> swatchComponents;
const int flags;
int edgeGap;
void setHue (float newH);
void setSV (float newS, float newV);
void updateHSV();
void update (NotificationType);
void changeColour();
void paint (Graphics&) override;
void resized() override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourSelector)
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A component that lets the user choose a colour.
This shows RGB sliders and a colourspace that the user can pick colours from.
This class is also a ChangeBroadcaster, so listeners can register to be told
when the colour changes.
@tags{GUI}
*/
class JUCE_API ColourSelector : public Component,
public ChangeBroadcaster
{
public:
//==============================================================================
/** Options for the type of selector to show. These are passed into the constructor. */
enum ColourSelectorOptions
{
showAlphaChannel = 1 << 0, /**< if set, the colour's alpha channel can be changed as well as its RGB. */
showColourAtTop = 1 << 1, /**< if set, a swatch of the colour is shown at the top of the component. */
editableColour = 1 << 2, /**< if set, the colour shows at the top of the component is editable. */
showSliders = 1 << 3, /**< if set, RGB sliders are shown at the bottom of the component. */
showColourspace = 1 << 4 /**< if set, a big HSV selector is shown. */
};
//==============================================================================
/** Creates a ColourSelector object.
The flags are a combination of values from the ColourSelectorOptions enum, specifying
which of the selector's features should be visible.
The edgeGap value specifies the amount of space to leave around the edge.
gapAroundColourSpaceComponent indicates how much of a gap to put around the
colourspace and hue selector components.
*/
ColourSelector (int flags = (showAlphaChannel | showColourAtTop | showSliders | showColourspace),
int edgeGap = 4,
int gapAroundColourSpaceComponent = 7);
/** Destructor. */
~ColourSelector() override;
//==============================================================================
/** Returns the colour that the user has currently selected.
The ColourSelector class is also a ChangeBroadcaster, so listeners can
register to be told when the colour changes.
@see setCurrentColour
*/
Colour getCurrentColour() const;
/** Changes the colour that is currently being shown.
@param newColour the new colour to show
@param notificationType whether to send a notification of the change to listeners.
A notification will only be sent if the colour has changed.
*/
void setCurrentColour (Colour newColour, NotificationType notificationType = sendNotification);
//==============================================================================
/** Tells the selector how many preset colour swatches you want to have on the component.
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
setSwatchColour(), to return the number of colours you want, and to set and retrieve
their values.
*/
virtual int getNumSwatches() const;
/** Called by the selector to find out the colour of one of the swatches.
Your subclass should return the colour of the swatch with the given index.
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
setSwatchColour(), to return the number of colours you want, and to set and retrieve
their values.
*/
virtual Colour getSwatchColour (int index) const;
/** Called by the selector when the user puts a new colour into one of the swatches.
Your subclass should change the colour of the swatch with the given index.
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
setSwatchColour(), to return the number of colours you want, and to set and retrieve
their values.
*/
virtual void setSwatchColour (int index, const Colour& newColour);
//==============================================================================
/** A set of colour IDs to use to change the colour of various aspects of the keyboard.
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 = 0x1007000, /**< the colour used to fill the component's background. */
labelTextColourId = 0x1007001 /**< the colour used for the labels next to the sliders. */
};
//==============================================================================
// These need to be public otherwise the Projucer's live-build engine will complain
class ColourSpaceView;
class HueSelectorComp;
class ColourPreviewComp;
private:
//==============================================================================
class SwatchComponent;
Colour colour;
float h, s, v;
std::unique_ptr<Slider> sliders[4];
std::unique_ptr<ColourSpaceView> colourSpace;
std::unique_ptr<HueSelectorComp> hueSelector;
std::unique_ptr<ColourPreviewComp> previewComponent;
OwnedArray<SwatchComponent> swatchComponents;
const int flags;
int edgeGap;
void setHue (float newH);
void setSV (float newS, float newV);
void updateHSV();
void update (NotificationType);
void changeColour();
void paint (Graphics&) override;
void resized() override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourSelector)
};
} // namespace juce

View File

@ -1,480 +1,480 @@
/*
==============================================================================
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 KeyMappingEditorComponent::ChangeKeyButton : public Button
{
public:
ChangeKeyButton (KeyMappingEditorComponent& kec, CommandID command,
const String& keyName, int keyIndex)
: Button (keyName),
owner (kec),
commandID (command),
keyNum (keyIndex)
{
setWantsKeyboardFocus (false);
setTriggeredOnMouseDown (keyNum >= 0);
setTooltip (keyIndex < 0 ? TRANS("Adds a new key-mapping")
: TRANS("Click to change this key-mapping"));
}
void paintButton (Graphics& g, bool /*isOver*/, bool /*isDown*/) override
{
getLookAndFeel().drawKeymapChangeButton (g, getWidth(), getHeight(), *this,
keyNum >= 0 ? getName() : String());
}
void clicked() override
{
if (keyNum >= 0)
{
Component::SafePointer<ChangeKeyButton> button (this);
PopupMenu m;
m.addItem (TRANS("Change this key-mapping"),
[button]
{
if (button != nullptr)
button.getComponent()->assignNewKey();
});
m.addSeparator();
m.addItem (TRANS("Remove this key-mapping"),
[button]
{
if (button != nullptr)
button->owner.getMappings().removeKeyPress (button->commandID,
button->keyNum);
});
m.showMenuAsync (PopupMenu::Options().withTargetComponent (this));
}
else
{
assignNewKey(); // + button pressed..
}
}
using Button::clicked;
void fitToContent (const int h) noexcept
{
if (keyNum < 0)
setSize (h, h);
else
setSize (jlimit (h * 4, h * 8, 6 + Font ((float) h * 0.6f).getStringWidth (getName())), h);
}
//==============================================================================
class KeyEntryWindow : public AlertWindow
{
public:
KeyEntryWindow (KeyMappingEditorComponent& kec)
: AlertWindow (TRANS("New key-mapping"),
TRANS("Please press a key combination now..."),
MessageBoxIconType::NoIcon),
owner (kec)
{
addButton (TRANS("OK"), 1);
addButton (TRANS("Cancel"), 0);
// (avoid return + escape keys getting processed by the buttons..)
for (auto* child : getChildren())
child->setWantsKeyboardFocus (false);
setWantsKeyboardFocus (true);
grabKeyboardFocus();
}
bool keyPressed (const KeyPress& key) override
{
lastPress = key;
String message (TRANS("Key") + ": " + owner.getDescriptionForKeyPress (key));
auto previousCommand = owner.getMappings().findCommandForKeyPress (key);
if (previousCommand != 0)
message << "\n\n("
<< TRANS("Currently assigned to \"CMDN\"")
.replace ("CMDN", TRANS (owner.getCommandManager().getNameOfCommand (previousCommand)))
<< ')';
setMessage (message);
return true;
}
bool keyStateChanged (bool) override
{
return true;
}
KeyPress lastPress;
private:
KeyMappingEditorComponent& owner;
JUCE_DECLARE_NON_COPYABLE (KeyEntryWindow)
};
static void assignNewKeyCallback (int result, ChangeKeyButton* button, KeyPress newKey)
{
if (result != 0 && button != nullptr)
button->setNewKey (newKey, true);
}
void setNewKey (const KeyPress& newKey, bool dontAskUser)
{
if (newKey.isValid())
{
auto previousCommand = owner.getMappings().findCommandForKeyPress (newKey);
if (previousCommand == 0 || dontAskUser)
{
owner.getMappings().removeKeyPress (newKey);
if (keyNum >= 0)
owner.getMappings().removeKeyPress (commandID, keyNum);
owner.getMappings().addKeyPress (commandID, newKey, keyNum);
}
else
{
AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
TRANS("Change key-mapping"),
TRANS("This key is already assigned to the command \"CMDN\"")
.replace ("CMDN", owner.getCommandManager().getNameOfCommand (previousCommand))
+ "\n\n"
+ TRANS("Do you want to re-assign it to this new command instead?"),
TRANS("Re-assign"),
TRANS("Cancel"),
this,
ModalCallbackFunction::forComponent (assignNewKeyCallback,
this, KeyPress (newKey)));
}
}
}
static void keyChosen (int result, ChangeKeyButton* button)
{
if (button != nullptr && button->currentKeyEntryWindow != nullptr)
{
if (result != 0)
{
button->currentKeyEntryWindow->setVisible (false);
button->setNewKey (button->currentKeyEntryWindow->lastPress, false);
}
button->currentKeyEntryWindow.reset();
}
}
void assignNewKey()
{
currentKeyEntryWindow.reset (new KeyEntryWindow (owner));
currentKeyEntryWindow->enterModalState (true, ModalCallbackFunction::forComponent (keyChosen, this));
}
private:
KeyMappingEditorComponent& owner;
const CommandID commandID;
const int keyNum;
std::unique_ptr<KeyEntryWindow> currentKeyEntryWindow;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeKeyButton)
};
//==============================================================================
class KeyMappingEditorComponent::ItemComponent : public Component
{
public:
ItemComponent (KeyMappingEditorComponent& kec, CommandID command)
: owner (kec), commandID (command)
{
setInterceptsMouseClicks (false, true);
const bool isReadOnly = owner.isCommandReadOnly (commandID);
auto keyPresses = owner.getMappings().getKeyPressesAssignedToCommand (commandID);
for (int i = 0; i < jmin ((int) maxNumAssignments, keyPresses.size()); ++i)
addKeyPressButton (owner.getDescriptionForKeyPress (keyPresses.getReference (i)), i, isReadOnly);
addKeyPressButton ("Change Key Mapping", -1, isReadOnly);
}
void addKeyPressButton (const String& desc, const int index, const bool isReadOnly)
{
auto* b = new ChangeKeyButton (owner, commandID, desc, index);
keyChangeButtons.add (b);
b->setEnabled (! isReadOnly);
b->setVisible (keyChangeButtons.size() <= (int) maxNumAssignments);
addChildComponent (b);
}
void paint (Graphics& g) override
{
g.setFont ((float) getHeight() * 0.7f);
g.setColour (owner.findColour (KeyMappingEditorComponent::textColourId));
g.drawFittedText (TRANS (owner.getCommandManager().getNameOfCommand (commandID)),
4, 0, jmax (40, getChildComponent (0)->getX() - 5), getHeight(),
Justification::centredLeft, true);
}
void resized() override
{
int x = getWidth() - 4;
for (int i = keyChangeButtons.size(); --i >= 0;)
{
auto* b = keyChangeButtons.getUnchecked(i);
b->fitToContent (getHeight() - 2);
b->setTopRightPosition (x, 1);
x = b->getX() - 5;
}
}
private:
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
{
return createIgnoredAccessibilityHandler (*this);
}
KeyMappingEditorComponent& owner;
OwnedArray<ChangeKeyButton> keyChangeButtons;
const CommandID commandID;
enum { maxNumAssignments = 3 };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemComponent)
};
//==============================================================================
class KeyMappingEditorComponent::MappingItem : public TreeViewItem
{
public:
MappingItem (KeyMappingEditorComponent& kec, CommandID command)
: owner (kec), commandID (command)
{}
String getUniqueName() const override { return String ((int) commandID) + "_id"; }
bool mightContainSubItems() override { return false; }
int getItemHeight() const override { return 20; }
std::unique_ptr<Component> createItemComponent() override { return std::make_unique<ItemComponent> (owner, commandID); }
String getAccessibilityName() override { return TRANS (owner.getCommandManager().getNameOfCommand (commandID)); }
private:
KeyMappingEditorComponent& owner;
const CommandID commandID;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MappingItem)
};
//==============================================================================
class KeyMappingEditorComponent::CategoryItem : public TreeViewItem
{
public:
CategoryItem (KeyMappingEditorComponent& kec, const String& name)
: owner (kec), categoryName (name)
{}
String getUniqueName() const override { return categoryName + "_cat"; }
bool mightContainSubItems() override { return true; }
int getItemHeight() const override { return 22; }
String getAccessibilityName() override { return categoryName; }
void paintItem (Graphics& g, int width, int height) override
{
g.setFont (Font ((float) height * 0.7f, Font::bold));
g.setColour (owner.findColour (KeyMappingEditorComponent::textColourId));
g.drawText (TRANS (categoryName), 2, 0, width - 2, height, Justification::centredLeft, true);
}
void itemOpennessChanged (bool isNowOpen) override
{
if (isNowOpen)
{
if (getNumSubItems() == 0)
for (auto command : owner.getCommandManager().getCommandsInCategory (categoryName))
if (owner.shouldCommandBeIncluded (command))
addSubItem (new MappingItem (owner, command));
}
else
{
clearSubItems();
}
}
private:
KeyMappingEditorComponent& owner;
String categoryName;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CategoryItem)
};
//==============================================================================
class KeyMappingEditorComponent::TopLevelItem : public TreeViewItem,
private ChangeListener
{
public:
TopLevelItem (KeyMappingEditorComponent& kec) : owner (kec)
{
setLinesDrawnForSubItems (false);
owner.getMappings().addChangeListener (this);
}
~TopLevelItem() override
{
owner.getMappings().removeChangeListener (this);
}
bool mightContainSubItems() override { return true; }
String getUniqueName() const override { return "keys"; }
void changeListenerCallback (ChangeBroadcaster*) override
{
const OpennessRestorer opennessRestorer (*this);
clearSubItems();
for (auto category : owner.getCommandManager().getCommandCategories())
{
int count = 0;
for (auto command : owner.getCommandManager().getCommandsInCategory (category))
if (owner.shouldCommandBeIncluded (command))
++count;
if (count > 0)
addSubItem (new CategoryItem (owner, category));
}
}
private:
KeyMappingEditorComponent& owner;
};
static void resetKeyMappingsToDefaultsCallback (int result, KeyMappingEditorComponent* owner)
{
if (result != 0 && owner != nullptr)
owner->getMappings().resetToDefaultMappings();
}
//==============================================================================
KeyMappingEditorComponent::KeyMappingEditorComponent (KeyPressMappingSet& mappingManager,
const bool showResetToDefaultButton)
: mappings (mappingManager),
resetButton (TRANS ("reset to defaults"))
{
treeItem.reset (new TopLevelItem (*this));
if (showResetToDefaultButton)
{
addAndMakeVisible (resetButton);
resetButton.onClick = [this]
{
AlertWindow::showOkCancelBox (MessageBoxIconType::QuestionIcon,
TRANS("Reset to defaults"),
TRANS("Are you sure you want to reset all the key-mappings to their default state?"),
TRANS("Reset"),
{}, this,
ModalCallbackFunction::forComponent (resetKeyMappingsToDefaultsCallback, this));
};
}
addAndMakeVisible (tree);
tree.setTitle ("Key Mappings");
tree.setColour (TreeView::backgroundColourId, findColour (backgroundColourId));
tree.setRootItemVisible (false);
tree.setDefaultOpenness (true);
tree.setRootItem (treeItem.get());
tree.setIndentSize (12);
}
KeyMappingEditorComponent::~KeyMappingEditorComponent()
{
tree.setRootItem (nullptr);
}
//==============================================================================
void KeyMappingEditorComponent::setColours (Colour mainBackground,
Colour textColour)
{
setColour (backgroundColourId, mainBackground);
setColour (textColourId, textColour);
tree.setColour (TreeView::backgroundColourId, mainBackground);
}
void KeyMappingEditorComponent::parentHierarchyChanged()
{
treeItem->changeListenerCallback (nullptr);
}
void KeyMappingEditorComponent::resized()
{
int h = getHeight();
if (resetButton.isVisible())
{
const int buttonHeight = 20;
h -= buttonHeight + 8;
int x = getWidth() - 8;
resetButton.changeWidthToFitText (buttonHeight);
resetButton.setTopRightPosition (x, h + 6);
}
tree.setBounds (0, 0, getWidth(), h);
}
//==============================================================================
bool KeyMappingEditorComponent::shouldCommandBeIncluded (const CommandID commandID)
{
auto* ci = mappings.getCommandManager().getCommandForID (commandID);
return ci != nullptr && (ci->flags & ApplicationCommandInfo::hiddenFromKeyEditor) == 0;
}
bool KeyMappingEditorComponent::isCommandReadOnly (const CommandID commandID)
{
auto* ci = mappings.getCommandManager().getCommandForID (commandID);
return ci != nullptr && (ci->flags & ApplicationCommandInfo::readOnlyInKeyEditor) != 0;
}
String KeyMappingEditorComponent::getDescriptionForKeyPress (const KeyPress& key)
{
return key.getTextDescription();
}
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class KeyMappingEditorComponent::ChangeKeyButton : public Button
{
public:
ChangeKeyButton (KeyMappingEditorComponent& kec, CommandID command,
const String& keyName, int keyIndex)
: Button (keyName),
owner (kec),
commandID (command),
keyNum (keyIndex)
{
setWantsKeyboardFocus (false);
setTriggeredOnMouseDown (keyNum >= 0);
setTooltip (keyIndex < 0 ? TRANS("Adds a new key-mapping")
: TRANS("Click to change this key-mapping"));
}
void paintButton (Graphics& g, bool /*isOver*/, bool /*isDown*/) override
{
getLookAndFeel().drawKeymapChangeButton (g, getWidth(), getHeight(), *this,
keyNum >= 0 ? getName() : String());
}
void clicked() override
{
if (keyNum >= 0)
{
Component::SafePointer<ChangeKeyButton> button (this);
PopupMenu m;
m.addItem (TRANS("Change this key-mapping"),
[button]
{
if (button != nullptr)
button.getComponent()->assignNewKey();
});
m.addSeparator();
m.addItem (TRANS("Remove this key-mapping"),
[button]
{
if (button != nullptr)
button->owner.getMappings().removeKeyPress (button->commandID,
button->keyNum);
});
m.showMenuAsync (PopupMenu::Options().withTargetComponent (this));
}
else
{
assignNewKey(); // + button pressed..
}
}
using Button::clicked;
void fitToContent (const int h) noexcept
{
if (keyNum < 0)
setSize (h, h);
else
setSize (jlimit (h * 4, h * 8, 6 + Font ((float) h * 0.6f).getStringWidth (getName())), h);
}
//==============================================================================
class KeyEntryWindow : public AlertWindow
{
public:
KeyEntryWindow (KeyMappingEditorComponent& kec)
: AlertWindow (TRANS("New key-mapping"),
TRANS("Please press a key combination now..."),
MessageBoxIconType::NoIcon),
owner (kec)
{
addButton (TRANS("OK"), 1);
addButton (TRANS("Cancel"), 0);
// (avoid return + escape keys getting processed by the buttons..)
for (auto* child : getChildren())
child->setWantsKeyboardFocus (false);
setWantsKeyboardFocus (true);
grabKeyboardFocus();
}
bool keyPressed (const KeyPress& key) override
{
lastPress = key;
String message (TRANS("Key") + ": " + owner.getDescriptionForKeyPress (key));
auto previousCommand = owner.getMappings().findCommandForKeyPress (key);
if (previousCommand != 0)
message << "\n\n("
<< TRANS("Currently assigned to \"CMDN\"")
.replace ("CMDN", TRANS (owner.getCommandManager().getNameOfCommand (previousCommand)))
<< ')';
setMessage (message);
return true;
}
bool keyStateChanged (bool) override
{
return true;
}
KeyPress lastPress;
private:
KeyMappingEditorComponent& owner;
JUCE_DECLARE_NON_COPYABLE (KeyEntryWindow)
};
static void assignNewKeyCallback (int result, ChangeKeyButton* button, KeyPress newKey)
{
if (result != 0 && button != nullptr)
button->setNewKey (newKey, true);
}
void setNewKey (const KeyPress& newKey, bool dontAskUser)
{
if (newKey.isValid())
{
auto previousCommand = owner.getMappings().findCommandForKeyPress (newKey);
if (previousCommand == 0 || dontAskUser)
{
owner.getMappings().removeKeyPress (newKey);
if (keyNum >= 0)
owner.getMappings().removeKeyPress (commandID, keyNum);
owner.getMappings().addKeyPress (commandID, newKey, keyNum);
}
else
{
AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
TRANS("Change key-mapping"),
TRANS("This key is already assigned to the command \"CMDN\"")
.replace ("CMDN", owner.getCommandManager().getNameOfCommand (previousCommand))
+ "\n\n"
+ TRANS("Do you want to re-assign it to this new command instead?"),
TRANS("Re-assign"),
TRANS("Cancel"),
this,
ModalCallbackFunction::forComponent (assignNewKeyCallback,
this, KeyPress (newKey)));
}
}
}
static void keyChosen (int result, ChangeKeyButton* button)
{
if (button != nullptr && button->currentKeyEntryWindow != nullptr)
{
if (result != 0)
{
button->currentKeyEntryWindow->setVisible (false);
button->setNewKey (button->currentKeyEntryWindow->lastPress, false);
}
button->currentKeyEntryWindow.reset();
}
}
void assignNewKey()
{
currentKeyEntryWindow.reset (new KeyEntryWindow (owner));
currentKeyEntryWindow->enterModalState (true, ModalCallbackFunction::forComponent (keyChosen, this));
}
private:
KeyMappingEditorComponent& owner;
const CommandID commandID;
const int keyNum;
std::unique_ptr<KeyEntryWindow> currentKeyEntryWindow;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeKeyButton)
};
//==============================================================================
class KeyMappingEditorComponent::ItemComponent : public Component
{
public:
ItemComponent (KeyMappingEditorComponent& kec, CommandID command)
: owner (kec), commandID (command)
{
setInterceptsMouseClicks (false, true);
const bool isReadOnly = owner.isCommandReadOnly (commandID);
auto keyPresses = owner.getMappings().getKeyPressesAssignedToCommand (commandID);
for (int i = 0; i < jmin ((int) maxNumAssignments, keyPresses.size()); ++i)
addKeyPressButton (owner.getDescriptionForKeyPress (keyPresses.getReference (i)), i, isReadOnly);
addKeyPressButton ("Change Key Mapping", -1, isReadOnly);
}
void addKeyPressButton (const String& desc, const int index, const bool isReadOnly)
{
auto* b = new ChangeKeyButton (owner, commandID, desc, index);
keyChangeButtons.add (b);
b->setEnabled (! isReadOnly);
b->setVisible (keyChangeButtons.size() <= (int) maxNumAssignments);
addChildComponent (b);
}
void paint (Graphics& g) override
{
g.setFont ((float) getHeight() * 0.7f);
g.setColour (owner.findColour (KeyMappingEditorComponent::textColourId));
g.drawFittedText (TRANS (owner.getCommandManager().getNameOfCommand (commandID)),
4, 0, jmax (40, getChildComponent (0)->getX() - 5), getHeight(),
Justification::centredLeft, true);
}
void resized() override
{
int x = getWidth() - 4;
for (int i = keyChangeButtons.size(); --i >= 0;)
{
auto* b = keyChangeButtons.getUnchecked(i);
b->fitToContent (getHeight() - 2);
b->setTopRightPosition (x, 1);
x = b->getX() - 5;
}
}
private:
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
{
return createIgnoredAccessibilityHandler (*this);
}
KeyMappingEditorComponent& owner;
OwnedArray<ChangeKeyButton> keyChangeButtons;
const CommandID commandID;
enum { maxNumAssignments = 3 };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemComponent)
};
//==============================================================================
class KeyMappingEditorComponent::MappingItem : public TreeViewItem
{
public:
MappingItem (KeyMappingEditorComponent& kec, CommandID command)
: owner (kec), commandID (command)
{}
String getUniqueName() const override { return String ((int) commandID) + "_id"; }
bool mightContainSubItems() override { return false; }
int getItemHeight() const override { return 20; }
std::unique_ptr<Component> createItemComponent() override { return std::make_unique<ItemComponent> (owner, commandID); }
String getAccessibilityName() override { return TRANS (owner.getCommandManager().getNameOfCommand (commandID)); }
private:
KeyMappingEditorComponent& owner;
const CommandID commandID;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MappingItem)
};
//==============================================================================
class KeyMappingEditorComponent::CategoryItem : public TreeViewItem
{
public:
CategoryItem (KeyMappingEditorComponent& kec, const String& name)
: owner (kec), categoryName (name)
{}
String getUniqueName() const override { return categoryName + "_cat"; }
bool mightContainSubItems() override { return true; }
int getItemHeight() const override { return 22; }
String getAccessibilityName() override { return categoryName; }
void paintItem (Graphics& g, int width, int height) override
{
g.setFont (Font ((float) height * 0.7f, Font::bold));
g.setColour (owner.findColour (KeyMappingEditorComponent::textColourId));
g.drawText (TRANS (categoryName), 2, 0, width - 2, height, Justification::centredLeft, true);
}
void itemOpennessChanged (bool isNowOpen) override
{
if (isNowOpen)
{
if (getNumSubItems() == 0)
for (auto command : owner.getCommandManager().getCommandsInCategory (categoryName))
if (owner.shouldCommandBeIncluded (command))
addSubItem (new MappingItem (owner, command));
}
else
{
clearSubItems();
}
}
private:
KeyMappingEditorComponent& owner;
String categoryName;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CategoryItem)
};
//==============================================================================
class KeyMappingEditorComponent::TopLevelItem : public TreeViewItem,
private ChangeListener
{
public:
TopLevelItem (KeyMappingEditorComponent& kec) : owner (kec)
{
setLinesDrawnForSubItems (false);
owner.getMappings().addChangeListener (this);
}
~TopLevelItem() override
{
owner.getMappings().removeChangeListener (this);
}
bool mightContainSubItems() override { return true; }
String getUniqueName() const override { return "keys"; }
void changeListenerCallback (ChangeBroadcaster*) override
{
const OpennessRestorer opennessRestorer (*this);
clearSubItems();
for (auto category : owner.getCommandManager().getCommandCategories())
{
int count = 0;
for (auto command : owner.getCommandManager().getCommandsInCategory (category))
if (owner.shouldCommandBeIncluded (command))
++count;
if (count > 0)
addSubItem (new CategoryItem (owner, category));
}
}
private:
KeyMappingEditorComponent& owner;
};
static void resetKeyMappingsToDefaultsCallback (int result, KeyMappingEditorComponent* owner)
{
if (result != 0 && owner != nullptr)
owner->getMappings().resetToDefaultMappings();
}
//==============================================================================
KeyMappingEditorComponent::KeyMappingEditorComponent (KeyPressMappingSet& mappingManager,
const bool showResetToDefaultButton)
: mappings (mappingManager),
resetButton (TRANS ("reset to defaults"))
{
treeItem.reset (new TopLevelItem (*this));
if (showResetToDefaultButton)
{
addAndMakeVisible (resetButton);
resetButton.onClick = [this]
{
AlertWindow::showOkCancelBox (MessageBoxIconType::QuestionIcon,
TRANS("Reset to defaults"),
TRANS("Are you sure you want to reset all the key-mappings to their default state?"),
TRANS("Reset"),
{}, this,
ModalCallbackFunction::forComponent (resetKeyMappingsToDefaultsCallback, this));
};
}
addAndMakeVisible (tree);
tree.setTitle ("Key Mappings");
tree.setColour (TreeView::backgroundColourId, findColour (backgroundColourId));
tree.setRootItemVisible (false);
tree.setDefaultOpenness (true);
tree.setRootItem (treeItem.get());
tree.setIndentSize (12);
}
KeyMappingEditorComponent::~KeyMappingEditorComponent()
{
tree.setRootItem (nullptr);
}
//==============================================================================
void KeyMappingEditorComponent::setColours (Colour mainBackground,
Colour textColour)
{
setColour (backgroundColourId, mainBackground);
setColour (textColourId, textColour);
tree.setColour (TreeView::backgroundColourId, mainBackground);
}
void KeyMappingEditorComponent::parentHierarchyChanged()
{
treeItem->changeListenerCallback (nullptr);
}
void KeyMappingEditorComponent::resized()
{
int h = getHeight();
if (resetButton.isVisible())
{
const int buttonHeight = 20;
h -= buttonHeight + 8;
int x = getWidth() - 8;
resetButton.changeWidthToFitText (buttonHeight);
resetButton.setTopRightPosition (x, h + 6);
}
tree.setBounds (0, 0, getWidth(), h);
}
//==============================================================================
bool KeyMappingEditorComponent::shouldCommandBeIncluded (const CommandID commandID)
{
auto* ci = mappings.getCommandManager().getCommandForID (commandID);
return ci != nullptr && (ci->flags & ApplicationCommandInfo::hiddenFromKeyEditor) == 0;
}
bool KeyMappingEditorComponent::isCommandReadOnly (const CommandID commandID)
{
auto* ci = mappings.getCommandManager().getCommandForID (commandID);
return ci != nullptr && (ci->flags & ApplicationCommandInfo::readOnlyInKeyEditor) != 0;
}
String KeyMappingEditorComponent::getDescriptionForKeyPress (const KeyPress& key)
{
return key.getTextDescription();
}
} // namespace juce

View File

@ -1,132 +1,132 @@
/*
==============================================================================
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 to allow editing of the keymaps stored by a KeyPressMappingSet
object.
@see KeyPressMappingSet
@tags{GUI}
*/
class JUCE_API KeyMappingEditorComponent : public Component
{
public:
//==============================================================================
/** Creates a KeyMappingEditorComponent.
@param mappingSet this is the set of mappings to display and edit. Make sure the
mappings object is not deleted before this component!
@param showResetToDefaultButton if true, then at the bottom of the list, the
component will include a 'reset to defaults' button.
*/
KeyMappingEditorComponent (KeyPressMappingSet& mappingSet,
bool showResetToDefaultButton);
/** Destructor. */
~KeyMappingEditorComponent() override;
//==============================================================================
/** Sets up the colours to use for parts of the component.
@param mainBackground colour to use for most of the background
@param textColour colour to use for the text
*/
void setColours (Colour mainBackground,
Colour textColour);
/** Returns the KeyPressMappingSet that this component is acting upon. */
KeyPressMappingSet& getMappings() const noexcept { return mappings; }
/** Returns the ApplicationCommandManager that this component is connected to. */
ApplicationCommandManager& getCommandManager() const noexcept { return mappings.getCommandManager(); }
//==============================================================================
/** Can be overridden if some commands need to be excluded from the list.
By default this will use the KeyPressMappingSet's shouldCommandBeVisibleInEditor()
method to decide what to return, but you can override it to handle special cases.
*/
virtual bool shouldCommandBeIncluded (CommandID commandID);
/** Can be overridden to indicate that some commands are shown as read-only.
By default this will use the KeyPressMappingSet's shouldCommandBeReadOnlyInEditor()
method to decide what to return, but you can override it to handle special cases.
*/
virtual bool isCommandReadOnly (CommandID commandID);
/** This can be overridden to let you change the format of the string used
to describe a keypress.
This is handy if you're using non-standard KeyPress objects, e.g. for custom
keys that are triggered by something else externally. If you override the
method, be sure to let the base class's method handle keys you're not
interested in.
*/
virtual String getDescriptionForKeyPress (const KeyPress& key);
//==============================================================================
/** A set of colour IDs to use to change the colour of various aspects of the editor.
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 = 0x100ad00, /**< The background colour to fill the editor background. */
textColourId = 0x100ad01, /**< The colour for the text. */
};
//==============================================================================
/** @internal */
void parentHierarchyChanged() override;
/** @internal */
void resized() override;
private:
//==============================================================================
KeyPressMappingSet& mappings;
TreeView tree;
TextButton resetButton;
class TopLevelItem;
class ChangeKeyButton;
class MappingItem;
class CategoryItem;
class ItemComponent;
std::unique_ptr<TopLevelItem> treeItem;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KeyMappingEditorComponent)
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A component to allow editing of the keymaps stored by a KeyPressMappingSet
object.
@see KeyPressMappingSet
@tags{GUI}
*/
class JUCE_API KeyMappingEditorComponent : public Component
{
public:
//==============================================================================
/** Creates a KeyMappingEditorComponent.
@param mappingSet this is the set of mappings to display and edit. Make sure the
mappings object is not deleted before this component!
@param showResetToDefaultButton if true, then at the bottom of the list, the
component will include a 'reset to defaults' button.
*/
KeyMappingEditorComponent (KeyPressMappingSet& mappingSet,
bool showResetToDefaultButton);
/** Destructor. */
~KeyMappingEditorComponent() override;
//==============================================================================
/** Sets up the colours to use for parts of the component.
@param mainBackground colour to use for most of the background
@param textColour colour to use for the text
*/
void setColours (Colour mainBackground,
Colour textColour);
/** Returns the KeyPressMappingSet that this component is acting upon. */
KeyPressMappingSet& getMappings() const noexcept { return mappings; }
/** Returns the ApplicationCommandManager that this component is connected to. */
ApplicationCommandManager& getCommandManager() const noexcept { return mappings.getCommandManager(); }
//==============================================================================
/** Can be overridden if some commands need to be excluded from the list.
By default this will use the KeyPressMappingSet's shouldCommandBeVisibleInEditor()
method to decide what to return, but you can override it to handle special cases.
*/
virtual bool shouldCommandBeIncluded (CommandID commandID);
/** Can be overridden to indicate that some commands are shown as read-only.
By default this will use the KeyPressMappingSet's shouldCommandBeReadOnlyInEditor()
method to decide what to return, but you can override it to handle special cases.
*/
virtual bool isCommandReadOnly (CommandID commandID);
/** This can be overridden to let you change the format of the string used
to describe a keypress.
This is handy if you're using non-standard KeyPress objects, e.g. for custom
keys that are triggered by something else externally. If you override the
method, be sure to let the base class's method handle keys you're not
interested in.
*/
virtual String getDescriptionForKeyPress (const KeyPress& key);
//==============================================================================
/** A set of colour IDs to use to change the colour of various aspects of the editor.
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 = 0x100ad00, /**< The background colour to fill the editor background. */
textColourId = 0x100ad01, /**< The colour for the text. */
};
//==============================================================================
/** @internal */
void parentHierarchyChanged() override;
/** @internal */
void resized() override;
private:
//==============================================================================
KeyPressMappingSet& mappings;
TreeView tree;
TextButton resetButton;
class TopLevelItem;
class ChangeKeyButton;
class MappingItem;
class CategoryItem;
class ItemComponent;
std::unique_ptr<TopLevelItem> treeItem;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KeyMappingEditorComponent)
};
} // namespace juce

View File

@ -1,497 +1,497 @@
/*
==============================================================================
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_ENABLE_LIVE_CONSTANT_EDITOR
namespace LiveConstantEditor
{
//==============================================================================
class AllComponentRepainter : private Timer,
private DeletedAtShutdown
{
public:
AllComponentRepainter() {}
~AllComponentRepainter() override { clearSingletonInstance(); }
JUCE_DECLARE_SINGLETON (AllComponentRepainter, false)
void trigger()
{
if (! isTimerRunning())
startTimer (100);
}
private:
void timerCallback() override
{
stopTimer();
Array<Component*> alreadyDone;
for (int i = TopLevelWindow::getNumTopLevelWindows(); --i >= 0;)
if (auto* c = TopLevelWindow::getTopLevelWindow(i))
repaintAndResizeAllComps (c, alreadyDone);
auto& desktop = Desktop::getInstance();
for (int i = desktop.getNumComponents(); --i >= 0;)
if (auto* c = desktop.getComponent(i))
repaintAndResizeAllComps (c, alreadyDone);
}
static void repaintAndResizeAllComps (Component::SafePointer<Component> c,
Array<Component*>& alreadyDone)
{
if (c->isVisible() && ! alreadyDone.contains (c))
{
c->repaint();
c->resized();
for (int i = c->getNumChildComponents(); --i >= 0;)
{
if (auto* child = c->getChildComponent(i))
{
repaintAndResizeAllComps (child, alreadyDone);
alreadyDone.add (child);
}
if (c == nullptr)
break;
}
}
}
};
JUCE_IMPLEMENT_SINGLETON (AllComponentRepainter)
JUCE_IMPLEMENT_SINGLETON (ValueList)
//==============================================================================
int64 parseInt (String s)
{
s = s.trimStart();
if (s.startsWithChar ('-'))
return -parseInt (s.substring (1));
if (s.startsWith ("0x"))
return s.substring(2).getHexValue64();
return s.getLargeIntValue();
}
double parseDouble (const String& s)
{
return s.retainCharacters ("0123456789.eE-").getDoubleValue();
}
String intToString (int v, bool preferHex) { return preferHex ? "0x" + String::toHexString (v) : String (v); }
String intToString (int64 v, bool preferHex) { return preferHex ? "0x" + String::toHexString (v) : String (v); }
//==============================================================================
LiveValueBase::LiveValueBase (const char* file, int line)
: sourceFile (file), sourceLine (line)
{
name = File (sourceFile).getFileName() + " : " + String (sourceLine);
}
LiveValueBase::~LiveValueBase()
{
}
//==============================================================================
LivePropertyEditorBase::LivePropertyEditorBase (LiveValueBase& v, CodeDocument& d)
: value (v), document (d), sourceEditor (document, &tokeniser)
{
setSize (600, 100);
addAndMakeVisible (name);
addAndMakeVisible (resetButton);
addAndMakeVisible (valueEditor);
addAndMakeVisible (sourceEditor);
findOriginalValueInCode();
selectOriginalValue();
name.setFont (13.0f);
name.setText (v.name, dontSendNotification);
valueEditor.setMultiLine (v.isString());
valueEditor.setReturnKeyStartsNewLine (v.isString());
valueEditor.setText (v.getStringValue (wasHex), dontSendNotification);
valueEditor.onTextChange = [this] { applyNewValue (valueEditor.getText()); };
sourceEditor.setReadOnly (true);
sourceEditor.setFont (sourceEditor.getFont().withHeight (13.0f));
resetButton.onClick = [this] { applyNewValue (value.getOriginalStringValue (wasHex)); };
}
void LivePropertyEditorBase::paint (Graphics& g)
{
g.setColour (Colours::white);
g.fillRect (getLocalBounds().removeFromBottom (1));
}
void LivePropertyEditorBase::resized()
{
auto r = getLocalBounds().reduced (0, 3).withTrimmedBottom (1);
auto left = r.removeFromLeft (jmax (200, r.getWidth() / 3));
auto top = left.removeFromTop (25);
resetButton.setBounds (top.removeFromRight (35).reduced (0, 3));
name.setBounds (top);
if (customComp != nullptr)
{
valueEditor.setBounds (left.removeFromTop (25));
left.removeFromTop (2);
customComp->setBounds (left);
}
else
{
valueEditor.setBounds (left);
}
r.removeFromLeft (4);
sourceEditor.setBounds (r);
}
void LivePropertyEditorBase::applyNewValue (const String& s)
{
value.setStringValue (s);
document.replaceSection (valueStart.getPosition(), valueEnd.getPosition(), value.getCodeValue (wasHex));
document.clearUndoHistory();
selectOriginalValue();
valueEditor.setText (s, dontSendNotification);
AllComponentRepainter::getInstance()->trigger();
}
void LivePropertyEditorBase::selectOriginalValue()
{
sourceEditor.selectRegion (valueStart, valueEnd);
}
void LivePropertyEditorBase::findOriginalValueInCode()
{
CodeDocument::Position pos (document, value.sourceLine, 0);
auto line = pos.getLineText();
auto p = line.getCharPointer();
p = CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT"));
if (p.isEmpty())
{
// Not sure how this would happen - some kind of mix-up between source code and line numbers..
jassertfalse;
return;
}
p += (int) (sizeof ("JUCE_LIVE_CONSTANT") - 1);
p.incrementToEndOfWhitespace();
if (! CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT")).isEmpty())
{
// Aargh! You've added two JUCE_LIVE_CONSTANT macros on the same line!
// They're identified by their line number, so you must make sure each
// one goes on a separate line!
jassertfalse;
}
if (p.getAndAdvance() == '(')
{
auto start = p, end = p;
int depth = 1;
while (! end.isEmpty())
{
auto c = end.getAndAdvance();
if (c == '(') ++depth;
if (c == ')') --depth;
if (depth == 0)
{
--end;
break;
}
}
if (end > start)
{
valueStart = CodeDocument::Position (document, value.sourceLine, (int) (start - line.getCharPointer()));
valueEnd = CodeDocument::Position (document, value.sourceLine, (int) (end - line.getCharPointer()));
valueStart.setPositionMaintained (true);
valueEnd.setPositionMaintained (true);
wasHex = String (start, end).containsIgnoreCase ("0x");
}
}
}
//==============================================================================
class ValueListHolderComponent : public Component
{
public:
ValueListHolderComponent (ValueList& l) : valueList (l)
{
setVisible (true);
}
void addItem (int width, LiveValueBase& v, CodeDocument& doc)
{
addAndMakeVisible (editors.add (v.createPropertyComponent (doc)));
layout (width);
}
void layout (int width)
{
setSize (width, editors.size() * itemHeight);
resized();
}
void resized() override
{
auto r = getLocalBounds().reduced (2, 0);
for (int i = 0; i < editors.size(); ++i)
editors.getUnchecked(i)->setBounds (r.removeFromTop (itemHeight));
}
enum { itemHeight = 120 };
ValueList& valueList;
OwnedArray<LivePropertyEditorBase> editors;
};
//==============================================================================
class ValueList::EditorWindow : public DocumentWindow,
private DeletedAtShutdown
{
public:
EditorWindow (ValueList& list)
: DocumentWindow ("Live Values", Colours::lightgrey, DocumentWindow::closeButton)
{
setLookAndFeel (&lookAndFeel);
setUsingNativeTitleBar (true);
viewport.setViewedComponent (new ValueListHolderComponent (list), true);
viewport.setSize (700, 600);
viewport.setScrollBarsShown (true, false);
setContentNonOwned (&viewport, true);
setResizable (true, false);
setResizeLimits (500, 400, 10000, 10000);
centreWithSize (getWidth(), getHeight());
setVisible (true);
}
~EditorWindow() override
{
setLookAndFeel (nullptr);
}
void closeButtonPressed() override
{
setVisible (false);
}
void updateItems (ValueList& list)
{
if (auto* l = dynamic_cast<ValueListHolderComponent*> (viewport.getViewedComponent()))
{
while (l->getNumChildComponents() < list.values.size())
{
if (auto* v = list.values [l->getNumChildComponents()])
l->addItem (viewport.getMaximumVisibleWidth(), *v, list.getDocument (v->sourceFile));
else
break;
}
setVisible (true);
}
}
void resized() override
{
DocumentWindow::resized();
if (auto* l = dynamic_cast<ValueListHolderComponent*> (viewport.getViewedComponent()))
l->layout (viewport.getMaximumVisibleWidth());
}
Viewport viewport;
LookAndFeel_V3 lookAndFeel;
};
//==============================================================================
ValueList::ValueList() {}
ValueList::~ValueList() { clearSingletonInstance(); }
void ValueList::addValue (LiveValueBase* v)
{
values.add (v);
triggerAsyncUpdate();
}
void ValueList::handleAsyncUpdate()
{
if (editorWindow == nullptr)
editorWindow = new EditorWindow (*this);
editorWindow->updateItems (*this);
}
CodeDocument& ValueList::getDocument (const File& file)
{
const int index = documentFiles.indexOf (file.getFullPathName());
if (index >= 0)
return *documents.getUnchecked (index);
auto* doc = documents.add (new CodeDocument());
documentFiles.add (file);
doc->replaceAllContent (file.loadFileAsString());
doc->clearUndoHistory();
return *doc;
}
//==============================================================================
struct ColourEditorComp : public Component,
private ChangeListener
{
ColourEditorComp (LivePropertyEditorBase& e) : editor (e)
{
setMouseCursor (MouseCursor::PointingHandCursor);
}
Colour getColour() const
{
return Colour ((uint32) parseInt (editor.value.getStringValue (false)));
}
void paint (Graphics& g) override
{
g.fillCheckerBoard (getLocalBounds().toFloat(), 6.0f, 6.0f,
Colour (0xffdddddd).overlaidWith (getColour()),
Colour (0xffffffff).overlaidWith (getColour()));
}
void mouseDown (const MouseEvent&) override
{
auto colourSelector = std::make_unique<ColourSelector>();
colourSelector->setName ("Colour");
colourSelector->setCurrentColour (getColour());
colourSelector->addChangeListener (this);
colourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
colourSelector->setSize (300, 400);
CallOutBox::launchAsynchronously (std::move (colourSelector), getScreenBounds(), nullptr);
}
void changeListenerCallback (ChangeBroadcaster* source) override
{
if (auto* cs = dynamic_cast<ColourSelector*> (source))
editor.applyNewValue (getAsString (cs->getCurrentColour(), true));
repaint();
}
LivePropertyEditorBase& editor;
};
Component* createColourEditor (LivePropertyEditorBase& editor)
{
return new ColourEditorComp (editor);
}
//==============================================================================
struct SliderComp : public Component
{
SliderComp (LivePropertyEditorBase& e, bool useFloat)
: editor (e), isFloat (useFloat)
{
slider.setTextBoxStyle (Slider::NoTextBox, true, 0, 0);
addAndMakeVisible (slider);
updateRange();
slider.onDragEnd = [this] { updateRange(); };
slider.onValueChange = [this]
{
editor.applyNewValue (isFloat ? getAsString ((double) slider.getValue(), editor.wasHex)
: getAsString ((int64) slider.getValue(), editor.wasHex));
};
}
virtual void updateRange()
{
double v = isFloat ? parseDouble (editor.value.getStringValue (false))
: (double) parseInt (editor.value.getStringValue (false));
double range = isFloat ? 10 : 100;
slider.setRange (v - range, v + range);
slider.setValue (v, dontSendNotification);
}
void resized() override
{
slider.setBounds (getLocalBounds().removeFromTop (25));
}
LivePropertyEditorBase& editor;
Slider slider;
bool isFloat;
};
//==============================================================================
struct BoolSliderComp : public SliderComp
{
BoolSliderComp (LivePropertyEditorBase& e)
: SliderComp (e, false)
{
slider.onValueChange = [this] { editor.applyNewValue (slider.getValue() > 0.5 ? "true" : "false"); };
}
void updateRange() override
{
slider.setRange (0.0, 1.0, dontSendNotification);
slider.setValue (editor.value.getStringValue (false) == "true", dontSendNotification);
}
};
Component* createIntegerSlider (LivePropertyEditorBase& editor) { return new SliderComp (editor, false); }
Component* createFloatSlider (LivePropertyEditorBase& editor) { return new SliderComp (editor, true); }
Component* createBoolSlider (LivePropertyEditorBase& editor) { return new BoolSliderComp (editor); }
}
#endif
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR
namespace LiveConstantEditor
{
//==============================================================================
class AllComponentRepainter : private Timer,
private DeletedAtShutdown
{
public:
AllComponentRepainter() {}
~AllComponentRepainter() override { clearSingletonInstance(); }
JUCE_DECLARE_SINGLETON (AllComponentRepainter, false)
void trigger()
{
if (! isTimerRunning())
startTimer (100);
}
private:
void timerCallback() override
{
stopTimer();
Array<Component*> alreadyDone;
for (int i = TopLevelWindow::getNumTopLevelWindows(); --i >= 0;)
if (auto* c = TopLevelWindow::getTopLevelWindow(i))
repaintAndResizeAllComps (c, alreadyDone);
auto& desktop = Desktop::getInstance();
for (int i = desktop.getNumComponents(); --i >= 0;)
if (auto* c = desktop.getComponent(i))
repaintAndResizeAllComps (c, alreadyDone);
}
static void repaintAndResizeAllComps (Component::SafePointer<Component> c,
Array<Component*>& alreadyDone)
{
if (c->isVisible() && ! alreadyDone.contains (c))
{
c->repaint();
c->resized();
for (int i = c->getNumChildComponents(); --i >= 0;)
{
if (auto* child = c->getChildComponent(i))
{
repaintAndResizeAllComps (child, alreadyDone);
alreadyDone.add (child);
}
if (c == nullptr)
break;
}
}
}
};
JUCE_IMPLEMENT_SINGLETON (AllComponentRepainter)
JUCE_IMPLEMENT_SINGLETON (ValueList)
//==============================================================================
int64 parseInt (String s)
{
s = s.trimStart();
if (s.startsWithChar ('-'))
return -parseInt (s.substring (1));
if (s.startsWith ("0x"))
return s.substring(2).getHexValue64();
return s.getLargeIntValue();
}
double parseDouble (const String& s)
{
return s.retainCharacters ("0123456789.eE-").getDoubleValue();
}
String intToString (int v, bool preferHex) { return preferHex ? "0x" + String::toHexString (v) : String (v); }
String intToString (int64 v, bool preferHex) { return preferHex ? "0x" + String::toHexString (v) : String (v); }
//==============================================================================
LiveValueBase::LiveValueBase (const char* file, int line)
: sourceFile (file), sourceLine (line)
{
name = File (sourceFile).getFileName() + " : " + String (sourceLine);
}
LiveValueBase::~LiveValueBase()
{
}
//==============================================================================
LivePropertyEditorBase::LivePropertyEditorBase (LiveValueBase& v, CodeDocument& d)
: value (v), document (d), sourceEditor (document, &tokeniser)
{
setSize (600, 100);
addAndMakeVisible (name);
addAndMakeVisible (resetButton);
addAndMakeVisible (valueEditor);
addAndMakeVisible (sourceEditor);
findOriginalValueInCode();
selectOriginalValue();
name.setFont (13.0f);
name.setText (v.name, dontSendNotification);
valueEditor.setMultiLine (v.isString());
valueEditor.setReturnKeyStartsNewLine (v.isString());
valueEditor.setText (v.getStringValue (wasHex), dontSendNotification);
valueEditor.onTextChange = [this] { applyNewValue (valueEditor.getText()); };
sourceEditor.setReadOnly (true);
sourceEditor.setFont (sourceEditor.getFont().withHeight (13.0f));
resetButton.onClick = [this] { applyNewValue (value.getOriginalStringValue (wasHex)); };
}
void LivePropertyEditorBase::paint (Graphics& g)
{
g.setColour (Colours::white);
g.fillRect (getLocalBounds().removeFromBottom (1));
}
void LivePropertyEditorBase::resized()
{
auto r = getLocalBounds().reduced (0, 3).withTrimmedBottom (1);
auto left = r.removeFromLeft (jmax (200, r.getWidth() / 3));
auto top = left.removeFromTop (25);
resetButton.setBounds (top.removeFromRight (35).reduced (0, 3));
name.setBounds (top);
if (customComp != nullptr)
{
valueEditor.setBounds (left.removeFromTop (25));
left.removeFromTop (2);
customComp->setBounds (left);
}
else
{
valueEditor.setBounds (left);
}
r.removeFromLeft (4);
sourceEditor.setBounds (r);
}
void LivePropertyEditorBase::applyNewValue (const String& s)
{
value.setStringValue (s);
document.replaceSection (valueStart.getPosition(), valueEnd.getPosition(), value.getCodeValue (wasHex));
document.clearUndoHistory();
selectOriginalValue();
valueEditor.setText (s, dontSendNotification);
AllComponentRepainter::getInstance()->trigger();
}
void LivePropertyEditorBase::selectOriginalValue()
{
sourceEditor.selectRegion (valueStart, valueEnd);
}
void LivePropertyEditorBase::findOriginalValueInCode()
{
CodeDocument::Position pos (document, value.sourceLine, 0);
auto line = pos.getLineText();
auto p = line.getCharPointer();
p = CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT"));
if (p.isEmpty())
{
// Not sure how this would happen - some kind of mix-up between source code and line numbers..
jassertfalse;
return;
}
p += (int) (sizeof ("JUCE_LIVE_CONSTANT") - 1);
p.incrementToEndOfWhitespace();
if (! CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT")).isEmpty())
{
// Aargh! You've added two JUCE_LIVE_CONSTANT macros on the same line!
// They're identified by their line number, so you must make sure each
// one goes on a separate line!
jassertfalse;
}
if (p.getAndAdvance() == '(')
{
auto start = p, end = p;
int depth = 1;
while (! end.isEmpty())
{
auto c = end.getAndAdvance();
if (c == '(') ++depth;
if (c == ')') --depth;
if (depth == 0)
{
--end;
break;
}
}
if (end > start)
{
valueStart = CodeDocument::Position (document, value.sourceLine, (int) (start - line.getCharPointer()));
valueEnd = CodeDocument::Position (document, value.sourceLine, (int) (end - line.getCharPointer()));
valueStart.setPositionMaintained (true);
valueEnd.setPositionMaintained (true);
wasHex = String (start, end).containsIgnoreCase ("0x");
}
}
}
//==============================================================================
class ValueListHolderComponent : public Component
{
public:
ValueListHolderComponent (ValueList& l) : valueList (l)
{
setVisible (true);
}
void addItem (int width, LiveValueBase& v, CodeDocument& doc)
{
addAndMakeVisible (editors.add (v.createPropertyComponent (doc)));
layout (width);
}
void layout (int width)
{
setSize (width, editors.size() * itemHeight);
resized();
}
void resized() override
{
auto r = getLocalBounds().reduced (2, 0);
for (int i = 0; i < editors.size(); ++i)
editors.getUnchecked(i)->setBounds (r.removeFromTop (itemHeight));
}
enum { itemHeight = 120 };
ValueList& valueList;
OwnedArray<LivePropertyEditorBase> editors;
};
//==============================================================================
class ValueList::EditorWindow : public DocumentWindow,
private DeletedAtShutdown
{
public:
EditorWindow (ValueList& list)
: DocumentWindow ("Live Values", Colours::lightgrey, DocumentWindow::closeButton)
{
setLookAndFeel (&lookAndFeel);
setUsingNativeTitleBar (true);
viewport.setViewedComponent (new ValueListHolderComponent (list), true);
viewport.setSize (700, 600);
viewport.setScrollBarsShown (true, false);
setContentNonOwned (&viewport, true);
setResizable (true, false);
setResizeLimits (500, 400, 10000, 10000);
centreWithSize (getWidth(), getHeight());
setVisible (true);
}
~EditorWindow() override
{
setLookAndFeel (nullptr);
}
void closeButtonPressed() override
{
setVisible (false);
}
void updateItems (ValueList& list)
{
if (auto* l = dynamic_cast<ValueListHolderComponent*> (viewport.getViewedComponent()))
{
while (l->getNumChildComponents() < list.values.size())
{
if (auto* v = list.values [l->getNumChildComponents()])
l->addItem (viewport.getMaximumVisibleWidth(), *v, list.getDocument (v->sourceFile));
else
break;
}
setVisible (true);
}
}
void resized() override
{
DocumentWindow::resized();
if (auto* l = dynamic_cast<ValueListHolderComponent*> (viewport.getViewedComponent()))
l->layout (viewport.getMaximumVisibleWidth());
}
Viewport viewport;
LookAndFeel_V3 lookAndFeel;
};
//==============================================================================
ValueList::ValueList() {}
ValueList::~ValueList() { clearSingletonInstance(); }
void ValueList::addValue (LiveValueBase* v)
{
values.add (v);
triggerAsyncUpdate();
}
void ValueList::handleAsyncUpdate()
{
if (editorWindow == nullptr)
editorWindow = new EditorWindow (*this);
editorWindow->updateItems (*this);
}
CodeDocument& ValueList::getDocument (const File& file)
{
const int index = documentFiles.indexOf (file.getFullPathName());
if (index >= 0)
return *documents.getUnchecked (index);
auto* doc = documents.add (new CodeDocument());
documentFiles.add (file);
doc->replaceAllContent (file.loadFileAsString());
doc->clearUndoHistory();
return *doc;
}
//==============================================================================
struct ColourEditorComp : public Component,
private ChangeListener
{
ColourEditorComp (LivePropertyEditorBase& e) : editor (e)
{
setMouseCursor (MouseCursor::PointingHandCursor);
}
Colour getColour() const
{
return Colour ((uint32) parseInt (editor.value.getStringValue (false)));
}
void paint (Graphics& g) override
{
g.fillCheckerBoard (getLocalBounds().toFloat(), 6.0f, 6.0f,
Colour (0xffdddddd).overlaidWith (getColour()),
Colour (0xffffffff).overlaidWith (getColour()));
}
void mouseDown (const MouseEvent&) override
{
auto colourSelector = std::make_unique<ColourSelector>();
colourSelector->setName ("Colour");
colourSelector->setCurrentColour (getColour());
colourSelector->addChangeListener (this);
colourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
colourSelector->setSize (300, 400);
CallOutBox::launchAsynchronously (std::move (colourSelector), getScreenBounds(), nullptr);
}
void changeListenerCallback (ChangeBroadcaster* source) override
{
if (auto* cs = dynamic_cast<ColourSelector*> (source))
editor.applyNewValue (getAsString (cs->getCurrentColour(), true));
repaint();
}
LivePropertyEditorBase& editor;
};
Component* createColourEditor (LivePropertyEditorBase& editor)
{
return new ColourEditorComp (editor);
}
//==============================================================================
struct SliderComp : public Component
{
SliderComp (LivePropertyEditorBase& e, bool useFloat)
: editor (e), isFloat (useFloat)
{
slider.setTextBoxStyle (Slider::NoTextBox, true, 0, 0);
addAndMakeVisible (slider);
updateRange();
slider.onDragEnd = [this] { updateRange(); };
slider.onValueChange = [this]
{
editor.applyNewValue (isFloat ? getAsString ((double) slider.getValue(), editor.wasHex)
: getAsString ((int64) slider.getValue(), editor.wasHex));
};
}
virtual void updateRange()
{
double v = isFloat ? parseDouble (editor.value.getStringValue (false))
: (double) parseInt (editor.value.getStringValue (false));
double range = isFloat ? 10 : 100;
slider.setRange (v - range, v + range);
slider.setValue (v, dontSendNotification);
}
void resized() override
{
slider.setBounds (getLocalBounds().removeFromTop (25));
}
LivePropertyEditorBase& editor;
Slider slider;
bool isFloat;
};
//==============================================================================
struct BoolSliderComp : public SliderComp
{
BoolSliderComp (LivePropertyEditorBase& e)
: SliderComp (e, false)
{
slider.onValueChange = [this] { editor.applyNewValue (slider.getValue() > 0.5 ? "true" : "false"); };
}
void updateRange() override
{
slider.setRange (0.0, 1.0, dontSendNotification);
slider.setValue (editor.value.getStringValue (false) == "true", dontSendNotification);
}
};
Component* createIntegerSlider (LivePropertyEditorBase& editor) { return new SliderComp (editor, false); }
Component* createFloatSlider (LivePropertyEditorBase& editor) { return new SliderComp (editor, true); }
Component* createBoolSlider (LivePropertyEditorBase& editor) { return new BoolSliderComp (editor); }
}
#endif
} // namespace juce

View File

@ -1,305 +1,305 @@
/*
==============================================================================
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_ENABLE_LIVE_CONSTANT_EDITOR && ! defined (DOXYGEN)
//==============================================================================
/** You can safely ignore all the stuff in this namespace - it's a bunch of boilerplate
code used to implement the JUCE_LIVE_CONSTANT functionality.
*/
namespace LiveConstantEditor
{
int64 parseInt (String);
double parseDouble (const String&);
String intToString (int, bool preferHex);
String intToString (int64, bool preferHex);
template <typename Type>
static void setFromString (Type& v, const String& s) { v = static_cast<Type> (s); }
inline void setFromString (char& v, const String& s) { v = (char) parseInt (s); }
inline void setFromString (unsigned char& v, const String& s) { v = (unsigned char) parseInt (s); }
inline void setFromString (short& v, const String& s) { v = (short) parseInt (s); }
inline void setFromString (unsigned short& v, const String& s) { v = (unsigned short) parseInt (s); }
inline void setFromString (int& v, const String& s) { v = (int) parseInt (s); }
inline void setFromString (unsigned int& v, const String& s) { v = (unsigned int) parseInt (s); }
inline void setFromString (long& v, const String& s) { v = (long) parseInt (s); }
inline void setFromString (unsigned long& v, const String& s) { v = (unsigned long) parseInt (s); }
inline void setFromString (int64& v, const String& s) { v = (int64) parseInt (s); }
inline void setFromString (uint64& v, const String& s) { v = (uint64) parseInt (s); }
inline void setFromString (double& v, const String& s) { v = parseDouble (s); }
inline void setFromString (float& v, const String& s) { v = (float) parseDouble (s); }
inline void setFromString (bool& v, const String& s) { v = (s == "true"); }
inline void setFromString (String& v, const String& s) { v = s; }
inline void setFromString (Colour& v, const String& s) { v = Colour ((uint32) parseInt (s)); }
template <typename Type>
inline String getAsString (const Type& v, bool) { return String (v); }
inline String getAsString (char v, bool preferHex) { return intToString ((int) v, preferHex); }
inline String getAsString (unsigned char v, bool preferHex) { return intToString ((int) v, preferHex); }
inline String getAsString (short v, bool preferHex) { return intToString ((int) v, preferHex); }
inline String getAsString (unsigned short v, bool preferHex) { return intToString ((int) v, preferHex); }
inline String getAsString (int v, bool preferHex) { return intToString ((int) v, preferHex); }
inline String getAsString (unsigned int v, bool preferHex) { return intToString ((int) v, preferHex); }
inline String getAsString (bool v, bool) { return v ? "true" : "false"; }
inline String getAsString (int64 v, bool preferHex) { return intToString ((int64) v, preferHex); }
inline String getAsString (uint64 v, bool preferHex) { return intToString ((int64) v, preferHex); }
inline String getAsString (Colour v, bool) { return intToString ((int) v.getARGB(), true); }
template <typename Type> struct isStringType { enum { value = 0 }; };
template <> struct isStringType<String> { enum { value = 1 }; };
template <typename Type>
inline String getAsCode (Type& v, bool preferHex) { return getAsString (v, preferHex); }
inline String getAsCode (Colour v, bool) { return "Colour (0x" + String::toHexString ((int) v.getARGB()).paddedLeft ('0', 8) + ")"; }
inline String getAsCode (const String& v, bool) { return CppTokeniserFunctions::addEscapeChars(v).quoted(); }
inline String getAsCode (const char* v, bool) { return getAsCode (String (v), false); }
template <typename Type>
inline const char* castToCharPointer (const Type&) { return ""; }
inline const char* castToCharPointer (const String& s) { return s.toRawUTF8(); }
struct LivePropertyEditorBase;
//==============================================================================
struct JUCE_API LiveValueBase
{
LiveValueBase (const char* file, int line);
virtual ~LiveValueBase();
virtual LivePropertyEditorBase* createPropertyComponent (CodeDocument&) = 0;
virtual String getStringValue (bool preferHex) const = 0;
virtual String getCodeValue (bool preferHex) const = 0;
virtual void setStringValue (const String&) = 0;
virtual String getOriginalStringValue (bool preferHex) const = 0;
virtual bool isString() const = 0;
String name, sourceFile;
int sourceLine;
JUCE_DECLARE_NON_COPYABLE (LiveValueBase)
};
//==============================================================================
struct JUCE_API LivePropertyEditorBase : public Component
{
LivePropertyEditorBase (LiveValueBase&, CodeDocument&);
void paint (Graphics&) override;
void resized() override;
void applyNewValue (const String&);
void selectOriginalValue();
void findOriginalValueInCode();
LiveValueBase& value;
Label name;
TextEditor valueEditor;
TextButton resetButton { "reset" };
CodeDocument& document;
CPlusPlusCodeTokeniser tokeniser;
CodeEditorComponent sourceEditor;
CodeDocument::Position valueStart, valueEnd;
std::unique_ptr<Component> customComp;
bool wasHex = false;
JUCE_DECLARE_NON_COPYABLE (LivePropertyEditorBase)
};
//==============================================================================
Component* createColourEditor (LivePropertyEditorBase&);
Component* createIntegerSlider (LivePropertyEditorBase&);
Component* createFloatSlider (LivePropertyEditorBase&);
Component* createBoolSlider (LivePropertyEditorBase&);
template <typename Type> struct CustomEditor { static Component* create (LivePropertyEditorBase&) { return nullptr; } };
template <> struct CustomEditor<char> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<unsigned char> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<int> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<unsigned int> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<short> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<unsigned short> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<int64> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<uint64> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<float> { static Component* create (LivePropertyEditorBase& e) { return createFloatSlider (e); } };
template <> struct CustomEditor<double> { static Component* create (LivePropertyEditorBase& e) { return createFloatSlider (e); } };
template <> struct CustomEditor<Colour> { static Component* create (LivePropertyEditorBase& e) { return createColourEditor (e); } };
template <> struct CustomEditor<bool> { static Component* create (LivePropertyEditorBase& e) { return createBoolSlider (e); } };
template <typename Type>
struct LivePropertyEditor : public LivePropertyEditorBase
{
template <typename ValueType>
LivePropertyEditor (ValueType& v, CodeDocument& d) : LivePropertyEditorBase (v, d)
{
customComp.reset (CustomEditor<Type>::create (*this));
addAndMakeVisible (customComp.get());
}
};
//==============================================================================
template <typename Type>
struct LiveValue : public LiveValueBase
{
LiveValue (const char* file, int line, const Type& initialValue)
: LiveValueBase (file, line), value (initialValue), originalValue (initialValue)
{}
operator Type() const noexcept { return value; }
Type get() const noexcept { return value; }
operator const char*() const { return castToCharPointer (value); }
LivePropertyEditorBase* createPropertyComponent (CodeDocument& doc) override
{
return new LivePropertyEditor<Type> (*this, doc);
}
String getStringValue (bool preferHex) const override { return getAsString (value, preferHex); }
String getCodeValue (bool preferHex) const override { return getAsCode (value, preferHex); }
String getOriginalStringValue (bool preferHex) const override { return getAsString (originalValue, preferHex); }
void setStringValue (const String& s) override { setFromString (value, s); }
bool isString() const override { return isStringType<Type>::value; }
Type value, originalValue;
JUCE_DECLARE_NON_COPYABLE (LiveValue)
};
//==============================================================================
class JUCE_API ValueList : private AsyncUpdater,
private DeletedAtShutdown
{
public:
ValueList();
~ValueList() override;
JUCE_DECLARE_SINGLETON (ValueList, false)
template <typename Type>
LiveValue<Type>& getValue (const char* file, int line, const Type& initialValue)
{
const ScopedLock sl (lock);
using ValueType = LiveValue<Type>;
for (auto* v : values)
if (v->sourceLine == line && v->sourceFile == file)
return *static_cast<ValueType*> (v);
auto v = new ValueType (file, line, initialValue);
addValue (v);
return *v;
}
private:
OwnedArray<LiveValueBase> values;
OwnedArray<CodeDocument> documents;
Array<File> documentFiles;
class EditorWindow;
Component::SafePointer<EditorWindow> editorWindow;
CriticalSection lock;
CodeDocument& getDocument (const File&);
void addValue (LiveValueBase*);
void handleAsyncUpdate() override;
};
template <typename Type>
inline LiveValue<Type>& getValue (const char* file, int line, const Type& initialValue)
{
// If you hit this assertion then the __FILE__ macro is providing a
// relative path instead of an absolute path. On Windows this will be
// a path relative to the build directory rather than the currently
// running application. To fix this you must compile with the /FC flag.
jassert (File::isAbsolutePath (file));
return ValueList::getInstance()->getValue (file, line, initialValue);
}
inline LiveValue<String>& getValue (const char* file, int line, const char* initialValue)
{
return getValue (file, line, String (initialValue));
}
}
#endif
//==============================================================================
#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR || DOXYGEN
/**
This macro wraps a primitive constant value in some cunning boilerplate code that allows
its value to be interactively tweaked in a popup window while your application is running.
In a release build, this macro disappears and is replaced by only the constant that it
wraps, but if JUCE_ENABLE_LIVE_CONSTANT_EDITOR is enabled, it injects a class wrapper
that automatically pops-up a window containing an editor that allows the value to be
tweaked at run-time. The editor window will also force all visible components to be
resized and repainted whenever a value is changed, so that if you use this to wrap
a colour or layout parameter, you'll be able to immediately see the effects of changing it.
The editor will also load the original source-file that contains each JUCE_LIVE_CONSTANT
macro, and will display a preview of the modified source code as you adjust the values.
Things to note:
- Only one of these per line! The __FILE__ and __LINE__ macros are used to identify
the value, so things will get confused if you have more than one per line
- Obviously because it needs to load the source code based on the __FILE__ macro,
it'll only work if the source files are stored locally in the same location as they
were when you compiled the program.
- It's only designed to cope with simple types: primitives, string literals, and
the Colour class, so if you try using it for other classes or complex expressions,
good luck!
- The editor window will get popped up whenever a new value is used for the first
time. You can close the window, but there's no way to get it back without restarting
the app!
e.g. in this example the colours, font size, and text used in the paint method can
all be adjusted live:
@code
void MyComp::paint (Graphics& g) override
{
g.fillAll (JUCE_LIVE_CONSTANT (Colour (0xffddddff)));
Colour fontColour = JUCE_LIVE_CONSTANT (Colour (0xff005500));
float fontSize = JUCE_LIVE_CONSTANT (16.0f);
g.setColour (fontColour);
g.setFont (fontSize);
g.drawFittedText (JUCE_LIVE_CONSTANT ("Hello world!"),
getLocalBounds(), Justification::centred, 2);
}
@endcode
*/
#define JUCE_LIVE_CONSTANT(initialValue) \
(juce::LiveConstantEditor::getValue (__FILE__, __LINE__ - 1, initialValue).get())
#else
#define JUCE_LIVE_CONSTANT(initialValue) \
(initialValue)
#endif
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR && ! defined (DOXYGEN)
//==============================================================================
/** You can safely ignore all the stuff in this namespace - it's a bunch of boilerplate
code used to implement the JUCE_LIVE_CONSTANT functionality.
*/
namespace LiveConstantEditor
{
int64 parseInt (String);
double parseDouble (const String&);
String intToString (int, bool preferHex);
String intToString (int64, bool preferHex);
template <typename Type>
static void setFromString (Type& v, const String& s) { v = static_cast<Type> (s); }
inline void setFromString (char& v, const String& s) { v = (char) parseInt (s); }
inline void setFromString (unsigned char& v, const String& s) { v = (unsigned char) parseInt (s); }
inline void setFromString (short& v, const String& s) { v = (short) parseInt (s); }
inline void setFromString (unsigned short& v, const String& s) { v = (unsigned short) parseInt (s); }
inline void setFromString (int& v, const String& s) { v = (int) parseInt (s); }
inline void setFromString (unsigned int& v, const String& s) { v = (unsigned int) parseInt (s); }
inline void setFromString (long& v, const String& s) { v = (long) parseInt (s); }
inline void setFromString (unsigned long& v, const String& s) { v = (unsigned long) parseInt (s); }
inline void setFromString (int64& v, const String& s) { v = (int64) parseInt (s); }
inline void setFromString (uint64& v, const String& s) { v = (uint64) parseInt (s); }
inline void setFromString (double& v, const String& s) { v = parseDouble (s); }
inline void setFromString (float& v, const String& s) { v = (float) parseDouble (s); }
inline void setFromString (bool& v, const String& s) { v = (s == "true"); }
inline void setFromString (String& v, const String& s) { v = s; }
inline void setFromString (Colour& v, const String& s) { v = Colour ((uint32) parseInt (s)); }
template <typename Type>
inline String getAsString (const Type& v, bool) { return String (v); }
inline String getAsString (char v, bool preferHex) { return intToString ((int) v, preferHex); }
inline String getAsString (unsigned char v, bool preferHex) { return intToString ((int) v, preferHex); }
inline String getAsString (short v, bool preferHex) { return intToString ((int) v, preferHex); }
inline String getAsString (unsigned short v, bool preferHex) { return intToString ((int) v, preferHex); }
inline String getAsString (int v, bool preferHex) { return intToString ((int) v, preferHex); }
inline String getAsString (unsigned int v, bool preferHex) { return intToString ((int) v, preferHex); }
inline String getAsString (bool v, bool) { return v ? "true" : "false"; }
inline String getAsString (int64 v, bool preferHex) { return intToString ((int64) v, preferHex); }
inline String getAsString (uint64 v, bool preferHex) { return intToString ((int64) v, preferHex); }
inline String getAsString (Colour v, bool) { return intToString ((int) v.getARGB(), true); }
template <typename Type> struct isStringType { enum { value = 0 }; };
template <> struct isStringType<String> { enum { value = 1 }; };
template <typename Type>
inline String getAsCode (Type& v, bool preferHex) { return getAsString (v, preferHex); }
inline String getAsCode (Colour v, bool) { return "Colour (0x" + String::toHexString ((int) v.getARGB()).paddedLeft ('0', 8) + ")"; }
inline String getAsCode (const String& v, bool) { return CppTokeniserFunctions::addEscapeChars(v).quoted(); }
inline String getAsCode (const char* v, bool) { return getAsCode (String (v), false); }
template <typename Type>
inline const char* castToCharPointer (const Type&) { return ""; }
inline const char* castToCharPointer (const String& s) { return s.toRawUTF8(); }
struct LivePropertyEditorBase;
//==============================================================================
struct JUCE_API LiveValueBase
{
LiveValueBase (const char* file, int line);
virtual ~LiveValueBase();
virtual LivePropertyEditorBase* createPropertyComponent (CodeDocument&) = 0;
virtual String getStringValue (bool preferHex) const = 0;
virtual String getCodeValue (bool preferHex) const = 0;
virtual void setStringValue (const String&) = 0;
virtual String getOriginalStringValue (bool preferHex) const = 0;
virtual bool isString() const = 0;
String name, sourceFile;
int sourceLine;
JUCE_DECLARE_NON_COPYABLE (LiveValueBase)
};
//==============================================================================
struct JUCE_API LivePropertyEditorBase : public Component
{
LivePropertyEditorBase (LiveValueBase&, CodeDocument&);
void paint (Graphics&) override;
void resized() override;
void applyNewValue (const String&);
void selectOriginalValue();
void findOriginalValueInCode();
LiveValueBase& value;
Label name;
TextEditor valueEditor;
TextButton resetButton { "reset" };
CodeDocument& document;
CPlusPlusCodeTokeniser tokeniser;
CodeEditorComponent sourceEditor;
CodeDocument::Position valueStart, valueEnd;
std::unique_ptr<Component> customComp;
bool wasHex = false;
JUCE_DECLARE_NON_COPYABLE (LivePropertyEditorBase)
};
//==============================================================================
Component* createColourEditor (LivePropertyEditorBase&);
Component* createIntegerSlider (LivePropertyEditorBase&);
Component* createFloatSlider (LivePropertyEditorBase&);
Component* createBoolSlider (LivePropertyEditorBase&);
template <typename Type> struct CustomEditor { static Component* create (LivePropertyEditorBase&) { return nullptr; } };
template <> struct CustomEditor<char> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<unsigned char> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<int> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<unsigned int> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<short> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<unsigned short> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<int64> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<uint64> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
template <> struct CustomEditor<float> { static Component* create (LivePropertyEditorBase& e) { return createFloatSlider (e); } };
template <> struct CustomEditor<double> { static Component* create (LivePropertyEditorBase& e) { return createFloatSlider (e); } };
template <> struct CustomEditor<Colour> { static Component* create (LivePropertyEditorBase& e) { return createColourEditor (e); } };
template <> struct CustomEditor<bool> { static Component* create (LivePropertyEditorBase& e) { return createBoolSlider (e); } };
template <typename Type>
struct LivePropertyEditor : public LivePropertyEditorBase
{
template <typename ValueType>
LivePropertyEditor (ValueType& v, CodeDocument& d) : LivePropertyEditorBase (v, d)
{
customComp.reset (CustomEditor<Type>::create (*this));
addAndMakeVisible (customComp.get());
}
};
//==============================================================================
template <typename Type>
struct LiveValue : public LiveValueBase
{
LiveValue (const char* file, int line, const Type& initialValue)
: LiveValueBase (file, line), value (initialValue), originalValue (initialValue)
{}
operator Type() const noexcept { return value; }
Type get() const noexcept { return value; }
operator const char*() const { return castToCharPointer (value); }
LivePropertyEditorBase* createPropertyComponent (CodeDocument& doc) override
{
return new LivePropertyEditor<Type> (*this, doc);
}
String getStringValue (bool preferHex) const override { return getAsString (value, preferHex); }
String getCodeValue (bool preferHex) const override { return getAsCode (value, preferHex); }
String getOriginalStringValue (bool preferHex) const override { return getAsString (originalValue, preferHex); }
void setStringValue (const String& s) override { setFromString (value, s); }
bool isString() const override { return isStringType<Type>::value; }
Type value, originalValue;
JUCE_DECLARE_NON_COPYABLE (LiveValue)
};
//==============================================================================
class JUCE_API ValueList : private AsyncUpdater,
private DeletedAtShutdown
{
public:
ValueList();
~ValueList() override;
JUCE_DECLARE_SINGLETON (ValueList, false)
template <typename Type>
LiveValue<Type>& getValue (const char* file, int line, const Type& initialValue)
{
const ScopedLock sl (lock);
using ValueType = LiveValue<Type>;
for (auto* v : values)
if (v->sourceLine == line && v->sourceFile == file)
return *static_cast<ValueType*> (v);
auto v = new ValueType (file, line, initialValue);
addValue (v);
return *v;
}
private:
OwnedArray<LiveValueBase> values;
OwnedArray<CodeDocument> documents;
Array<File> documentFiles;
class EditorWindow;
Component::SafePointer<EditorWindow> editorWindow;
CriticalSection lock;
CodeDocument& getDocument (const File&);
void addValue (LiveValueBase*);
void handleAsyncUpdate() override;
};
template <typename Type>
inline LiveValue<Type>& getValue (const char* file, int line, const Type& initialValue)
{
// If you hit this assertion then the __FILE__ macro is providing a
// relative path instead of an absolute path. On Windows this will be
// a path relative to the build directory rather than the currently
// running application. To fix this you must compile with the /FC flag.
jassert (File::isAbsolutePath (file));
return ValueList::getInstance()->getValue (file, line, initialValue);
}
inline LiveValue<String>& getValue (const char* file, int line, const char* initialValue)
{
return getValue (file, line, String (initialValue));
}
}
#endif
//==============================================================================
#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR || DOXYGEN
/**
This macro wraps a primitive constant value in some cunning boilerplate code that allows
its value to be interactively tweaked in a popup window while your application is running.
In a release build, this macro disappears and is replaced by only the constant that it
wraps, but if JUCE_ENABLE_LIVE_CONSTANT_EDITOR is enabled, it injects a class wrapper
that automatically pops-up a window containing an editor that allows the value to be
tweaked at run-time. The editor window will also force all visible components to be
resized and repainted whenever a value is changed, so that if you use this to wrap
a colour or layout parameter, you'll be able to immediately see the effects of changing it.
The editor will also load the original source-file that contains each JUCE_LIVE_CONSTANT
macro, and will display a preview of the modified source code as you adjust the values.
Things to note:
- Only one of these per line! The __FILE__ and __LINE__ macros are used to identify
the value, so things will get confused if you have more than one per line
- Obviously because it needs to load the source code based on the __FILE__ macro,
it'll only work if the source files are stored locally in the same location as they
were when you compiled the program.
- It's only designed to cope with simple types: primitives, string literals, and
the Colour class, so if you try using it for other classes or complex expressions,
good luck!
- The editor window will get popped up whenever a new value is used for the first
time. You can close the window, but there's no way to get it back without restarting
the app!
e.g. in this example the colours, font size, and text used in the paint method can
all be adjusted live:
@code
void MyComp::paint (Graphics& g) override
{
g.fillAll (JUCE_LIVE_CONSTANT (Colour (0xffddddff)));
Colour fontColour = JUCE_LIVE_CONSTANT (Colour (0xff005500));
float fontSize = JUCE_LIVE_CONSTANT (16.0f);
g.setColour (fontColour);
g.setFont (fontSize);
g.drawFittedText (JUCE_LIVE_CONSTANT ("Hello world!"),
getLocalBounds(), Justification::centred, 2);
}
@endcode
*/
#define JUCE_LIVE_CONSTANT(initialValue) \
(juce::LiveConstantEditor::getValue (__FILE__, __LINE__ - 1, initialValue).get())
#else
#define JUCE_LIVE_CONSTANT(initialValue) \
(initialValue)
#endif
} // namespace juce

View File

@ -1,156 +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
{
PreferencesPanel::PreferencesPanel()
: buttonSize (70)
{
}
PreferencesPanel::~PreferencesPanel()
{
}
int PreferencesPanel::getButtonSize() const noexcept
{
return buttonSize;
}
void PreferencesPanel::setButtonSize (int newSize)
{
buttonSize = newSize;
resized();
}
//==============================================================================
void PreferencesPanel::addSettingsPage (const String& title,
const Drawable* icon,
const Drawable* overIcon,
const Drawable* downIcon)
{
auto* button = new DrawableButton (title, DrawableButton::ImageAboveTextLabel);
buttons.add (button);
button->setImages (icon, overIcon, downIcon);
button->setRadioGroupId (1);
button->onClick = [this] { clickedPage(); };
button->setClickingTogglesState (true);
button->setWantsKeyboardFocus (false);
addAndMakeVisible (button);
resized();
if (currentPage == nullptr)
setCurrentPage (title);
}
void PreferencesPanel::addSettingsPage (const String& title, const void* imageData, int imageDataSize)
{
DrawableImage icon, iconOver, iconDown;
icon.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
iconOver.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
iconOver.setOverlayColour (Colours::black.withAlpha (0.12f));
iconDown.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
iconDown.setOverlayColour (Colours::black.withAlpha (0.25f));
addSettingsPage (title, &icon, &iconOver, &iconDown);
}
//==============================================================================
void PreferencesPanel::showInDialogBox (const String& dialogTitle, int dialogWidth, int dialogHeight, Colour backgroundColour)
{
setSize (dialogWidth, dialogHeight);
DialogWindow::LaunchOptions o;
o.content.setNonOwned (this);
o.dialogTitle = dialogTitle;
o.dialogBackgroundColour = backgroundColour;
o.escapeKeyTriggersCloseButton = false;
o.useNativeTitleBar = false;
o.resizable = false;
o.launchAsync();
}
//==============================================================================
void PreferencesPanel::resized()
{
for (int i = 0; i < buttons.size(); ++i)
buttons.getUnchecked(i)->setBounds (i * buttonSize, 0, buttonSize, buttonSize);
if (currentPage != nullptr)
currentPage->setBounds (getLocalBounds().withTop (buttonSize + 5));
}
void PreferencesPanel::paint (Graphics& g)
{
g.setColour (Colours::grey);
g.fillRect (0, buttonSize + 2, getWidth(), 1);
}
void PreferencesPanel::setCurrentPage (const String& pageName)
{
if (currentPageName != pageName)
{
currentPageName = pageName;
currentPage.reset();
currentPage.reset (createComponentForPage (pageName));
if (currentPage != nullptr)
{
addAndMakeVisible (currentPage.get());
currentPage->toBack();
resized();
}
for (auto* b : buttons)
{
if (b->getName() == pageName)
{
b->setToggleState (true, dontSendNotification);
break;
}
}
}
}
void PreferencesPanel::clickedPage()
{
for (auto* b : buttons)
{
if (b->getToggleState())
{
setCurrentPage (b->getName());
break;
}
}
}
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
PreferencesPanel::PreferencesPanel()
: buttonSize (70)
{
}
PreferencesPanel::~PreferencesPanel()
{
}
int PreferencesPanel::getButtonSize() const noexcept
{
return buttonSize;
}
void PreferencesPanel::setButtonSize (int newSize)
{
buttonSize = newSize;
resized();
}
//==============================================================================
void PreferencesPanel::addSettingsPage (const String& title,
const Drawable* icon,
const Drawable* overIcon,
const Drawable* downIcon)
{
auto* button = new DrawableButton (title, DrawableButton::ImageAboveTextLabel);
buttons.add (button);
button->setImages (icon, overIcon, downIcon);
button->setRadioGroupId (1);
button->onClick = [this] { clickedPage(); };
button->setClickingTogglesState (true);
button->setWantsKeyboardFocus (false);
addAndMakeVisible (button);
resized();
if (currentPage == nullptr)
setCurrentPage (title);
}
void PreferencesPanel::addSettingsPage (const String& title, const void* imageData, int imageDataSize)
{
DrawableImage icon, iconOver, iconDown;
icon.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
iconOver.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
iconOver.setOverlayColour (Colours::black.withAlpha (0.12f));
iconDown.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
iconDown.setOverlayColour (Colours::black.withAlpha (0.25f));
addSettingsPage (title, &icon, &iconOver, &iconDown);
}
//==============================================================================
void PreferencesPanel::showInDialogBox (const String& dialogTitle, int dialogWidth, int dialogHeight, Colour backgroundColour)
{
setSize (dialogWidth, dialogHeight);
DialogWindow::LaunchOptions o;
o.content.setNonOwned (this);
o.dialogTitle = dialogTitle;
o.dialogBackgroundColour = backgroundColour;
o.escapeKeyTriggersCloseButton = false;
o.useNativeTitleBar = false;
o.resizable = false;
o.launchAsync();
}
//==============================================================================
void PreferencesPanel::resized()
{
for (int i = 0; i < buttons.size(); ++i)
buttons.getUnchecked(i)->setBounds (i * buttonSize, 0, buttonSize, buttonSize);
if (currentPage != nullptr)
currentPage->setBounds (getLocalBounds().withTop (buttonSize + 5));
}
void PreferencesPanel::paint (Graphics& g)
{
g.setColour (Colours::grey);
g.fillRect (0, buttonSize + 2, getWidth(), 1);
}
void PreferencesPanel::setCurrentPage (const String& pageName)
{
if (currentPageName != pageName)
{
currentPageName = pageName;
currentPage.reset();
currentPage.reset (createComponentForPage (pageName));
if (currentPage != nullptr)
{
addAndMakeVisible (currentPage.get());
currentPage->toBack();
resized();
}
for (auto* b : buttons)
{
if (b->getName() == pageName)
{
b->setToggleState (true, dontSendNotification);
break;
}
}
}
}
void PreferencesPanel::clickedPage()
{
for (auto* b : buttons)
{
if (b->getToggleState())
{
setCurrentPage (b->getName());
break;
}
}
}
} // namespace juce

View File

@ -1,147 +1,147 @@
/*
==============================================================================
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 with a set of buttons at the top for changing between pages of
preferences.
This is just a handy way of writing a Mac-style preferences panel where you
have a row of buttons along the top for the different preference categories,
each button having an icon above its name. Clicking these will show an
appropriate prefs page below it.
You can either put one of these inside your own component, or just use the
showInDialogBox() method to show it in a window and run it modally.
To use it, just add a set of named pages with the addSettingsPage() method,
and implement the createComponentForPage() method to create suitable components
for each of these pages.
@tags{GUI}
*/
class JUCE_API PreferencesPanel : public Component
{
public:
//==============================================================================
/** Creates an empty panel.
Use addSettingsPage() to add some pages to it in your constructor.
*/
PreferencesPanel();
/** Destructor. */
~PreferencesPanel() override;
//==============================================================================
/** Creates a page using a set of drawables to define the page's icon.
Note that the other version of this method is much easier if you're using
an image instead of a custom drawable.
@param pageTitle the name of this preferences page - you'll need to
make sure your createComponentForPage() method creates
a suitable component when it is passed this name
@param normalIcon the drawable to display in the page's button normally
@param overIcon the drawable to display in the page's button when the mouse is over
@param downIcon the drawable to display in the page's button when the button is down
@see DrawableButton
*/
void addSettingsPage (const String& pageTitle,
const Drawable* normalIcon,
const Drawable* overIcon,
const Drawable* downIcon);
/** Creates a page using a set of drawables to define the page's icon.
The other version of this method gives you more control over the icon, but this
one is much easier if you're just loading it from a file.
@param pageTitle the name of this preferences page - you'll need to
make sure your createComponentForPage() method creates
a suitable component when it is passed this name
@param imageData a block of data containing an image file, e.g. a jpeg, png or gif.
For this to look good, you'll probably want to use a nice
transparent png file.
@param imageDataSize the size of the image data, in bytes
*/
void addSettingsPage (const String& pageTitle,
const void* imageData,
int imageDataSize);
/** Utility method to display this panel in a DialogWindow.
Calling this will create a DialogWindow containing this panel with the
given size and title, and will run it modally, returning when the user
closes the dialog box.
*/
void showInDialogBox (const String& dialogTitle,
int dialogWidth,
int dialogHeight,
Colour backgroundColour = Colours::white);
//==============================================================================
/** Subclasses must override this to return a component for each preferences page.
The subclass should return a pointer to a new component representing the named
page, which the panel will then display.
The panel will delete the component later when the user goes to another page
or deletes the panel.
*/
virtual Component* createComponentForPage (const String& pageName) = 0;
//==============================================================================
/** Changes the current page being displayed. */
void setCurrentPage (const String& pageName);
/** Returns the size of the buttons shown along the top. */
int getButtonSize() const noexcept;
/** Changes the size of the buttons shown along the top. */
void setButtonSize (int newSize);
//==============================================================================
/** @internal */
void resized() override;
/** @internal */
void paint (Graphics&) override;
private:
//==============================================================================
String currentPageName;
std::unique_ptr<Component> currentPage;
OwnedArray<DrawableButton> buttons;
int buttonSize;
void clickedPage();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PreferencesPanel)
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A component with a set of buttons at the top for changing between pages of
preferences.
This is just a handy way of writing a Mac-style preferences panel where you
have a row of buttons along the top for the different preference categories,
each button having an icon above its name. Clicking these will show an
appropriate prefs page below it.
You can either put one of these inside your own component, or just use the
showInDialogBox() method to show it in a window and run it modally.
To use it, just add a set of named pages with the addSettingsPage() method,
and implement the createComponentForPage() method to create suitable components
for each of these pages.
@tags{GUI}
*/
class JUCE_API PreferencesPanel : public Component
{
public:
//==============================================================================
/** Creates an empty panel.
Use addSettingsPage() to add some pages to it in your constructor.
*/
PreferencesPanel();
/** Destructor. */
~PreferencesPanel() override;
//==============================================================================
/** Creates a page using a set of drawables to define the page's icon.
Note that the other version of this method is much easier if you're using
an image instead of a custom drawable.
@param pageTitle the name of this preferences page - you'll need to
make sure your createComponentForPage() method creates
a suitable component when it is passed this name
@param normalIcon the drawable to display in the page's button normally
@param overIcon the drawable to display in the page's button when the mouse is over
@param downIcon the drawable to display in the page's button when the button is down
@see DrawableButton
*/
void addSettingsPage (const String& pageTitle,
const Drawable* normalIcon,
const Drawable* overIcon,
const Drawable* downIcon);
/** Creates a page using a set of drawables to define the page's icon.
The other version of this method gives you more control over the icon, but this
one is much easier if you're just loading it from a file.
@param pageTitle the name of this preferences page - you'll need to
make sure your createComponentForPage() method creates
a suitable component when it is passed this name
@param imageData a block of data containing an image file, e.g. a jpeg, png or gif.
For this to look good, you'll probably want to use a nice
transparent png file.
@param imageDataSize the size of the image data, in bytes
*/
void addSettingsPage (const String& pageTitle,
const void* imageData,
int imageDataSize);
/** Utility method to display this panel in a DialogWindow.
Calling this will create a DialogWindow containing this panel with the
given size and title, and will run it modally, returning when the user
closes the dialog box.
*/
void showInDialogBox (const String& dialogTitle,
int dialogWidth,
int dialogHeight,
Colour backgroundColour = Colours::white);
//==============================================================================
/** Subclasses must override this to return a component for each preferences page.
The subclass should return a pointer to a new component representing the named
page, which the panel will then display.
The panel will delete the component later when the user goes to another page
or deletes the panel.
*/
virtual Component* createComponentForPage (const String& pageName) = 0;
//==============================================================================
/** Changes the current page being displayed. */
void setCurrentPage (const String& pageName);
/** Returns the size of the buttons shown along the top. */
int getButtonSize() const noexcept;
/** Changes the size of the buttons shown along the top. */
void setButtonSize (int newSize);
//==============================================================================
/** @internal */
void resized() override;
/** @internal */
void paint (Graphics&) override;
private:
//==============================================================================
String currentPageName;
std::unique_ptr<Component> currentPage;
OwnedArray<DrawableButton> buttons;
int buttonSize;
void clickedPage();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PreferencesPanel)
};
} // namespace juce

View File

@ -1,229 +1,229 @@
/*
==============================================================================
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_ANDROID && ! JUCE_IOS && ! JUCE_MAC
bool PushNotifications::Notification::isValid() const noexcept { return true; }
#endif
PushNotifications::Notification::Notification (const Notification& other)
: identifier (other.identifier),
title (other.title),
body (other.body),
subtitle (other.subtitle),
groupId (other.groupId),
badgeNumber (other.badgeNumber),
soundToPlay (other.soundToPlay),
properties (other.properties),
category (other.category),
triggerIntervalSec (other.triggerIntervalSec),
repeat (other.repeat),
icon (other.icon),
channelId (other.channelId),
largeIcon (other.largeIcon),
tickerText (other.tickerText),
actions (other.actions),
progress (other.progress),
person (other.person),
type (other.type),
priority (other.priority),
lockScreenAppearance (other.lockScreenAppearance),
publicVersion (other.publicVersion.get() != nullptr ? new Notification (*other.publicVersion) : nullptr),
groupSortKey (other.groupSortKey),
groupSummary (other.groupSummary),
accentColour (other.accentColour),
ledColour (other.ledColour),
ledBlinkPattern (other.ledBlinkPattern),
vibrationPattern (other.vibrationPattern),
shouldAutoCancel (other.shouldAutoCancel),
localOnly (other.localOnly),
ongoing (other.ongoing),
alertOnlyOnce (other.alertOnlyOnce),
timestampVisibility (other.timestampVisibility),
badgeIconType (other.badgeIconType),
groupAlertBehaviour (other.groupAlertBehaviour),
timeoutAfterMs (other.timeoutAfterMs)
{
}
//==============================================================================
JUCE_IMPLEMENT_SINGLETON (PushNotifications)
PushNotifications::PushNotifications()
#if JUCE_PUSH_NOTIFICATIONS
: pimpl (new Pimpl (*this))
#endif
{
}
PushNotifications::~PushNotifications() { clearSingletonInstance(); }
void PushNotifications::addListener (Listener* l) { listeners.add (l); }
void PushNotifications::removeListener (Listener* l) { listeners.remove (l); }
void PushNotifications::requestPermissionsWithSettings (const PushNotifications::Settings& settings)
{
#if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC)
pimpl->requestPermissionsWithSettings (settings);
#else
ignoreUnused (settings);
listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
#endif
}
void PushNotifications::requestSettingsUsed()
{
#if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC)
pimpl->requestSettingsUsed();
#else
listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
#endif
}
bool PushNotifications::areNotificationsEnabled() const
{
#if JUCE_PUSH_NOTIFICATIONS
return pimpl->areNotificationsEnabled();
#else
return false;
#endif
}
void PushNotifications::getDeliveredNotifications() const
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->getDeliveredNotifications();
#endif
}
void PushNotifications::removeAllDeliveredNotifications()
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->removeAllDeliveredNotifications();
#endif
}
String PushNotifications::getDeviceToken() const
{
#if JUCE_PUSH_NOTIFICATIONS
return pimpl->getDeviceToken();
#else
return {};
#endif
}
void PushNotifications::setupChannels (const Array<ChannelGroup>& groups, const Array<Channel>& channels)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->setupChannels (groups, channels);
#else
ignoreUnused (groups, channels);
#endif
}
void PushNotifications::getPendingLocalNotifications() const
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->getPendingLocalNotifications();
#endif
}
void PushNotifications::removeAllPendingLocalNotifications()
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->removeAllPendingLocalNotifications();
#endif
}
void PushNotifications::subscribeToTopic (const String& topic)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->subscribeToTopic (topic);
#else
ignoreUnused (topic);
#endif
}
void PushNotifications::unsubscribeFromTopic (const String& topic)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->unsubscribeFromTopic (topic);
#else
ignoreUnused (topic);
#endif
}
void PushNotifications::sendLocalNotification (const Notification& n)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->sendLocalNotification (n);
#else
ignoreUnused (n);
#endif
}
void PushNotifications::removeDeliveredNotification (const String& identifier)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->removeDeliveredNotification (identifier);
#else
ignoreUnused (identifier);
#endif
}
void PushNotifications::removePendingLocalNotification (const String& identifier)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->removePendingLocalNotification (identifier);
#else
ignoreUnused (identifier);
#endif
}
void PushNotifications::sendUpstreamMessage (const String& serverSenderId,
const String& collapseKey,
const String& messageId,
const String& messageType,
int timeToLive,
const StringPairArray& additionalData)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->sendUpstreamMessage (serverSenderId,
collapseKey,
messageId,
messageType,
timeToLive,
additionalData);
#else
ignoreUnused (serverSenderId, collapseKey, messageId, messageType);
ignoreUnused (timeToLive, additionalData);
#endif
}
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
#if ! JUCE_ANDROID && ! JUCE_IOS && ! JUCE_MAC
bool PushNotifications::Notification::isValid() const noexcept { return true; }
#endif
PushNotifications::Notification::Notification (const Notification& other)
: identifier (other.identifier),
title (other.title),
body (other.body),
subtitle (other.subtitle),
groupId (other.groupId),
badgeNumber (other.badgeNumber),
soundToPlay (other.soundToPlay),
properties (other.properties),
category (other.category),
triggerIntervalSec (other.triggerIntervalSec),
repeat (other.repeat),
icon (other.icon),
channelId (other.channelId),
largeIcon (other.largeIcon),
tickerText (other.tickerText),
actions (other.actions),
progress (other.progress),
person (other.person),
type (other.type),
priority (other.priority),
lockScreenAppearance (other.lockScreenAppearance),
publicVersion (other.publicVersion.get() != nullptr ? new Notification (*other.publicVersion) : nullptr),
groupSortKey (other.groupSortKey),
groupSummary (other.groupSummary),
accentColour (other.accentColour),
ledColour (other.ledColour),
ledBlinkPattern (other.ledBlinkPattern),
vibrationPattern (other.vibrationPattern),
shouldAutoCancel (other.shouldAutoCancel),
localOnly (other.localOnly),
ongoing (other.ongoing),
alertOnlyOnce (other.alertOnlyOnce),
timestampVisibility (other.timestampVisibility),
badgeIconType (other.badgeIconType),
groupAlertBehaviour (other.groupAlertBehaviour),
timeoutAfterMs (other.timeoutAfterMs)
{
}
//==============================================================================
JUCE_IMPLEMENT_SINGLETON (PushNotifications)
PushNotifications::PushNotifications()
#if JUCE_PUSH_NOTIFICATIONS
: pimpl (new Pimpl (*this))
#endif
{
}
PushNotifications::~PushNotifications() { clearSingletonInstance(); }
void PushNotifications::addListener (Listener* l) { listeners.add (l); }
void PushNotifications::removeListener (Listener* l) { listeners.remove (l); }
void PushNotifications::requestPermissionsWithSettings (const PushNotifications::Settings& settings)
{
#if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC)
pimpl->requestPermissionsWithSettings (settings);
#else
ignoreUnused (settings);
listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
#endif
}
void PushNotifications::requestSettingsUsed()
{
#if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC)
pimpl->requestSettingsUsed();
#else
listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
#endif
}
bool PushNotifications::areNotificationsEnabled() const
{
#if JUCE_PUSH_NOTIFICATIONS
return pimpl->areNotificationsEnabled();
#else
return false;
#endif
}
void PushNotifications::getDeliveredNotifications() const
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->getDeliveredNotifications();
#endif
}
void PushNotifications::removeAllDeliveredNotifications()
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->removeAllDeliveredNotifications();
#endif
}
String PushNotifications::getDeviceToken() const
{
#if JUCE_PUSH_NOTIFICATIONS
return pimpl->getDeviceToken();
#else
return {};
#endif
}
void PushNotifications::setupChannels (const Array<ChannelGroup>& groups, const Array<Channel>& channels)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->setupChannels (groups, channels);
#else
ignoreUnused (groups, channels);
#endif
}
void PushNotifications::getPendingLocalNotifications() const
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->getPendingLocalNotifications();
#endif
}
void PushNotifications::removeAllPendingLocalNotifications()
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->removeAllPendingLocalNotifications();
#endif
}
void PushNotifications::subscribeToTopic (const String& topic)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->subscribeToTopic (topic);
#else
ignoreUnused (topic);
#endif
}
void PushNotifications::unsubscribeFromTopic (const String& topic)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->unsubscribeFromTopic (topic);
#else
ignoreUnused (topic);
#endif
}
void PushNotifications::sendLocalNotification (const Notification& n)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->sendLocalNotification (n);
#else
ignoreUnused (n);
#endif
}
void PushNotifications::removeDeliveredNotification (const String& identifier)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->removeDeliveredNotification (identifier);
#else
ignoreUnused (identifier);
#endif
}
void PushNotifications::removePendingLocalNotification (const String& identifier)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->removePendingLocalNotification (identifier);
#else
ignoreUnused (identifier);
#endif
}
void PushNotifications::sendUpstreamMessage (const String& serverSenderId,
const String& collapseKey,
const String& messageId,
const String& messageType,
int timeToLive,
const StringPairArray& additionalData)
{
#if JUCE_PUSH_NOTIFICATIONS
pimpl->sendUpstreamMessage (serverSenderId,
collapseKey,
messageId,
messageType,
timeToLive,
additionalData);
#else
ignoreUnused (serverSenderId, collapseKey, messageId, messageType);
ignoreUnused (timeToLive, additionalData);
#endif
}
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -1,188 +1,184 @@
/*
==============================================================================
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
{
RecentlyOpenedFilesList::RecentlyOpenedFilesList()
: maxNumberOfItems (10)
{
}
RecentlyOpenedFilesList::~RecentlyOpenedFilesList()
{
}
//==============================================================================
void RecentlyOpenedFilesList::setMaxNumberOfItems (const int newMaxNumber)
{
maxNumberOfItems = jmax (1, newMaxNumber);
files.removeRange (maxNumberOfItems, getNumFiles());
}
int RecentlyOpenedFilesList::getNumFiles() const
{
return files.size();
}
File RecentlyOpenedFilesList::getFile (const int index) const
{
return File (files [index]);
}
void RecentlyOpenedFilesList::clear()
{
files.clear();
}
void RecentlyOpenedFilesList::addFile (const File& file)
{
removeFile (file);
files.insert (0, file.getFullPathName());
setMaxNumberOfItems (maxNumberOfItems);
}
void RecentlyOpenedFilesList::removeFile (const File& file)
{
files.removeString (file.getFullPathName());
}
void RecentlyOpenedFilesList::removeNonExistentFiles()
{
for (int i = getNumFiles(); --i >= 0;)
if (! getFile(i).exists())
files.remove (i);
}
//==============================================================================
int RecentlyOpenedFilesList::createPopupMenuItems (PopupMenu& menuToAddTo,
const int baseItemId,
const bool showFullPaths,
const bool dontAddNonExistentFiles,
const File** filesToAvoid)
{
int num = 0;
for (int i = 0; i < getNumFiles(); ++i)
{
const File f (getFile(i));
if ((! dontAddNonExistentFiles) || f.exists())
{
bool needsAvoiding = false;
if (filesToAvoid != nullptr)
{
for (const File** avoid = filesToAvoid; *avoid != nullptr; ++avoid)
{
if (f == **avoid)
{
needsAvoiding = true;
break;
}
}
}
if (! needsAvoiding)
{
menuToAddTo.addItem (baseItemId + i,
showFullPaths ? f.getFullPathName()
: f.getFileName());
++num;
}
}
}
return num;
}
//==============================================================================
String RecentlyOpenedFilesList::toString() const
{
return files.joinIntoString ("\n");
}
void RecentlyOpenedFilesList::restoreFromString (const String& stringifiedVersion)
{
clear();
files.addLines (stringifiedVersion);
setMaxNumberOfItems (maxNumberOfItems);
}
//==============================================================================
void RecentlyOpenedFilesList::registerRecentFileNatively (const File& file)
{
#if JUCE_MAC
JUCE_AUTORELEASEPOOL
{
[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL: createNSURLFromFile (file)];
}
#else
ignoreUnused (file);
#endif
}
void RecentlyOpenedFilesList::forgetRecentFileNatively (const File& file)
{
#if JUCE_MAC
JUCE_AUTORELEASEPOOL
{
// for some reason, OSX doesn't provide a method to just remove a single file
// from the recent list, so we clear them all and add them back excluding
// the specified file
auto sharedDocController = [NSDocumentController sharedDocumentController];
auto recentDocumentURLs = [sharedDocController recentDocumentURLs];
[sharedDocController clearRecentDocuments: nil];
auto* nsFile = createNSURLFromFile (file);
auto reverseEnumerator = [recentDocumentURLs reverseObjectEnumerator];
for (NSURL* url : reverseEnumerator)
if (! [url isEqual:nsFile])
[sharedDocController noteNewRecentDocumentURL:url];
}
#else
ignoreUnused (file);
#endif
}
void RecentlyOpenedFilesList::clearRecentFilesNatively()
{
#if JUCE_MAC
JUCE_AUTORELEASEPOOL
{
[[NSDocumentController sharedDocumentController] clearRecentDocuments: nil];
}
#endif
}
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
RecentlyOpenedFilesList::RecentlyOpenedFilesList()
: maxNumberOfItems (10)
{
}
//==============================================================================
void RecentlyOpenedFilesList::setMaxNumberOfItems (const int newMaxNumber)
{
maxNumberOfItems = jmax (1, newMaxNumber);
files.removeRange (maxNumberOfItems, getNumFiles());
}
int RecentlyOpenedFilesList::getNumFiles() const
{
return files.size();
}
File RecentlyOpenedFilesList::getFile (const int index) const
{
return File (files [index]);
}
void RecentlyOpenedFilesList::clear()
{
files.clear();
}
void RecentlyOpenedFilesList::addFile (const File& file)
{
removeFile (file);
files.insert (0, file.getFullPathName());
setMaxNumberOfItems (maxNumberOfItems);
}
void RecentlyOpenedFilesList::removeFile (const File& file)
{
files.removeString (file.getFullPathName());
}
void RecentlyOpenedFilesList::removeNonExistentFiles()
{
for (int i = getNumFiles(); --i >= 0;)
if (! getFile(i).exists())
files.remove (i);
}
//==============================================================================
int RecentlyOpenedFilesList::createPopupMenuItems (PopupMenu& menuToAddTo,
const int baseItemId,
const bool showFullPaths,
const bool dontAddNonExistentFiles,
const File** filesToAvoid)
{
int num = 0;
for (int i = 0; i < getNumFiles(); ++i)
{
const File f (getFile(i));
if ((! dontAddNonExistentFiles) || f.exists())
{
bool needsAvoiding = false;
if (filesToAvoid != nullptr)
{
for (const File** avoid = filesToAvoid; *avoid != nullptr; ++avoid)
{
if (f == **avoid)
{
needsAvoiding = true;
break;
}
}
}
if (! needsAvoiding)
{
menuToAddTo.addItem (baseItemId + i,
showFullPaths ? f.getFullPathName()
: f.getFileName());
++num;
}
}
}
return num;
}
//==============================================================================
String RecentlyOpenedFilesList::toString() const
{
return files.joinIntoString ("\n");
}
void RecentlyOpenedFilesList::restoreFromString (const String& stringifiedVersion)
{
clear();
files.addLines (stringifiedVersion);
setMaxNumberOfItems (maxNumberOfItems);
}
//==============================================================================
void RecentlyOpenedFilesList::registerRecentFileNatively (const File& file)
{
#if JUCE_MAC
JUCE_AUTORELEASEPOOL
{
[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL: createNSURLFromFile (file)];
}
#else
ignoreUnused (file);
#endif
}
void RecentlyOpenedFilesList::forgetRecentFileNatively (const File& file)
{
#if JUCE_MAC
JUCE_AUTORELEASEPOOL
{
// for some reason, OSX doesn't provide a method to just remove a single file
// from the recent list, so we clear them all and add them back excluding
// the specified file
auto sharedDocController = [NSDocumentController sharedDocumentController];
auto recentDocumentURLs = [sharedDocController recentDocumentURLs];
[sharedDocController clearRecentDocuments: nil];
auto* nsFile = createNSURLFromFile (file);
auto reverseEnumerator = [recentDocumentURLs reverseObjectEnumerator];
for (NSURL* url : reverseEnumerator)
if (! [url isEqual:nsFile])
[sharedDocController noteNewRecentDocumentURL:url];
}
#else
ignoreUnused (file);
#endif
}
void RecentlyOpenedFilesList::clearRecentFilesNatively()
{
#if JUCE_MAC
JUCE_AUTORELEASEPOOL
{
[[NSDocumentController sharedDocumentController] clearRecentDocuments: nil];
}
#endif
}
} // namespace juce

View File

@ -1,180 +1,177 @@
/*
==============================================================================
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 a set of files for use as a list of recently-opened documents.
This is a handy class for holding your list of recently-opened documents, with
helpful methods for things like purging any non-existent files, automatically
adding them to a menu, and making persistence easy.
@see File, FileBasedDocument
@tags{GUI}
*/
class JUCE_API RecentlyOpenedFilesList
{
public:
//==============================================================================
/** Creates an empty list.
*/
RecentlyOpenedFilesList();
/** Destructor. */
~RecentlyOpenedFilesList();
//==============================================================================
/** Sets a limit for the number of files that will be stored in the list.
When addFile() is called, then if there is no more space in the list, the
least-recently added file will be dropped.
@see getMaxNumberOfItems
*/
void setMaxNumberOfItems (int newMaxNumber);
/** Returns the number of items that this list will store.
@see setMaxNumberOfItems
*/
int getMaxNumberOfItems() const noexcept { return maxNumberOfItems; }
/** Returns the number of files in the list.
The most recently added file is always at index 0.
*/
int getNumFiles() const;
/** Returns one of the files in the list.
The most recently added file is always at index 0.
*/
File getFile (int index) const;
/** Returns an array of all the absolute pathnames in the list.
*/
const StringArray& getAllFilenames() const noexcept { return files; }
/** Clears all the files from the list. */
void clear();
/** Adds a file to the list.
The file will be added at index 0. If this file is already in the list, it will
be moved up to index 0, but a file can only appear once in the list.
If the list already contains the maximum number of items that is permitted, the
least-recently added file will be dropped from the end.
*/
void addFile (const File& file);
/** Removes a file from the list. */
void removeFile (const File& file);
/** Checks each of the files in the list, removing any that don't exist.
You might want to call this after reloading a list of files, or before putting them
on a menu.
*/
void removeNonExistentFiles();
/** Tells the OS to add a file to the OS-managed list of recent documents for this app.
Not all OSes maintain a list of recent files for an application, so this
function will have no effect on some OSes. Currently it's just implemented for OSX.
*/
static void registerRecentFileNatively (const File& file);
/** Tells the OS to remove a file from the OS-managed list of recent documents for this app.
Not all OSes maintain a list of recent files for an application, so this
function will have no effect on some OSes. Currently it's just implemented for OSX.
*/
static void forgetRecentFileNatively (const File& file);
/** Tells the OS to clear the OS-managed list of recent documents for this app.
Not all OSes maintain a list of recent files for an application, so this
function will have no effect on some OSes. Currently it's just implemented for OSX.
*/
static void clearRecentFilesNatively();
//==============================================================================
/** Adds entries to a menu, representing each of the files in the list.
This is handy for creating an "open recent file..." menu in your app. The
menu items are numbered consecutively starting with the baseItemId value,
and can either be added as complete pathnames, or just the last part of the
filename.
If dontAddNonExistentFiles is true, then each file will be checked and only those
that exist will be added.
If filesToAvoid is not a nullptr, then it is considered to be a zero-terminated array
of pointers to file objects. Any files that appear in this list will not be added to
the menu - the reason for this is that you might have a number of files already open,
so might not want these to be shown in the menu.
It returns the number of items that were added.
*/
int createPopupMenuItems (PopupMenu& menuToAddItemsTo,
int baseItemId,
bool showFullPaths,
bool dontAddNonExistentFiles,
const File** filesToAvoid = nullptr);
//==============================================================================
/** Returns a string that encapsulates all the files in the list.
The string that is returned can later be passed into restoreFromString() in
order to recreate the list. This is handy for persisting your list, e.g. in
a PropertiesFile object.
@see restoreFromString
*/
String toString() const;
/** Restores the list from a previously stringified version of the list.
Pass in a stringified version created with toString() in order to persist/restore
your list.
@see toString
*/
void restoreFromString (const String& stringifiedVersion);
private:
//==============================================================================
StringArray files;
int maxNumberOfItems;
JUCE_LEAK_DETECTOR (RecentlyOpenedFilesList)
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Manages a set of files for use as a list of recently-opened documents.
This is a handy class for holding your list of recently-opened documents, with
helpful methods for things like purging any non-existent files, automatically
adding them to a menu, and making persistence easy.
@see File, FileBasedDocument
@tags{GUI}
*/
class JUCE_API RecentlyOpenedFilesList
{
public:
//==============================================================================
/** Creates an empty list.
*/
RecentlyOpenedFilesList();
//==============================================================================
/** Sets a limit for the number of files that will be stored in the list.
When addFile() is called, then if there is no more space in the list, the
least-recently added file will be dropped.
@see getMaxNumberOfItems
*/
void setMaxNumberOfItems (int newMaxNumber);
/** Returns the number of items that this list will store.
@see setMaxNumberOfItems
*/
int getMaxNumberOfItems() const noexcept { return maxNumberOfItems; }
/** Returns the number of files in the list.
The most recently added file is always at index 0.
*/
int getNumFiles() const;
/** Returns one of the files in the list.
The most recently added file is always at index 0.
*/
File getFile (int index) const;
/** Returns an array of all the absolute pathnames in the list.
*/
const StringArray& getAllFilenames() const noexcept { return files; }
/** Clears all the files from the list. */
void clear();
/** Adds a file to the list.
The file will be added at index 0. If this file is already in the list, it will
be moved up to index 0, but a file can only appear once in the list.
If the list already contains the maximum number of items that is permitted, the
least-recently added file will be dropped from the end.
*/
void addFile (const File& file);
/** Removes a file from the list. */
void removeFile (const File& file);
/** Checks each of the files in the list, removing any that don't exist.
You might want to call this after reloading a list of files, or before putting them
on a menu.
*/
void removeNonExistentFiles();
/** Tells the OS to add a file to the OS-managed list of recent documents for this app.
Not all OSes maintain a list of recent files for an application, so this
function will have no effect on some OSes. Currently it's just implemented for OSX.
*/
static void registerRecentFileNatively (const File& file);
/** Tells the OS to remove a file from the OS-managed list of recent documents for this app.
Not all OSes maintain a list of recent files for an application, so this
function will have no effect on some OSes. Currently it's just implemented for OSX.
*/
static void forgetRecentFileNatively (const File& file);
/** Tells the OS to clear the OS-managed list of recent documents for this app.
Not all OSes maintain a list of recent files for an application, so this
function will have no effect on some OSes. Currently it's just implemented for OSX.
*/
static void clearRecentFilesNatively();
//==============================================================================
/** Adds entries to a menu, representing each of the files in the list.
This is handy for creating an "open recent file..." menu in your app. The
menu items are numbered consecutively starting with the baseItemId value,
and can either be added as complete pathnames, or just the last part of the
filename.
If dontAddNonExistentFiles is true, then each file will be checked and only those
that exist will be added.
If filesToAvoid is not a nullptr, then it is considered to be a zero-terminated array
of pointers to file objects. Any files that appear in this list will not be added to
the menu - the reason for this is that you might have a number of files already open,
so might not want these to be shown in the menu.
It returns the number of items that were added.
*/
int createPopupMenuItems (PopupMenu& menuToAddItemsTo,
int baseItemId,
bool showFullPaths,
bool dontAddNonExistentFiles,
const File** filesToAvoid = nullptr);
//==============================================================================
/** Returns a string that encapsulates all the files in the list.
The string that is returned can later be passed into restoreFromString() in
order to recreate the list. This is handy for persisting your list, e.g. in
a PropertiesFile object.
@see restoreFromString
*/
String toString() const;
/** Restores the list from a previously stringified version of the list.
Pass in a stringified version created with toString() in order to persist/restore
your list.
@see toString
*/
void restoreFromString (const String& stringifiedVersion);
private:
//==============================================================================
StringArray files;
int maxNumberOfItems;
JUCE_LEAK_DETECTOR (RecentlyOpenedFilesList)
};
} // namespace juce

View File

@ -1,101 +1,101 @@
/*
==============================================================================
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
{
SplashScreen::SplashScreen (const String& title, const Image& image, bool useDropShadow)
: Component (title),
backgroundImage (image),
clickCountToDelete (0)
{
// You must supply a valid image here!
jassert (backgroundImage.isValid());
setOpaque (! backgroundImage.hasAlphaChannel());
#if JUCE_IOS || JUCE_ANDROID
const bool useFullScreen = true;
#else
const bool useFullScreen = false;
#endif
makeVisible (image.getWidth(), image.getHeight(), useDropShadow, useFullScreen);
}
SplashScreen::SplashScreen (const String& title, int width, int height, bool useDropShadow)
: Component (title),
clickCountToDelete (0)
{
makeVisible (width, height, useDropShadow, false);
}
void SplashScreen::makeVisible (int w, int h, bool useDropShadow, bool fullscreen)
{
clickCountToDelete = Desktop::getInstance().getMouseButtonClickCounter();
creationTime = Time::getCurrentTime();
const Rectangle<int> screenSize = Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea;
const int width = (fullscreen ? screenSize.getWidth() : w);
const int height = (fullscreen ? screenSize.getHeight() : h);
setAlwaysOnTop (true);
setVisible (true);
centreWithSize (width, height);
addToDesktop (useDropShadow ? ComponentPeer::windowHasDropShadow : 0);
if (fullscreen)
getPeer()->setFullScreen (true);
toFront (false);
}
SplashScreen::~SplashScreen() {}
void SplashScreen::deleteAfterDelay (RelativeTime timeout, bool removeOnMouseClick)
{
// Note that this method must be safe to call from non-GUI threads
if (! removeOnMouseClick)
clickCountToDelete = std::numeric_limits<int>::max();
minimumVisibleTime = timeout;
startTimer (50);
}
void SplashScreen::paint (Graphics& g)
{
g.setOpacity (1.0f);
g.drawImage (backgroundImage, getLocalBounds().toFloat(), RectanglePlacement (RectanglePlacement::fillDestination));
}
void SplashScreen::timerCallback()
{
if (Time::getCurrentTime() > creationTime + minimumVisibleTime
|| Desktop::getInstance().getMouseButtonClickCounter() > clickCountToDelete)
delete this;
}
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
SplashScreen::SplashScreen (const String& title, const Image& image, bool useDropShadow)
: Component (title),
backgroundImage (image),
clickCountToDelete (0)
{
// You must supply a valid image here!
jassert (backgroundImage.isValid());
setOpaque (! backgroundImage.hasAlphaChannel());
#if JUCE_IOS || JUCE_ANDROID
const bool useFullScreen = true;
#else
const bool useFullScreen = false;
#endif
makeVisible (image.getWidth(), image.getHeight(), useDropShadow, useFullScreen);
}
SplashScreen::SplashScreen (const String& title, int width, int height, bool useDropShadow)
: Component (title),
clickCountToDelete (0)
{
makeVisible (width, height, useDropShadow, false);
}
void SplashScreen::makeVisible (int w, int h, bool useDropShadow, bool fullscreen)
{
clickCountToDelete = Desktop::getInstance().getMouseButtonClickCounter();
creationTime = Time::getCurrentTime();
const Rectangle<int> screenSize = Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea;
const int width = (fullscreen ? screenSize.getWidth() : w);
const int height = (fullscreen ? screenSize.getHeight() : h);
setAlwaysOnTop (true);
setVisible (true);
centreWithSize (width, height);
addToDesktop (useDropShadow ? ComponentPeer::windowHasDropShadow : 0);
if (fullscreen)
getPeer()->setFullScreen (true);
toFront (false);
}
SplashScreen::~SplashScreen() {}
void SplashScreen::deleteAfterDelay (RelativeTime timeout, bool removeOnMouseClick)
{
// Note that this method must be safe to call from non-GUI threads
if (! removeOnMouseClick)
clickCountToDelete = std::numeric_limits<int>::max();
minimumVisibleTime = timeout;
startTimer (50);
}
void SplashScreen::paint (Graphics& g)
{
g.setOpacity (1.0f);
g.drawImage (backgroundImage, getLocalBounds().toFloat(), RectanglePlacement (RectanglePlacement::fillDestination));
}
void SplashScreen::timerCallback()
{
if (Time::getCurrentTime() > creationTime + minimumVisibleTime
|| Desktop::getInstance().getMouseButtonClickCounter() > clickCountToDelete)
delete this;
}
} // namespace juce

View File

@ -1,156 +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
{
//==============================================================================
/** A component for showing a splash screen while your app starts up.
This will automatically position itself, and can be told to delete itself after
being on-screen for a minimum length of time.
To use it, just create one of these in your JUCEApplicationBase::initialise() method,
and when your initialisation tasks have finished running, call its deleteAfterDelay()
method to make it automatically get rid of itself.
Note that although you could call deleteAfterDelay() as soon as you create the
SplashScreen object, if you've got a long initialisation procedure, you probably
don't want the splash to time-out and disappear before the initialisation has
finished, which is why it makes sense to not call this method until the end of
your init tasks.
E.g. @code
void MyApp::initialise (const String& commandLine)
{
splash = new SplashScreen ("Welcome to my app!",
ImageFileFormat::loadFrom (File ("/foobar/splash.jpg")),
true);
// now kick off your initialisation work on some kind of thread or task, and
launchBackgroundInitialisationThread();
}
void MyApp::myInitialisationWorkFinished()
{
// ..assuming this is some kind of callback method that is triggered when
// your background initialisation threads have finished, and it's time to open
// your main window, etc..
splash->deleteAfterDelay (RelativeTime::seconds (4), false);
...etc...
}
@endcode
@tags{GUI}
*/
class JUCE_API SplashScreen : public Component,
private Timer,
private DeletedAtShutdown
{
public:
//==============================================================================
/** Creates a SplashScreen object.
When called, the constructor will position the SplashScreen in the centre of the
display, and after the time specified, it will automatically delete itself.
Bear in mind that if you call this during your JUCEApplicationBase::initialise()
method and then block the message thread by performing some kind of task, then
obviously neither your splash screen nor any other GUI will appear until you
allow the message thread to resume and do its work. So if you have time-consuming
tasks to do during startup, use a background thread for them.
After creating one of these (or your subclass of it), you should do your app's
initialisation work, and then call the deleteAfterDelay() method to tell this object
to delete itself after the user has had chance to get a good look at it.
If you're writing a custom splash screen class, there's another protected constructor
that your subclass can call, which doesn't take an image.
@param title the name to give the component
@param backgroundImage an image to draw on the component. The component's size
will be set to the size of this image, and if the image is
semi-transparent, the component will be made non-opaque
@param useDropShadow if true, the window will have a drop shadow
*/
SplashScreen (const String& title,
const Image& backgroundImage,
bool useDropShadow);
/** Destructor. */
~SplashScreen() override;
/** Tells the component to auto-delete itself after a timeout period, or when the
mouse is clicked.
You should call this after finishing your app's initialisation work.
Note that although you could call deleteAfterDelay() as soon as you create the
SplashScreen object, if you've got a long initialisation procedure, you probably
don't want the splash to time-out and disappear before your initialisation has
finished, which is why it makes sense to not call this method and start the
self-delete timer until you're ready.
It's safe to call this method from a non-GUI thread as long as there's no danger that
the object may be being deleted at the same time.
@param minimumTotalTimeToDisplayFor how long the splash screen should stay visible for.
Note that this time is measured from the construction-time of this
object, not from the time that the deleteAfterDelay() method is
called, so if you call this method after a long initialisation
period, it may be deleted without any further delay.
@param removeOnMouseClick if true, the window will be deleted as soon as the user clicks
the mouse (anywhere)
*/
void deleteAfterDelay (RelativeTime minimumTotalTimeToDisplayFor,
bool removeOnMouseClick);
protected:
//==============================================================================
/** This constructor is for use by custom sub-classes that don't want to provide an image. */
SplashScreen (const String& title, int width, int height, bool useDropShadow);
/** @internal */
void paint (Graphics&) override;
private:
//==============================================================================
Image backgroundImage;
Time creationTime;
RelativeTime minimumVisibleTime;
int clickCountToDelete;
void timerCallback() override;
void makeVisible (int w, int h, bool shadow, bool fullscreen);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SplashScreen)
};
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** A component for showing a splash screen while your app starts up.
This will automatically position itself, and can be told to delete itself after
being on-screen for a minimum length of time.
To use it, just create one of these in your JUCEApplicationBase::initialise() method,
and when your initialisation tasks have finished running, call its deleteAfterDelay()
method to make it automatically get rid of itself.
Note that although you could call deleteAfterDelay() as soon as you create the
SplashScreen object, if you've got a long initialisation procedure, you probably
don't want the splash to time-out and disappear before the initialisation has
finished, which is why it makes sense to not call this method until the end of
your init tasks.
E.g. @code
void MyApp::initialise (const String& commandLine)
{
splash = new SplashScreen ("Welcome to my app!",
ImageFileFormat::loadFrom (File ("/foobar/splash.jpg")),
true);
// now kick off your initialisation work on some kind of thread or task, and
launchBackgroundInitialisationThread();
}
void MyApp::myInitialisationWorkFinished()
{
// ..assuming this is some kind of callback method that is triggered when
// your background initialisation threads have finished, and it's time to open
// your main window, etc..
splash->deleteAfterDelay (RelativeTime::seconds (4), false);
...etc...
}
@endcode
@tags{GUI}
*/
class JUCE_API SplashScreen : public Component,
private Timer,
private DeletedAtShutdown
{
public:
//==============================================================================
/** Creates a SplashScreen object.
When called, the constructor will position the SplashScreen in the centre of the
display, and after the time specified, it will automatically delete itself.
Bear in mind that if you call this during your JUCEApplicationBase::initialise()
method and then block the message thread by performing some kind of task, then
obviously neither your splash screen nor any other GUI will appear until you
allow the message thread to resume and do its work. So if you have time-consuming
tasks to do during startup, use a background thread for them.
After creating one of these (or your subclass of it), you should do your app's
initialisation work, and then call the deleteAfterDelay() method to tell this object
to delete itself after the user has had chance to get a good look at it.
If you're writing a custom splash screen class, there's another protected constructor
that your subclass can call, which doesn't take an image.
@param title the name to give the component
@param backgroundImage an image to draw on the component. The component's size
will be set to the size of this image, and if the image is
semi-transparent, the component will be made non-opaque
@param useDropShadow if true, the window will have a drop shadow
*/
SplashScreen (const String& title,
const Image& backgroundImage,
bool useDropShadow);
/** Destructor. */
~SplashScreen() override;
/** Tells the component to auto-delete itself after a timeout period, or when the
mouse is clicked.
You should call this after finishing your app's initialisation work.
Note that although you could call deleteAfterDelay() as soon as you create the
SplashScreen object, if you've got a long initialisation procedure, you probably
don't want the splash to time-out and disappear before your initialisation has
finished, which is why it makes sense to not call this method and start the
self-delete timer until you're ready.
It's safe to call this method from a non-GUI thread as long as there's no danger that
the object may be being deleted at the same time.
@param minimumTotalTimeToDisplayFor how long the splash screen should stay visible for.
Note that this time is measured from the construction-time of this
object, not from the time that the deleteAfterDelay() method is
called, so if you call this method after a long initialisation
period, it may be deleted without any further delay.
@param removeOnMouseClick if true, the window will be deleted as soon as the user clicks
the mouse (anywhere)
*/
void deleteAfterDelay (RelativeTime minimumTotalTimeToDisplayFor,
bool removeOnMouseClick);
protected:
//==============================================================================
/** This constructor is for use by custom sub-classes that don't want to provide an image. */
SplashScreen (const String& title, int width, int height, bool useDropShadow);
/** @internal */
void paint (Graphics&) override;
private:
//==============================================================================
Image backgroundImage;
Time creationTime;
RelativeTime minimumVisibleTime;
int clickCountToDelete;
void timerCallback() override;
void makeVisible (int w, int h, bool shadow, bool fullscreen);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SplashScreen)
};
} // namespace juce

View File

@ -1,42 +1,42 @@
/*
==============================================================================
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_WINDOWS || JUCE_LINUX || JUCE_BSD || JUCE_MAC
SystemTrayIconComponent::SystemTrayIconComponent()
{
addToDesktop (0);
}
SystemTrayIconComponent::~SystemTrayIconComponent()
{
}
#endif
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD || JUCE_MAC
SystemTrayIconComponent::SystemTrayIconComponent()
{
addToDesktop (0);
}
SystemTrayIconComponent::~SystemTrayIconComponent()
{
}
#endif
} // namespace juce

View File

@ -1,116 +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
{
#if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD || JUCE_MAC || DOXYGEN
//==============================================================================
/**
This component sits in the taskbar tray as a small icon.
(NB: The exact behaviour of this class will differ between OSes, and it
isn't fully implemented for all OSes)
To use it, just create one of these components, but don't attempt to make it
visible, add it to a parent, or put it on the desktop.
You can then call setIconImage() to create an icon for it in the taskbar.
To change the icon's tooltip, you can use setIconTooltip().
To respond to mouse-events, you can override the normal mouseDown(),
mouseUp(), mouseDoubleClick() and mouseMove() methods, and although the x, y
position will not be valid, you can use this to respond to clicks. Traditionally
you'd use a left-click to show your application's window, and a right-click
to show a pop-up menu.
@tags{GUI}
*/
class JUCE_API SystemTrayIconComponent : public Component
{
public:
//==============================================================================
/** Constructor. */
SystemTrayIconComponent();
/** Destructor. */
~SystemTrayIconComponent() override;
//==============================================================================
/** Changes the image shown in the taskbar.
On Windows and Linux a full colour Image is used as an icon.
On macOS a template image is used, where all non-transparent regions will be
rendered in a monochrome colour selected dynamically by the operating system.
@param colourImage An colour image to use as an icon on Windows and Linux
@param templateImage A template image to use as an icon on macOS
*/
void setIconImage (const Image& colourImage, const Image& templateImage);
/** Changes the icon's tooltip (if the current OS supports this). */
void setIconTooltip (const String& tooltip);
/** Highlights the icon (if the current OS supports this). */
void setHighlighted (bool);
/** Shows a floating text bubble pointing to the icon (if the current OS supports this). */
void showInfoBubble (const String& title, const String& content);
/** Hides the icon's floating text bubble (if the current OS supports this). */
void hideInfoBubble();
/** Returns the raw handle to whatever kind of internal OS structure is
involved in showing this icon.
@see ComponentPeer::getNativeHandle()
*/
void* getNativeHandle() const;
#if JUCE_LINUX || JUCE_BSD
/** @internal */
void paint (Graphics&) override;
#endif
#if JUCE_MAC
/** Shows a menu attached to the OSX menu bar icon. */
void showDropdownMenu (const PopupMenu& menu);
#endif
private:
//==============================================================================
JUCE_PUBLIC_IN_DLL_BUILD (class Pimpl)
std::unique_ptr<Pimpl> pimpl;
[[deprecated ("The new setIconImage function signature requires different images for macOS and the other platforms.")]]
void setIconImage (const Image& newImage);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SystemTrayIconComponent)
};
#endif
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD || JUCE_MAC || DOXYGEN
//==============================================================================
/**
This component sits in the taskbar tray as a small icon.
(NB: The exact behaviour of this class will differ between OSes, and it
isn't fully implemented for all OSes)
To use it, just create one of these components, but don't attempt to make it
visible, add it to a parent, or put it on the desktop.
You can then call setIconImage() to create an icon for it in the taskbar.
To change the icon's tooltip, you can use setIconTooltip().
To respond to mouse-events, you can override the normal mouseDown(),
mouseUp(), mouseDoubleClick() and mouseMove() methods, and although the x, y
position will not be valid, you can use this to respond to clicks. Traditionally
you'd use a left-click to show your application's window, and a right-click
to show a pop-up menu.
@tags{GUI}
*/
class JUCE_API SystemTrayIconComponent : public Component
{
public:
//==============================================================================
/** Constructor. */
SystemTrayIconComponent();
/** Destructor. */
~SystemTrayIconComponent() override;
//==============================================================================
/** Changes the image shown in the taskbar.
On Windows and Linux a full colour Image is used as an icon.
On macOS a template image is used, where all non-transparent regions will be
rendered in a monochrome colour selected dynamically by the operating system.
@param colourImage An colour image to use as an icon on Windows and Linux
@param templateImage A template image to use as an icon on macOS
*/
void setIconImage (const Image& colourImage, const Image& templateImage);
/** Changes the icon's tooltip (if the current OS supports this). */
void setIconTooltip (const String& tooltip);
/** Highlights the icon (if the current OS supports this). */
void setHighlighted (bool);
/** Shows a floating text bubble pointing to the icon (if the current OS supports this). */
void showInfoBubble (const String& title, const String& content);
/** Hides the icon's floating text bubble (if the current OS supports this). */
void hideInfoBubble();
/** Returns the raw handle to whatever kind of internal OS structure is
involved in showing this icon.
@see ComponentPeer::getNativeHandle()
*/
void* getNativeHandle() const;
#if JUCE_LINUX || JUCE_BSD
/** @internal */
void paint (Graphics&) override;
#endif
#if JUCE_MAC
/** Shows a menu attached to the OSX menu bar icon. */
void showDropdownMenu (const PopupMenu& menu);
#endif
private:
//==============================================================================
JUCE_PUBLIC_IN_DLL_BUILD (class Pimpl)
std::unique_ptr<Pimpl> pimpl;
[[deprecated ("The new setIconImage function signature requires different images for macOS and the other platforms.")]]
void setIconImage (const Image& newImage);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SystemTrayIconComponent)
};
#endif
} // namespace juce

View File

@ -1,287 +1,287 @@
/*
==============================================================================
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_WEB_BROWSER || DOXYGEN
//==============================================================================
/**
A component that displays an embedded web browser.
The browser itself will be platform-dependent. On Mac and iOS it will be
WebKit, on Android it will be Chrome, and on Linux it will be WebKit.
On Windows it will be IE, but if JUCE_USE_WIN_WEBVIEW2 is enabled then using
the WindowsWebView2WebBrowserComponent wrapper instead of this class directly
will attempt to use the Microsoft Edge (Chromium) WebView2. See the documentation
of that class for more information about its requirements.
@tags{GUI}
*/
class JUCE_API WebBrowserComponent : public Component
{
public:
//==============================================================================
/** Creates a WebBrowserComponent.
Once it's created and visible, send the browser to a URL using goToURL().
@param unloadPageWhenBrowserIsHidden if this is true, then when the browser
component is taken offscreen, it'll clear the current page
and replace it with a blank page - this can be handy to stop
the browser using resources in the background when it's not
actually being used.
*/
explicit WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true);
/** Destructor. */
~WebBrowserComponent() override;
//==============================================================================
/** Sends the browser to a particular URL.
@param url the URL to go to.
@param headers an optional set of parameters to put in the HTTP header. If
you supply this, it should be a set of string in the form
"HeaderKey: HeaderValue"
@param postData an optional block of data that will be attached to the HTTP
POST request
*/
void goToURL (const String& url,
const StringArray* headers = nullptr,
const MemoryBlock* postData = nullptr);
/** Stops the current page loading. */
void stop();
/** Sends the browser back one page. */
void goBack();
/** Sends the browser forward one page. */
void goForward();
/** Refreshes the browser. */
void refresh();
/** Clear cookies that the OS has stored for the WebComponents of this application */
static void clearCookies();
//==============================================================================
/** This callback is called when the browser is about to navigate
to a new location.
You can override this method to perform some action when the user
tries to go to a particular URL. To allow the operation to carry on,
return true, or return false to stop the navigation happening.
*/
virtual bool pageAboutToLoad (const String& newURL) { ignoreUnused (newURL); return true; }
/** This callback happens when the browser has finished loading a page. */
virtual void pageFinishedLoading (const String& url) { ignoreUnused (url); }
/** This callback happens when a network error was encountered while
trying to load a page.
You can override this method to show some other error page by calling
goToURL. Return true to allow the browser to carry on to the internal
browser error page.
The errorInfo contains some platform dependent string describing the
error.
*/
virtual bool pageLoadHadNetworkError (const String& errorInfo) { ignoreUnused (errorInfo); return true; }
/** This callback occurs when a script or other activity in the browser asks for
the window to be closed.
*/
virtual void windowCloseRequest() {}
/** This callback occurs when the browser attempts to load a URL in a new window.
This won't actually load the window but gives you a chance to either launch a
new window yourself or just load the URL into the current window with goToURL().
*/
virtual void newWindowAttemptingToLoad (const String& newURL) { ignoreUnused (newURL); }
//==============================================================================
/** @internal */
void paint (Graphics&) override;
/** @internal */
void resized() override;
/** @internal */
void parentHierarchyChanged() override;
/** @internal */
void visibilityChanged() override;
/** @internal */
void focusGained (FocusChangeType) override;
/** @internal */
class Pimpl;
protected:
friend class WindowsWebView2WebBrowserComponent;
/** @internal */
struct ConstructWithoutPimpl
{
explicit ConstructWithoutPimpl (bool unloadOnHide) : unloadWhenHidden (unloadOnHide) {}
const bool unloadWhenHidden;
};
explicit WebBrowserComponent (ConstructWithoutPimpl);
private:
//==============================================================================
std::unique_ptr<Pimpl> browser;
bool blankPageShown = false, unloadPageWhenHidden;
String lastURL;
StringArray lastHeaders;
MemoryBlock lastPostData;
void reloadLastURL();
void checkWindowAssociation();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebBrowserComponent)
};
//==============================================================================
/** Class used to create a set of preferences to pass to the WindowsWebView2WebBrowserComponent
wrapper constructor to modify aspects of its behaviour and settings.
You can chain together a series of calls to this class's methods to create a set of whatever
preferences you want to specify.
@tags{GUI}
*/
class JUCE_API WebView2Preferences
{
public:
//==============================================================================
/** Sets a custom location for the WebView2Loader.dll that is not a part of the
standard system DLL search paths.
*/
WebView2Preferences withDLLLocation (const File& location) const { return with (&WebView2Preferences::dllLocation, location); }
/** Sets a non-default location for storing user data for the browser instance. */
WebView2Preferences withUserDataFolder (const File& folder) const { return with (&WebView2Preferences::userDataFolder, folder); }
/** If this is set, the status bar usually displayed in the lower-left of the webview
will be disabled.
*/
WebView2Preferences withStatusBarDisabled() const { return with (&WebView2Preferences::disableStatusBar, true); }
/** If this is set, a blank page will be displayed on error instead of the default
built-in error page.
*/
WebView2Preferences withBuiltInErrorPageDisabled() const { return with (&WebView2Preferences::disableBuiltInErrorPage, true); }
/** Sets the background colour that WebView2 renders underneath all web content.
This colour must either be fully opaque or transparent. On Windows 7 this
colour must be opaque.
*/
WebView2Preferences withBackgroundColour (const Colour& colour) const
{
// the background colour must be either fully opaque or transparent!
jassert (colour.isOpaque() || colour.isTransparent());
return with (&WebView2Preferences::backgroundColour, colour);
}
//==============================================================================
File getDLLLocation() const { return dllLocation; }
File getUserDataFolder() const { return userDataFolder; }
bool getIsStatusBarDisabled() const noexcept { return disableStatusBar; }
bool getIsBuiltInErrorPageDisabled() const noexcept { return disableBuiltInErrorPage; }
Colour getBackgroundColour() const { return backgroundColour; }
private:
//==============================================================================
template <typename Member, typename Item>
WebView2Preferences with (Member&& member, Item&& item) const
{
auto options = *this;
options.*member = std::forward<Item> (item);
return options;
}
File dllLocation, userDataFolder;
bool disableStatusBar = false, disableBuiltInErrorPage = false;
Colour backgroundColour = Colours::white;
};
/**
If you have enabled the JUCE_USE_WIN_WEBVIEW2 flag then this wrapper will attempt to
use the Microsoft Edge (Chromium) WebView2 control instead of IE on Windows. It will
behave the same as WebBrowserComponent on all other platforms and will fall back to
IE on Windows if the WebView2 requirements are not met.
This requires Microsoft Edge (minimum version 82.0.488.0) to be installed at runtime.
Currently this also requires that WebView2Loader.dll, which can be found in the
Microsoft.Web.WebView package, is installed at runtime. As this is not a standard
system DLL, we can't rely on it being found via the normal system DLL search paths.
Therefore in order to use WebView2 you need to ensure that WebView2Loader.dll is
installed either to a location covered by the Windows DLL system search paths or
to the folder specified in the WebView2Preferences.
@tags{GUI}
*/
class WindowsWebView2WebBrowserComponent : public WebBrowserComponent
{
public:
//==============================================================================
/** Creates a WebBrowserComponent that is compatible with the WebView2 control
on Windows.
@param unloadPageWhenBrowserIsHidden if this is true, then when the browser
component is taken offscreen, it'll clear the current page
and replace it with a blank page - this can be handy to stop
the browser using resources in the background when it's not
actually being used.
@param preferences a set of preferences used to control aspects of the webview's
behaviour.
@see WebView2Preferences
*/
WindowsWebView2WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true,
const WebView2Preferences& preferences = {});
// This constructor has been deprecated. Use the new constructor that takes a
// WebView2Preferences instead.
explicit WindowsWebView2WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true,
const File& dllLocation = {},
const File& userDataFolder = {})
: WindowsWebView2WebBrowserComponent (unloadPageWhenBrowserIsHidden,
WebView2Preferences().withDLLLocation (dllLocation)
.withUserDataFolder (userDataFolder))
{
}
};
#endif
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_WEB_BROWSER || DOXYGEN
//==============================================================================
/**
A component that displays an embedded web browser.
The browser itself will be platform-dependent. On Mac and iOS it will be
WebKit, on Android it will be Chrome, and on Linux it will be WebKit.
On Windows it will be IE, but if JUCE_USE_WIN_WEBVIEW2 is enabled then using
the WindowsWebView2WebBrowserComponent wrapper instead of this class directly
will attempt to use the Microsoft Edge (Chromium) WebView2. See the documentation
of that class for more information about its requirements.
@tags{GUI}
*/
class JUCE_API WebBrowserComponent : public Component
{
public:
//==============================================================================
/** Creates a WebBrowserComponent.
Once it's created and visible, send the browser to a URL using goToURL().
@param unloadPageWhenBrowserIsHidden if this is true, then when the browser
component is taken offscreen, it'll clear the current page
and replace it with a blank page - this can be handy to stop
the browser using resources in the background when it's not
actually being used.
*/
explicit WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true);
/** Destructor. */
~WebBrowserComponent() override;
//==============================================================================
/** Sends the browser to a particular URL.
@param url the URL to go to.
@param headers an optional set of parameters to put in the HTTP header. If
you supply this, it should be a set of string in the form
"HeaderKey: HeaderValue"
@param postData an optional block of data that will be attached to the HTTP
POST request
*/
void goToURL (const String& url,
const StringArray* headers = nullptr,
const MemoryBlock* postData = nullptr);
/** Stops the current page loading. */
void stop();
/** Sends the browser back one page. */
void goBack();
/** Sends the browser forward one page. */
void goForward();
/** Refreshes the browser. */
void refresh();
/** Clear cookies that the OS has stored for the WebComponents of this application */
static void clearCookies();
//==============================================================================
/** This callback is called when the browser is about to navigate
to a new location.
You can override this method to perform some action when the user
tries to go to a particular URL. To allow the operation to carry on,
return true, or return false to stop the navigation happening.
*/
virtual bool pageAboutToLoad (const String& newURL) { ignoreUnused (newURL); return true; }
/** This callback happens when the browser has finished loading a page. */
virtual void pageFinishedLoading (const String& url) { ignoreUnused (url); }
/** This callback happens when a network error was encountered while
trying to load a page.
You can override this method to show some other error page by calling
goToURL. Return true to allow the browser to carry on to the internal
browser error page.
The errorInfo contains some platform dependent string describing the
error.
*/
virtual bool pageLoadHadNetworkError (const String& errorInfo) { ignoreUnused (errorInfo); return true; }
/** This callback occurs when a script or other activity in the browser asks for
the window to be closed.
*/
virtual void windowCloseRequest() {}
/** This callback occurs when the browser attempts to load a URL in a new window.
This won't actually load the window but gives you a chance to either launch a
new window yourself or just load the URL into the current window with goToURL().
*/
virtual void newWindowAttemptingToLoad (const String& newURL) { ignoreUnused (newURL); }
//==============================================================================
/** @internal */
void paint (Graphics&) override;
/** @internal */
void resized() override;
/** @internal */
void parentHierarchyChanged() override;
/** @internal */
void visibilityChanged() override;
/** @internal */
void focusGained (FocusChangeType) override;
/** @internal */
class Pimpl;
protected:
friend class WindowsWebView2WebBrowserComponent;
/** @internal */
struct ConstructWithoutPimpl
{
explicit ConstructWithoutPimpl (bool unloadOnHide) : unloadWhenHidden (unloadOnHide) {}
const bool unloadWhenHidden;
};
explicit WebBrowserComponent (ConstructWithoutPimpl);
private:
//==============================================================================
std::unique_ptr<Pimpl> browser;
bool blankPageShown = false, unloadPageWhenHidden;
String lastURL;
StringArray lastHeaders;
MemoryBlock lastPostData;
void reloadLastURL();
void checkWindowAssociation();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebBrowserComponent)
};
//==============================================================================
/** Class used to create a set of preferences to pass to the WindowsWebView2WebBrowserComponent
wrapper constructor to modify aspects of its behaviour and settings.
You can chain together a series of calls to this class's methods to create a set of whatever
preferences you want to specify.
@tags{GUI}
*/
class JUCE_API WebView2Preferences
{
public:
//==============================================================================
/** Sets a custom location for the WebView2Loader.dll that is not a part of the
standard system DLL search paths.
*/
JUCE_NODISCARD WebView2Preferences withDLLLocation (const File& location) const { return with (&WebView2Preferences::dllLocation, location); }
/** Sets a non-default location for storing user data for the browser instance. */
WebView2Preferences withUserDataFolder (const File& folder) const { return with (&WebView2Preferences::userDataFolder, folder); }
/** If this is set, the status bar usually displayed in the lower-left of the webview
will be disabled.
*/
JUCE_NODISCARD WebView2Preferences withStatusBarDisabled() const { return with (&WebView2Preferences::disableStatusBar, true); }
/** If this is set, a blank page will be displayed on error instead of the default
built-in error page.
*/
JUCE_NODISCARD WebView2Preferences withBuiltInErrorPageDisabled() const { return with (&WebView2Preferences::disableBuiltInErrorPage, true); }
/** Sets the background colour that WebView2 renders underneath all web content.
This colour must either be fully opaque or transparent. On Windows 7 this
colour must be opaque.
*/
JUCE_NODISCARD WebView2Preferences withBackgroundColour (const Colour& colour) const
{
// the background colour must be either fully opaque or transparent!
jassert (colour.isOpaque() || colour.isTransparent());
return with (&WebView2Preferences::backgroundColour, colour);
}
//==============================================================================
File getDLLLocation() const { return dllLocation; }
File getUserDataFolder() const { return userDataFolder; }
bool getIsStatusBarDisabled() const noexcept { return disableStatusBar; }
bool getIsBuiltInErrorPageDisabled() const noexcept { return disableBuiltInErrorPage; }
Colour getBackgroundColour() const { return backgroundColour; }
private:
//==============================================================================
template <typename Member, typename Item>
WebView2Preferences with (Member&& member, Item&& item) const
{
auto options = *this;
options.*member = std::forward<Item> (item);
return options;
}
File dllLocation, userDataFolder;
bool disableStatusBar = false, disableBuiltInErrorPage = false;
Colour backgroundColour = Colours::white;
};
/**
If you have enabled the JUCE_USE_WIN_WEBVIEW2 flag then this wrapper will attempt to
use the Microsoft Edge (Chromium) WebView2 control instead of IE on Windows. It will
behave the same as WebBrowserComponent on all other platforms and will fall back to
IE on Windows if the WebView2 requirements are not met.
This requires Microsoft Edge (minimum version 82.0.488.0) to be installed at runtime.
Currently this also requires that WebView2Loader.dll, which can be found in the
Microsoft.Web.WebView package, is installed at runtime. As this is not a standard
system DLL, we can't rely on it being found via the normal system DLL search paths.
Therefore in order to use WebView2 you need to ensure that WebView2Loader.dll is
installed either to a location covered by the Windows DLL system search paths or
to the folder specified in the WebView2Preferences.
@tags{GUI}
*/
class WindowsWebView2WebBrowserComponent : public WebBrowserComponent
{
public:
//==============================================================================
/** Creates a WebBrowserComponent that is compatible with the WebView2 control
on Windows.
@param unloadPageWhenBrowserIsHidden if this is true, then when the browser
component is taken offscreen, it'll clear the current page
and replace it with a blank page - this can be handy to stop
the browser using resources in the background when it's not
actually being used.
@param preferences a set of preferences used to control aspects of the webview's
behaviour.
@see WebView2Preferences
*/
WindowsWebView2WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true,
const WebView2Preferences& preferences = {});
// This constructor has been deprecated. Use the new constructor that takes a
// WebView2Preferences instead.
explicit WindowsWebView2WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true,
const File& dllLocation = {},
const File& userDataFolder = {})
: WindowsWebView2WebBrowserComponent (unloadPageWhenBrowserIsHidden,
WebView2Preferences().withDLLLocation (dllLocation)
.withUserDataFolder (userDataFolder))
{
}
};
#endif
} // namespace juce