migrating to the latest JUCE version
This commit is contained in:
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user