mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-13 00:04:19 +00:00
Added Animated App template and examples
This commit is contained in:
parent
fefcf7aca6
commit
ff6520a89a
1141 changed files with 438491 additions and 94 deletions
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
AnimatedAppComponent::AnimatedAppComponent()
|
||||
: lastUpdateTime (Time::getCurrentTime()), totalUpdates (0)
|
||||
{
|
||||
setOpaque (true);
|
||||
}
|
||||
|
||||
void AnimatedAppComponent::setFramesPerSecond (int framesPerSecond)
|
||||
{
|
||||
jassert (framesPerSecond > 0 && framesPerSecond < 1000);
|
||||
startTimer (1000 / framesPerSecond);
|
||||
}
|
||||
|
||||
int AnimatedAppComponent::getMillisecondsSinceLastUpdate() const noexcept
|
||||
{
|
||||
return (int) (Time::getCurrentTime() - lastUpdateTime).inMilliseconds();
|
||||
}
|
||||
|
||||
void AnimatedAppComponent::timerCallback()
|
||||
{
|
||||
++totalUpdates;
|
||||
update();
|
||||
repaint();
|
||||
lastUpdateTime = Time::getCurrentTime();
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_ANIMATEDAPPCOMPONENT_H_INCLUDED
|
||||
#define JUCE_ANIMATEDAPPCOMPONENT_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
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)
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // JUCE_ANIMATEDAPPCOMPONENT_H_INCLUDED
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_APPLEREMOTE_H_INCLUDED
|
||||
#define JUCE_APPLEREMOTE_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
#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.
|
||||
*/
|
||||
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
|
||||
#endif // JUCE_APPLEREMOTE_H_INCLUDED
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
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,
|
||||
w - bubblePaddingX, 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_BUBBLEMESSAGECOMPONENT_H_INCLUDED
|
||||
#define JUCE_BUBBLEMESSAGECOMPONENT_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
*/
|
||||
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();
|
||||
|
||||
//==============================================================================
|
||||
/** 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).
|
||||
|
||||
For details about exactly how it decides where to position itself, see
|
||||
BubbleComponent::updatePosition().
|
||||
|
||||
@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 compnent. 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.
|
||||
|
||||
For details about exactly how it decides where to position itself, see
|
||||
BubbleComponent::updatePosition().
|
||||
|
||||
@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 compnent. 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);
|
||||
/** @internal */
|
||||
void paintContent (Graphics& g, int w, int h);
|
||||
/** @internal */
|
||||
void timerCallback();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int fadeOutLength, mouseClickCounter;
|
||||
TextLayout textLayout;
|
||||
int64 expiryTime;
|
||||
bool deleteAfterUse;
|
||||
|
||||
void createLayout (const AttributedString&);
|
||||
void init (int numMillisecondsBeforeRemoving,
|
||||
bool removeWhenMouseClicked,
|
||||
bool deleteSelfAfterUse);
|
||||
void hide (bool fadeOut);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BubbleMessageComponent)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_BUBBLEMESSAGECOMPONENT_H_INCLUDED
|
||||
|
|
@ -0,0 +1,566 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class ColourSelector::ColourComponentSlider : public Slider
|
||||
{
|
||||
public:
|
||||
ColourComponentSlider (const String& name)
|
||||
: Slider (name)
|
||||
{
|
||||
setRange (0.0, 255.0, 1.0);
|
||||
}
|
||||
|
||||
String getTextFromValue (double value)
|
||||
{
|
||||
return String::toHexString ((int) value).toUpperCase().paddedLeft ('0', 2);
|
||||
}
|
||||
|
||||
double getValueFromText (const String& text)
|
||||
{
|
||||
return (double) text.getHexValue32();
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (ColourComponentSlider)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ColourSelector::ColourSpaceMarker : public Component
|
||||
{
|
||||
public:
|
||||
ColourSpaceMarker()
|
||||
{
|
||||
setInterceptsMouseClicks (false, false);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.setColour (Colour::greyLevel (0.1f));
|
||||
g.drawEllipse (1.0f, 1.0f, getWidth() - 2.0f, getHeight() - 2.0f, 1.0f);
|
||||
g.setColour (Colour::greyLevel (0.9f));
|
||||
g.drawEllipse (2.0f, 2.0f, getWidth() - 4.0f, getHeight() - 4.0f, 1.0f);
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (ColourSpaceMarker)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ColourSelector::ColourSpaceView : public Component
|
||||
{
|
||||
public:
|
||||
ColourSpaceView (ColourSelector& cs, float& hue, float& sat, float& val, const int edgeSize)
|
||||
: owner (cs), h (hue), s (sat), v (val), lastHue (0.0f), edge (edgeSize)
|
||||
{
|
||||
addAndMakeVisible (marker);
|
||||
setMouseCursor (MouseCursor::CrosshairCursor);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
if (colours.isNull())
|
||||
{
|
||||
const int width = getWidth() / 2;
|
||||
const int height = getHeight() / 2;
|
||||
colours = Image (Image::RGB, width, height, false);
|
||||
|
||||
Image::BitmapData pixels (colours, Image::BitmapData::writeOnly);
|
||||
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
const float val = 1.0f - y / (float) height;
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
const float sat = x / (float) width;
|
||||
pixels.setPixelColour (x, y, Colour (h, sat, val, 1.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g.setOpacity (1.0f);
|
||||
g.drawImageTransformed (colours,
|
||||
RectanglePlacement (RectanglePlacement::stretchToFit)
|
||||
.getTransformToFit (colours.getBounds().toFloat(),
|
||||
getLocalBounds().reduced (edge).toFloat()),
|
||||
false);
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent& e) override
|
||||
{
|
||||
mouseDrag (e);
|
||||
}
|
||||
|
||||
void mouseDrag (const MouseEvent& e) override
|
||||
{
|
||||
const float sat = (e.x - edge) / (float) (getWidth() - edge * 2);
|
||||
const float val = 1.0f - (e.y - edge) / (float) (getHeight() - edge * 2);
|
||||
|
||||
owner.setSV (sat, val);
|
||||
}
|
||||
|
||||
void updateIfNeeded()
|
||||
{
|
||||
if (lastHue != h)
|
||||
{
|
||||
lastHue = h;
|
||||
colours = Image::null;
|
||||
repaint();
|
||||
}
|
||||
|
||||
updateMarker();
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
colours = Image::null;
|
||||
updateMarker();
|
||||
}
|
||||
|
||||
private:
|
||||
ColourSelector& owner;
|
||||
float& h;
|
||||
float& s;
|
||||
float& v;
|
||||
float lastHue;
|
||||
ColourSpaceMarker marker;
|
||||
const int edge;
|
||||
Image colours;
|
||||
|
||||
void updateMarker()
|
||||
{
|
||||
marker.setBounds (roundToInt ((getWidth() - edge * 2) * s),
|
||||
roundToInt ((getHeight() - edge * 2) * (1.0f - v)),
|
||||
edge * 2, edge * 2);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ColourSpaceView)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ColourSelector::HueSelectorMarker : public Component
|
||||
{
|
||||
public:
|
||||
HueSelectorMarker()
|
||||
{
|
||||
setInterceptsMouseClicks (false, false);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
const float cw = (float) getWidth();
|
||||
const float ch = (float) getHeight();
|
||||
|
||||
Path p;
|
||||
p.addTriangle (1.0f, 1.0f,
|
||||
cw * 0.3f, ch * 0.5f,
|
||||
1.0f, ch - 1.0f);
|
||||
|
||||
p.addTriangle (cw - 1.0f, 1.0f,
|
||||
cw * 0.7f, ch * 0.5f,
|
||||
cw - 1.0f, ch - 1.0f);
|
||||
|
||||
g.setColour (Colours::white.withAlpha (0.75f));
|
||||
g.fillPath (p);
|
||||
|
||||
g.setColour (Colours::black.withAlpha (0.75f));
|
||||
g.strokePath (p, PathStrokeType (1.2f));
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (HueSelectorMarker)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ColourSelector::HueSelectorComp : public Component
|
||||
{
|
||||
public:
|
||||
HueSelectorComp (ColourSelector& cs, float& hue, const int edgeSize)
|
||||
: owner (cs), h (hue), edge (edgeSize)
|
||||
{
|
||||
addAndMakeVisible (marker);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
ColourGradient cg;
|
||||
cg.isRadial = false;
|
||||
cg.point1.setXY (0.0f, (float) edge);
|
||||
cg.point2.setXY (0.0f, (float) (getHeight() - edge));
|
||||
|
||||
for (float i = 0.0f; i <= 1.0f; i += 0.02f)
|
||||
cg.addColour (i, Colour (i, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
g.setGradientFill (cg);
|
||||
g.fillRect (getLocalBounds().reduced (edge));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
marker.setBounds (0, roundToInt ((getHeight() - edge * 2) * h), getWidth(), edge * 2);
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent& e) override
|
||||
{
|
||||
mouseDrag (e);
|
||||
}
|
||||
|
||||
void mouseDrag (const MouseEvent& e) override
|
||||
{
|
||||
owner.setHue ((e.y - edge) / (float) (getHeight() - edge * 2));
|
||||
}
|
||||
|
||||
void updateIfNeeded()
|
||||
{
|
||||
resized();
|
||||
}
|
||||
|
||||
private:
|
||||
ColourSelector& owner;
|
||||
float& h;
|
||||
HueSelectorMarker marker;
|
||||
const int edge;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (HueSelectorComp)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ColourSelector::SwatchComponent : public Component
|
||||
{
|
||||
public:
|
||||
SwatchComponent (ColourSelector& cs, int itemIndex)
|
||||
: owner (cs), index (itemIndex)
|
||||
{
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
const Colour c (owner.getSwatchColour (index));
|
||||
|
||||
g.fillCheckerBoard (getLocalBounds(), 6, 6,
|
||||
Colour (0xffdddddd).overlaidWith (c),
|
||||
Colour (0xffffffff).overlaidWith (c));
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent&) override
|
||||
{
|
||||
PopupMenu m;
|
||||
m.addItem (1, TRANS("Use this swatch as the current colour"));
|
||||
m.addSeparator();
|
||||
m.addItem (2, TRANS("Set this swatch to the current colour"));
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options().withTargetComponent (this),
|
||||
ModalCallbackFunction::forComponent (menuStaticCallback, this));
|
||||
}
|
||||
|
||||
private:
|
||||
ColourSelector& owner;
|
||||
const int index;
|
||||
|
||||
static void menuStaticCallback (int result, SwatchComponent* comp)
|
||||
{
|
||||
if (comp != nullptr)
|
||||
{
|
||||
if (result == 1)
|
||||
comp->setColourFromSwatch();
|
||||
else if (result == 2)
|
||||
comp->setSwatchFromColour();
|
||||
}
|
||||
}
|
||||
|
||||
void setColourFromSwatch()
|
||||
{
|
||||
owner.setCurrentColour (owner.getSwatchColour (index));
|
||||
}
|
||||
|
||||
void setSwatchFromColour()
|
||||
{
|
||||
if (owner.getSwatchColour (index) != owner.getCurrentColour())
|
||||
{
|
||||
owner.setSwatchColour (index, owner.getCurrentColour());
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (SwatchComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ColourSelector::ColourSelector (const int sectionsToShow, const int edge, const int gapAroundColourSpaceComponent)
|
||||
: colour (Colours::white),
|
||||
flags (sectionsToShow),
|
||||
edgeGap (edge)
|
||||
{
|
||||
// not much point having a selector with no components in it!
|
||||
jassert ((flags & (showColourAtTop | showSliders | showColourspace)) != 0);
|
||||
|
||||
updateHSV();
|
||||
|
||||
if ((flags & showSliders) != 0)
|
||||
{
|
||||
addAndMakeVisible (sliders[0] = new ColourComponentSlider (TRANS ("red")));
|
||||
addAndMakeVisible (sliders[1] = new ColourComponentSlider (TRANS ("green")));
|
||||
addAndMakeVisible (sliders[2] = new ColourComponentSlider (TRANS ("blue")));
|
||||
addChildComponent (sliders[3] = new ColourComponentSlider (TRANS ("alpha")));
|
||||
|
||||
sliders[3]->setVisible ((flags & showAlphaChannel) != 0);
|
||||
|
||||
for (int i = 4; --i >= 0;)
|
||||
sliders[i]->addListener (this);
|
||||
}
|
||||
|
||||
if ((flags & showColourspace) != 0)
|
||||
{
|
||||
addAndMakeVisible (colourSpace = new ColourSpaceView (*this, h, s, v, gapAroundColourSpaceComponent));
|
||||
addAndMakeVisible (hueSelector = new HueSelectorComp (*this, h, gapAroundColourSpaceComponent));
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
ColourSelector::~ColourSelector()
|
||||
{
|
||||
dispatchPendingMessages();
|
||||
swatchComponents.clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Colour ColourSelector::getCurrentColour() const
|
||||
{
|
||||
return ((flags & showAlphaChannel) != 0) ? colour : colour.withAlpha ((uint8) 0xff);
|
||||
}
|
||||
|
||||
void ColourSelector::setCurrentColour (Colour c)
|
||||
{
|
||||
if (c != colour)
|
||||
{
|
||||
colour = ((flags & showAlphaChannel) != 0) ? c : c.withAlpha ((uint8) 0xff);
|
||||
|
||||
updateHSV();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void ColourSelector::setHue (float newH)
|
||||
{
|
||||
newH = jlimit (0.0f, 1.0f, newH);
|
||||
|
||||
if (h != newH)
|
||||
{
|
||||
h = newH;
|
||||
colour = Colour (h, s, v, colour.getFloatAlpha());
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void ColourSelector::setSV (float newS, float newV)
|
||||
{
|
||||
newS = jlimit (0.0f, 1.0f, newS);
|
||||
newV = jlimit (0.0f, 1.0f, newV);
|
||||
|
||||
if (s != newS || v != newV)
|
||||
{
|
||||
s = newS;
|
||||
v = newV;
|
||||
colour = Colour (h, s, v, colour.getFloatAlpha());
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ColourSelector::updateHSV()
|
||||
{
|
||||
colour.getHSB (h, s, v);
|
||||
}
|
||||
|
||||
void ColourSelector::update()
|
||||
{
|
||||
if (sliders[0] != nullptr)
|
||||
{
|
||||
sliders[0]->setValue ((int) colour.getRed());
|
||||
sliders[1]->setValue ((int) colour.getGreen());
|
||||
sliders[2]->setValue ((int) colour.getBlue());
|
||||
sliders[3]->setValue ((int) colour.getAlpha());
|
||||
}
|
||||
|
||||
if (colourSpace != nullptr)
|
||||
{
|
||||
colourSpace->updateIfNeeded();
|
||||
hueSelector->updateIfNeeded();
|
||||
}
|
||||
|
||||
if ((flags & showColourAtTop) != 0)
|
||||
repaint (previewArea);
|
||||
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ColourSelector::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
|
||||
if ((flags & showColourAtTop) != 0)
|
||||
{
|
||||
const Colour currentColour (getCurrentColour());
|
||||
|
||||
g.fillCheckerBoard (previewArea, 10, 10,
|
||||
Colour (0xffdddddd).overlaidWith (currentColour),
|
||||
Colour (0xffffffff).overlaidWith (currentColour));
|
||||
|
||||
g.setColour (Colours::white.overlaidWith (currentColour).contrasting());
|
||||
g.setFont (Font (14.0f, Font::bold));
|
||||
g.drawText (currentColour.toDisplayString ((flags & showAlphaChannel) != 0),
|
||||
previewArea, Justification::centred, false);
|
||||
}
|
||||
|
||||
if ((flags & showSliders) != 0)
|
||||
{
|
||||
g.setColour (findColour (labelTextColourId));
|
||||
g.setFont (11.0f);
|
||||
|
||||
for (int i = 4; --i >= 0;)
|
||||
{
|
||||
if (sliders[i]->isVisible())
|
||||
g.drawText (sliders[i]->getName() + ":",
|
||||
0, sliders[i]->getY(),
|
||||
sliders[i]->getX() - 8, sliders[i]->getHeight(),
|
||||
Justification::centredRight, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ColourSelector::resized()
|
||||
{
|
||||
const int swatchesPerRow = 8;
|
||||
const int swatchHeight = 22;
|
||||
|
||||
const int numSliders = ((flags & showAlphaChannel) != 0) ? 4 : 3;
|
||||
const int numSwatches = getNumSwatches();
|
||||
|
||||
const int swatchSpace = numSwatches > 0 ? edgeGap + swatchHeight * ((numSwatches + 7) / swatchesPerRow) : 0;
|
||||
const int sliderSpace = ((flags & showSliders) != 0) ? jmin (22 * numSliders + edgeGap, proportionOfHeight (0.3f)) : 0;
|
||||
const int topSpace = ((flags & showColourAtTop) != 0) ? jmin (30 + edgeGap * 2, proportionOfHeight (0.2f)) : edgeGap;
|
||||
|
||||
previewArea.setBounds (edgeGap, edgeGap, getWidth() - edgeGap * 2, topSpace - edgeGap * 2);
|
||||
|
||||
int y = topSpace;
|
||||
|
||||
if ((flags & showColourspace) != 0)
|
||||
{
|
||||
const int hueWidth = jmin (50, proportionOfWidth (0.15f));
|
||||
|
||||
colourSpace->setBounds (edgeGap, y,
|
||||
getWidth() - hueWidth - edgeGap - 4,
|
||||
getHeight() - topSpace - sliderSpace - swatchSpace - edgeGap);
|
||||
|
||||
hueSelector->setBounds (colourSpace->getRight() + 4, y,
|
||||
getWidth() - edgeGap - (colourSpace->getRight() + 4),
|
||||
colourSpace->getHeight());
|
||||
|
||||
y = getHeight() - sliderSpace - swatchSpace - edgeGap;
|
||||
}
|
||||
|
||||
if ((flags & showSliders) != 0)
|
||||
{
|
||||
const int sliderHeight = jmax (4, sliderSpace / numSliders);
|
||||
|
||||
for (int i = 0; i < numSliders; ++i)
|
||||
{
|
||||
sliders[i]->setBounds (proportionOfWidth (0.2f), y,
|
||||
proportionOfWidth (0.72f), sliderHeight - 2);
|
||||
|
||||
y += sliderHeight;
|
||||
}
|
||||
}
|
||||
|
||||
if (numSwatches > 0)
|
||||
{
|
||||
const int startX = 8;
|
||||
const int xGap = 4;
|
||||
const int yGap = 4;
|
||||
const int swatchWidth = (getWidth() - startX * 2) / swatchesPerRow;
|
||||
y += edgeGap;
|
||||
|
||||
if (swatchComponents.size() != numSwatches)
|
||||
{
|
||||
swatchComponents.clear();
|
||||
|
||||
for (int i = 0; i < numSwatches; ++i)
|
||||
{
|
||||
SwatchComponent* const sc = new SwatchComponent (*this, i);
|
||||
swatchComponents.add (sc);
|
||||
addAndMakeVisible (sc);
|
||||
}
|
||||
}
|
||||
|
||||
int x = startX;
|
||||
|
||||
for (int i = 0; i < swatchComponents.size(); ++i)
|
||||
{
|
||||
SwatchComponent* const sc = swatchComponents.getUnchecked(i);
|
||||
|
||||
sc->setBounds (x + xGap / 2,
|
||||
y + yGap / 2,
|
||||
swatchWidth - xGap,
|
||||
swatchHeight - yGap);
|
||||
|
||||
if (((i + 1) % swatchesPerRow) == 0)
|
||||
{
|
||||
x = startX;
|
||||
y += swatchHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
x += swatchWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ColourSelector::sliderValueChanged (Slider*)
|
||||
{
|
||||
if (sliders[0] != nullptr)
|
||||
setCurrentColour (Colour ((uint8) sliders[0]->getValue(),
|
||||
(uint8) sliders[1]->getValue(),
|
||||
(uint8) sliders[2]->getValue(),
|
||||
(uint8) sliders[3]->getValue()));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int ColourSelector::getNumSwatches() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Colour ColourSelector::getSwatchColour (const int) const
|
||||
{
|
||||
jassertfalse; // if you've overridden getNumSwatches(), you also need to implement this method
|
||||
return Colours::black;
|
||||
}
|
||||
|
||||
void ColourSelector::setSwatchColour (const int, const Colour&) const
|
||||
{
|
||||
jassertfalse; // if you've overridden getNumSwatches(), you also need to implement this method
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_COLOURSELECTOR_H_INCLUDED
|
||||
#define JUCE_COLOURSELECTOR_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
class JUCE_API ColourSelector : public Component,
|
||||
public ChangeBroadcaster,
|
||||
protected SliderListener
|
||||
{
|
||||
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. */
|
||||
showSliders = 1 << 2, /**< if set, RGB sliders are shown at the bottom of the component. */
|
||||
showColourspace = 1 << 3 /**< 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();
|
||||
|
||||
//==============================================================================
|
||||
/** 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. */
|
||||
void setCurrentColour (Colour newColour);
|
||||
|
||||
//==============================================================================
|
||||
/** 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) const;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** 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. */
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class ColourSpaceView;
|
||||
class HueSelectorComp;
|
||||
class SwatchComponent;
|
||||
class ColourComponentSlider;
|
||||
class ColourSpaceMarker;
|
||||
class HueSelectorMarker;
|
||||
friend class ColourSpaceView;
|
||||
friend struct ContainerDeletePolicy<ColourSpaceView>;
|
||||
friend class HueSelectorComp;
|
||||
friend struct ContainerDeletePolicy<HueSelectorComp>;
|
||||
|
||||
Colour colour;
|
||||
float h, s, v;
|
||||
ScopedPointer<Slider> sliders[4];
|
||||
ScopedPointer<ColourSpaceView> colourSpace;
|
||||
ScopedPointer<HueSelectorComp> hueSelector;
|
||||
OwnedArray<SwatchComponent> swatchComponents;
|
||||
const int flags;
|
||||
int edgeGap;
|
||||
Rectangle<int> previewArea;
|
||||
|
||||
void setHue (float newH);
|
||||
void setSV (float newS, float newV);
|
||||
void updateHSV();
|
||||
void update();
|
||||
void sliderValueChanged (Slider*);
|
||||
void paint (Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourSelector)
|
||||
|
||||
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
|
||||
// This constructor is here temporarily to prevent old code compiling, because the parameters
|
||||
// have changed - if you get an error here, update your code to use the new constructor instead..
|
||||
ColourSelector (bool);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_COLOURSELECTOR_H_INCLUDED
|
||||
|
|
@ -0,0 +1,472 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class KeyMappingEditorComponent::ChangeKeyButton : public Button
|
||||
{
|
||||
public:
|
||||
ChangeKeyButton (KeyMappingEditorComponent& kec, const CommandID command,
|
||||
const String& keyName, const 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::empty);
|
||||
}
|
||||
|
||||
static void menuCallback (int result, ChangeKeyButton* button)
|
||||
{
|
||||
if (button != nullptr)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case 1: button->assignNewKey(); break;
|
||||
case 2: button->owner.getMappings().removeKeyPress (button->commandID, button->keyNum); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clicked() override
|
||||
{
|
||||
if (keyNum >= 0)
|
||||
{
|
||||
// existing key clicked..
|
||||
PopupMenu m;
|
||||
m.addItem (1, TRANS("Change this key-mapping"));
|
||||
m.addSeparator();
|
||||
m.addItem (2, TRANS("Remove this key-mapping"));
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options(),
|
||||
ModalCallbackFunction::forComponent (menuCallback, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
assignNewKey(); // + button pressed..
|
||||
}
|
||||
}
|
||||
|
||||
void fitToContent (const int h) noexcept
|
||||
{
|
||||
if (keyNum < 0)
|
||||
setSize (h, h);
|
||||
else
|
||||
setSize (jlimit (h * 4, h * 8, 6 + Font (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..."),
|
||||
AlertWindow::NoIcon),
|
||||
owner (kec)
|
||||
{
|
||||
addButton (TRANS("OK"), 1);
|
||||
addButton (TRANS("Cancel"), 0);
|
||||
|
||||
// (avoid return + escape keys getting processed by the buttons..)
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
getChildComponent (i)->setWantsKeyboardFocus (false);
|
||||
|
||||
setWantsKeyboardFocus (true);
|
||||
grabKeyboardFocus();
|
||||
}
|
||||
|
||||
bool keyPressed (const KeyPress& key) override
|
||||
{
|
||||
lastPress = key;
|
||||
String message (TRANS("Key") + ": " + owner.getDescriptionForKeyPress (key));
|
||||
|
||||
const CommandID 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())
|
||||
{
|
||||
const CommandID 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 (AlertWindow::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 (result != 0 && button != nullptr && button->currentKeyEntryWindow != nullptr)
|
||||
{
|
||||
button->currentKeyEntryWindow->setVisible (false);
|
||||
button->setNewKey (button->currentKeyEntryWindow->lastPress, false);
|
||||
}
|
||||
|
||||
button->currentKeyEntryWindow = nullptr;
|
||||
}
|
||||
|
||||
void assignNewKey()
|
||||
{
|
||||
currentKeyEntryWindow = new KeyEntryWindow (owner);
|
||||
currentKeyEntryWindow->enterModalState (true, ModalCallbackFunction::forComponent (keyChosen, this));
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
const CommandID commandID;
|
||||
const int keyNum;
|
||||
ScopedPointer<KeyEntryWindow> currentKeyEntryWindow;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeKeyButton)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::ItemComponent : public Component
|
||||
{
|
||||
public:
|
||||
ItemComponent (KeyMappingEditorComponent& kec, const CommandID command)
|
||||
: owner (kec), commandID (command)
|
||||
{
|
||||
setInterceptsMouseClicks (false, true);
|
||||
|
||||
const bool isReadOnly = owner.isCommandReadOnly (commandID);
|
||||
|
||||
const Array<KeyPress> 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 (String::empty, -1, isReadOnly);
|
||||
}
|
||||
|
||||
void addKeyPressButton (const String& desc, const int index, const bool isReadOnly)
|
||||
{
|
||||
ChangeKeyButton* const 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 (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;)
|
||||
{
|
||||
ChangeKeyButton* const b = keyChangeButtons.getUnchecked(i);
|
||||
|
||||
b->fitToContent (getHeight() - 2);
|
||||
b->setTopRightPosition (x, 1);
|
||||
x = b->getX() - 5;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
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, const 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; }
|
||||
Component* createItemComponent() override { return new ItemComponent (owner, 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; }
|
||||
|
||||
void paintItem (Graphics& g, int width, int height) override
|
||||
{
|
||||
g.setFont (Font (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)
|
||||
{
|
||||
const Array<CommandID> commands (owner.getCommandManager().getCommandsInCategory (categoryName));
|
||||
|
||||
for (int i = 0; i < commands.size(); ++i)
|
||||
if (owner.shouldCommandBeIncluded (commands.getUnchecked(i)))
|
||||
addSubItem (new MappingItem (owner, commands.getUnchecked(i)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
clearSubItems();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
String categoryName;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CategoryItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::TopLevelItem : public TreeViewItem,
|
||||
public ButtonListener,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
TopLevelItem (KeyMappingEditorComponent& kec) : owner (kec)
|
||||
{
|
||||
setLinesDrawnForSubItems (false);
|
||||
owner.getMappings().addChangeListener (this);
|
||||
}
|
||||
|
||||
~TopLevelItem()
|
||||
{
|
||||
owner.getMappings().removeChangeListener (this);
|
||||
}
|
||||
|
||||
bool mightContainSubItems() { return true; }
|
||||
String getUniqueName() const { return "keys"; }
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
const OpennessRestorer opennessRestorer (*this);
|
||||
clearSubItems();
|
||||
|
||||
const StringArray categories (owner.getCommandManager().getCommandCategories());
|
||||
|
||||
for (int i = 0; i < categories.size(); ++i)
|
||||
{
|
||||
const Array<CommandID> commands (owner.getCommandManager().getCommandsInCategory (categories[i]));
|
||||
int count = 0;
|
||||
|
||||
for (int j = 0; j < commands.size(); ++j)
|
||||
if (owner.shouldCommandBeIncluded (commands.getUnchecked(j)))
|
||||
++count;
|
||||
|
||||
if (count > 0)
|
||||
addSubItem (new CategoryItem (owner, categories[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static void resetToDefaultsCallback (int result, KeyMappingEditorComponent* owner)
|
||||
{
|
||||
if (result != 0 && owner != nullptr)
|
||||
owner->getMappings().resetToDefaultMappings();
|
||||
}
|
||||
|
||||
void buttonClicked (Button*) override
|
||||
{
|
||||
AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon,
|
||||
TRANS("Reset to defaults"),
|
||||
TRANS("Are you sure you want to reset all the key-mappings to their default state?"),
|
||||
TRANS("Reset"),
|
||||
String::empty,
|
||||
&owner,
|
||||
ModalCallbackFunction::forComponent (resetToDefaultsCallback, &owner));
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
KeyMappingEditorComponent::KeyMappingEditorComponent (KeyPressMappingSet& mappingManager,
|
||||
const bool showResetToDefaultButton)
|
||||
: mappings (mappingManager),
|
||||
resetButton (TRANS ("reset to defaults"))
|
||||
{
|
||||
treeItem = new TopLevelItem (*this);
|
||||
|
||||
if (showResetToDefaultButton)
|
||||
{
|
||||
addAndMakeVisible (resetButton);
|
||||
resetButton.addListener (treeItem);
|
||||
}
|
||||
|
||||
addAndMakeVisible (tree);
|
||||
tree.setColour (TreeView::backgroundColourId, findColour (backgroundColourId));
|
||||
tree.setRootItemVisible (false);
|
||||
tree.setDefaultOpenness (true);
|
||||
tree.setRootItem (treeItem);
|
||||
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)
|
||||
{
|
||||
const ApplicationCommandInfo* const ci = mappings.getCommandManager().getCommandForID (commandID);
|
||||
|
||||
return ci != nullptr && (ci->flags & ApplicationCommandInfo::hiddenFromKeyEditor) == 0;
|
||||
}
|
||||
|
||||
bool KeyMappingEditorComponent::isCommandReadOnly (const CommandID commandID)
|
||||
{
|
||||
const ApplicationCommandInfo* const ci = mappings.getCommandManager().getCommandForID (commandID);
|
||||
|
||||
return ci != nullptr && (ci->flags & ApplicationCommandInfo::readOnlyInKeyEditor) != 0;
|
||||
}
|
||||
|
||||
String KeyMappingEditorComponent::getDescriptionForKeyPress (const KeyPress& key)
|
||||
{
|
||||
return key.getTextDescription();
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_KEYMAPPINGEDITORCOMPONENT_H_INCLUDED
|
||||
#define JUCE_KEYMAPPINGEDITORCOMPONENT_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component to allow editing of the keymaps stored by a KeyPressMappingSet
|
||||
object.
|
||||
|
||||
@see KeyPressMappingSet
|
||||
*/
|
||||
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();
|
||||
|
||||
//==============================================================================
|
||||
/** 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.
|
||||
|
||||
To change the colours of the menu that pops up
|
||||
|
||||
@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;
|
||||
friend class TopLevelItem;
|
||||
friend struct ContainerDeletePolicy<ChangeKeyButton>;
|
||||
friend struct ContainerDeletePolicy<TopLevelItem>;
|
||||
ScopedPointer<TopLevelItem> treeItem;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KeyMappingEditorComponent)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_KEYMAPPINGEDITORCOMPONENT_H_INCLUDED
|
||||
|
|
@ -0,0 +1,474 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR
|
||||
|
||||
namespace LiveConstantEditor
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class AllComponentRepainter : private Timer,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
AllComponentRepainter() {}
|
||||
|
||||
static AllComponentRepainter& getInstance()
|
||||
{
|
||||
static AllComponentRepainter* instance = new AllComponentRepainter();
|
||||
return *instance;
|
||||
}
|
||||
|
||||
void trigger()
|
||||
{
|
||||
if (! isTimerRunning())
|
||||
startTimer (100);
|
||||
}
|
||||
|
||||
private:
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
for (int i = TopLevelWindow::getNumTopLevelWindows(); --i >= 0;)
|
||||
if (Component* c = TopLevelWindow::getTopLevelWindow(i))
|
||||
repaintAndResizeAllComps (c);
|
||||
}
|
||||
|
||||
static void repaintAndResizeAllComps (Component::SafePointer<Component> c)
|
||||
{
|
||||
if (c->isVisible())
|
||||
{
|
||||
c->repaint();
|
||||
c->resized();
|
||||
|
||||
for (int i = c->getNumChildComponents(); --i >= 0;)
|
||||
if (c != nullptr)
|
||||
if (Component* child = c->getChildComponent(i))
|
||||
repaintAndResizeAllComps (child);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
int64 parseInt (String s)
|
||||
{
|
||||
s = s.retainCharacters ("0123456789abcdefABCDEFx");
|
||||
|
||||
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), resetButton ("reset"), document (d), sourceEditor (document, &tokeniser), wasHex (false)
|
||||
{
|
||||
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.addListener (this);
|
||||
sourceEditor.setReadOnly (true);
|
||||
resetButton.addListener (this);
|
||||
}
|
||||
|
||||
void LivePropertyEditorBase::paint (Graphics& g)
|
||||
{
|
||||
g.setColour (Colours::white);
|
||||
g.fillRect (getLocalBounds().removeFromBottom (1));
|
||||
}
|
||||
|
||||
void LivePropertyEditorBase::resized()
|
||||
{
|
||||
Rectangle<int> r (getLocalBounds().reduced (0, 3).withTrimmedBottom (1));
|
||||
|
||||
Rectangle<int> left (r.removeFromLeft (jmax (200, r.getWidth() / 3)));
|
||||
|
||||
Rectangle<int> 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::textEditorTextChanged (TextEditor&)
|
||||
{
|
||||
applyNewValue (valueEditor.getText());
|
||||
}
|
||||
|
||||
void LivePropertyEditorBase::buttonClicked (Button*)
|
||||
{
|
||||
applyNewValue (value.getOriginalStringValue (wasHex));
|
||||
}
|
||||
|
||||
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);
|
||||
String line (pos.getLineText());
|
||||
String::CharPointerType 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 = p.findEndOfWhitespace();
|
||||
|
||||
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() == '(')
|
||||
{
|
||||
String::CharPointerType start (p), end (p);
|
||||
|
||||
int depth = 1;
|
||||
|
||||
while (! end.isEmpty())
|
||||
{
|
||||
const juce_wchar 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
|
||||
{
|
||||
Rectangle<int> 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);
|
||||
}
|
||||
|
||||
void closeButtonPressed() override
|
||||
{
|
||||
setVisible (false);
|
||||
}
|
||||
|
||||
void updateItems (ValueList& list)
|
||||
{
|
||||
if (ValueListHolderComponent* l = dynamic_cast<ValueListHolderComponent*> (viewport.getViewedComponent()))
|
||||
{
|
||||
while (l->getNumChildComponents() < list.values.size())
|
||||
{
|
||||
if (LiveValueBase* v = list.values [l->getNumChildComponents()])
|
||||
l->addItem (viewport.getMaximumVisibleWidth(), *v, list.getDocument (v->sourceFile));
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
setVisible (true);
|
||||
}
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
DocumentWindow::resized();
|
||||
|
||||
if (ValueListHolderComponent* l = dynamic_cast<ValueListHolderComponent*> (viewport.getViewedComponent()))
|
||||
l->layout (viewport.getMaximumVisibleWidth());
|
||||
}
|
||||
|
||||
Viewport viewport;
|
||||
LookAndFeel_V3 lookAndFeel;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ValueList::ValueList() {}
|
||||
ValueList::~ValueList() {}
|
||||
|
||||
ValueList& ValueList::getInstance()
|
||||
{
|
||||
static ValueList* i = new ValueList();
|
||||
return *i;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
CodeDocument* 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(), 6, 6,
|
||||
Colour (0xffdddddd).overlaidWith (getColour()),
|
||||
Colour (0xffffffff).overlaidWith (getColour()));
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent&) override
|
||||
{
|
||||
ColourSelector* colourSelector = new ColourSelector();
|
||||
colourSelector->setName ("Colour");
|
||||
colourSelector->setCurrentColour (getColour());
|
||||
colourSelector->addChangeListener (this);
|
||||
colourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
|
||||
colourSelector->setSize (300, 400);
|
||||
|
||||
CallOutBox::launchAsynchronously (colourSelector, getScreenBounds(), nullptr);
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster* source) override
|
||||
{
|
||||
if (ColourSelector* cs = dynamic_cast<ColourSelector*> (source))
|
||||
editor.applyNewValue (getAsString (cs->getCurrentColour(), true));
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
LivePropertyEditorBase& editor;
|
||||
};
|
||||
|
||||
Component* createColourEditor (LivePropertyEditorBase& editor)
|
||||
{
|
||||
return new ColourEditorComp (editor);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class SliderComp : public Component,
|
||||
private Slider::Listener
|
||||
{
|
||||
public:
|
||||
SliderComp (LivePropertyEditorBase& e, bool useFloat)
|
||||
: editor (e), isFloat (useFloat)
|
||||
{
|
||||
slider.setTextBoxStyle (Slider::NoTextBox, true, 0, 0);
|
||||
addAndMakeVisible (slider);
|
||||
updateRange();
|
||||
slider.addListener (this);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private:
|
||||
LivePropertyEditorBase& editor;
|
||||
Slider slider;
|
||||
bool isFloat;
|
||||
|
||||
void sliderValueChanged (Slider*)
|
||||
{
|
||||
editor.applyNewValue (isFloat ? getAsString ((double) slider.getValue(), editor.wasHex)
|
||||
: getAsString ((int64) slider.getValue(), editor.wasHex));
|
||||
|
||||
}
|
||||
|
||||
void sliderDragStarted (Slider*) {}
|
||||
void sliderDragEnded (Slider*) { updateRange(); }
|
||||
|
||||
void resized()
|
||||
{
|
||||
slider.setBounds (getLocalBounds().removeFromTop (25));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Component* createIntegerSlider (LivePropertyEditorBase& editor) { return new SliderComp (editor, false); }
|
||||
Component* createFloatSlider (LivePropertyEditorBase& editor) { return new SliderComp (editor, true); }
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_LIVECONSTANTEDITOR_H_INCLUDED
|
||||
#define JUCE_LIVECONSTANTEDITOR_H_INCLUDED
|
||||
|
||||
#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR && ! 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 (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 (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,
|
||||
private TextEditor::Listener,
|
||||
private ButtonListener
|
||||
{
|
||||
LivePropertyEditorBase (LiveValueBase&, CodeDocument&);
|
||||
|
||||
void paint (Graphics&) override;
|
||||
void resized() override;
|
||||
void textEditorTextChanged (TextEditor&) override;
|
||||
void buttonClicked (Button*) override;
|
||||
|
||||
void applyNewValue (const String&);
|
||||
void selectOriginalValue();
|
||||
void findOriginalValueInCode();
|
||||
|
||||
LiveValueBase& value;
|
||||
Label name;
|
||||
TextEditor valueEditor;
|
||||
TextButton resetButton;
|
||||
CodeDocument& document;
|
||||
CPlusPlusCodeTokeniser tokeniser;
|
||||
CodeEditorComponent sourceEditor;
|
||||
CodeDocument::Position valueStart, valueEnd;
|
||||
ScopedPointer<Component> customComp;
|
||||
bool wasHex;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (LivePropertyEditorBase)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
Component* createColourEditor (LivePropertyEditorBase&);
|
||||
Component* createIntegerSlider (LivePropertyEditorBase&);
|
||||
Component* createFloatSlider (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 <typename Type>
|
||||
struct LivePropertyEditor : public LivePropertyEditorBase
|
||||
{
|
||||
template <typename ValueType>
|
||||
LivePropertyEditor (ValueType& v, CodeDocument& d) : LivePropertyEditorBase (v, d)
|
||||
{
|
||||
addAndMakeVisible (customComp = CustomEditor<Type>::create (*this));
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
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; }
|
||||
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();
|
||||
|
||||
static ValueList& getInstance();
|
||||
|
||||
template <typename Type>
|
||||
LiveValue<Type>& getValue (const char* file, int line, const Type& initialValue)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
typedef LiveValue<Type> ValueType;
|
||||
|
||||
for (int i = 0; i < values.size(); ++i)
|
||||
{
|
||||
LiveValueBase* v = values.getUnchecked(i);
|
||||
|
||||
if (v->sourceLine == line && v->sourceFile == file)
|
||||
return *static_cast<ValueType*> (v);
|
||||
}
|
||||
|
||||
ValueType* v = new ValueType (file, line, initialValue);
|
||||
addValue (v);
|
||||
return *v;
|
||||
}
|
||||
|
||||
private:
|
||||
OwnedArray<LiveValueBase> values;
|
||||
OwnedArray<CodeDocument> documents;
|
||||
Array<File> documentFiles;
|
||||
class EditorWindow;
|
||||
friend class EditorWindow;
|
||||
friend struct ContainerDeletePolicy<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)
|
||||
{
|
||||
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))
|
||||
#else
|
||||
#define JUCE_LIVE_CONSTANT(initialValue) \
|
||||
(initialValue)
|
||||
#endif
|
||||
|
||||
|
||||
#endif // JUCE_LIVECONSTANTEDITOR_H_INCLUDED
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
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)
|
||||
{
|
||||
DrawableButton* const button = new DrawableButton (title, DrawableButton::ImageAboveTextLabel);
|
||||
buttons.add (button);
|
||||
|
||||
button->setImages (icon, overIcon, downIcon);
|
||||
button->setRadioGroupId (1);
|
||||
button->addListener (this);
|
||||
button->setClickingTogglesState (true);
|
||||
button->setWantsKeyboardFocus (false);
|
||||
addAndMakeVisible (button);
|
||||
|
||||
resized();
|
||||
|
||||
if (currentPage == nullptr)
|
||||
setCurrentPage (title);
|
||||
}
|
||||
|
||||
void PreferencesPanel::addSettingsPage (const String& title, const void* imageData, const 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 = nullptr;
|
||||
currentPage = createComponentForPage (pageName);
|
||||
|
||||
if (currentPage != nullptr)
|
||||
{
|
||||
addAndMakeVisible (currentPage);
|
||||
currentPage->toBack();
|
||||
resized();
|
||||
}
|
||||
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
{
|
||||
if (buttons.getUnchecked(i)->getName() == pageName)
|
||||
{
|
||||
buttons.getUnchecked(i)->setToggleState (true, dontSendNotification);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreferencesPanel::buttonClicked (Button*)
|
||||
{
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
{
|
||||
if (buttons.getUnchecked(i)->getToggleState())
|
||||
{
|
||||
setCurrentPage (buttons.getUnchecked(i)->getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_PREFERENCESPANEL_H_INCLUDED
|
||||
#define JUCE_PREFERENCESPANEL_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
class JUCE_API PreferencesPanel : public Component,
|
||||
private ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug)
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty panel.
|
||||
|
||||
Use addSettingsPage() to add some pages to it in your constructor.
|
||||
*/
|
||||
PreferencesPanel();
|
||||
|
||||
/** Destructor. */
|
||||
~PreferencesPanel();
|
||||
|
||||
//==============================================================================
|
||||
/** 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;
|
||||
/** @internal */
|
||||
void buttonClicked (Button*) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String currentPageName;
|
||||
ScopedPointer <Component> currentPage;
|
||||
OwnedArray<DrawableButton> buttons;
|
||||
int buttonSize;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PreferencesPanel)
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // JUCE_PREFERENCESPANEL_H_INCLUDED
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
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: [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())]];
|
||||
}
|
||||
#else
|
||||
(void) file;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_RECENTLYOPENEDFILESLIST_H_INCLUDED
|
||||
#define JUCE_RECENTLYOPENEDFILESLIST_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
*/
|
||||
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);
|
||||
|
||||
//==============================================================================
|
||||
/** 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 non-zero, 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)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_RECENTLYOPENEDFILESLIST_H_INCLUDED
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
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());
|
||||
makeVisible (image.getWidth(), image.getHeight(), useDropShadow);
|
||||
}
|
||||
|
||||
SplashScreen::SplashScreen (const String& title, int width, int height, bool useDropShadow)
|
||||
: Component (title),
|
||||
clickCountToDelete (0)
|
||||
{
|
||||
makeVisible (width, height, useDropShadow);
|
||||
}
|
||||
|
||||
void SplashScreen::makeVisible (int w, int h, bool useDropShadow)
|
||||
{
|
||||
clickCountToDelete = Desktop::getInstance().getMouseButtonClickCounter();
|
||||
creationTime = Time::getCurrentTime();
|
||||
|
||||
setAlwaysOnTop (true);
|
||||
setVisible (true);
|
||||
centreWithSize (w, h);
|
||||
addToDesktop (useDropShadow ? ComponentPeer::windowHasDropShadow : 0);
|
||||
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,
|
||||
0, 0, getWidth(), getHeight(),
|
||||
0, 0, backgroundImage.getWidth(), backgroundImage.getHeight());
|
||||
}
|
||||
|
||||
void SplashScreen::timerCallback()
|
||||
{
|
||||
if (Time::getCurrentTime() > creationTime + minimumVisibleTime
|
||||
|| Desktop::getInstance().getMouseButtonClickCounter() > clickCountToDelete)
|
||||
delete this;
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_SPLASHSCREEN_H_INCLUDED
|
||||
#define JUCE_SPLASHSCREEN_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** 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
|
||||
*/
|
||||
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();
|
||||
|
||||
/** 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);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SplashScreen)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_SPLASHSCREEN_H_INCLUDED
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_WINDOWS || JUCE_LINUX || JUCE_MAC
|
||||
|
||||
SystemTrayIconComponent::SystemTrayIconComponent()
|
||||
{
|
||||
addToDesktop (0);
|
||||
}
|
||||
|
||||
SystemTrayIconComponent::~SystemTrayIconComponent()
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_SYSTEMTRAYICONCOMPONENT_H_INCLUDED
|
||||
#define JUCE_SYSTEMTRAYICONCOMPONENT_H_INCLUDED
|
||||
|
||||
#if JUCE_WINDOWS || JUCE_LINUX || 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.
|
||||
*/
|
||||
class JUCE_API SystemTrayIconComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
SystemTrayIconComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~SystemTrayIconComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the image shown in the taskbar. */
|
||||
void setIconImage (const Image& newImage);
|
||||
|
||||
/** 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
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_PUBLIC_IN_DLL_BUILD (class Pimpl)
|
||||
ScopedPointer<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SystemTrayIconComponent)
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
#endif // JUCE_SYSTEMTRAYICONCOMPONENT_H_INCLUDED
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_WEBBROWSERCOMPONENT_H_INCLUDED
|
||||
#define JUCE_WEBBROWSERCOMPONENT_H_INCLUDED
|
||||
|
||||
#if JUCE_WEB_BROWSER || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that displays an embedded web browser.
|
||||
|
||||
The browser itself will be platform-dependent. On the Mac, probably Safari, on
|
||||
Windows, probably IE.
|
||||
|
||||
*/
|
||||
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();
|
||||
|
||||
//==============================================================================
|
||||
/** 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();
|
||||
|
||||
//==============================================================================
|
||||
/** 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);
|
||||
|
||||
/** This callback happens when the browser has finished loading a page. */
|
||||
virtual void pageFinishedLoading (const String& url);
|
||||
|
||||
/** This callback occurs when a script or other activity in the browser asks for
|
||||
the window to be closed.
|
||||
*/
|
||||
virtual void windowCloseRequest();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
void visibilityChanged() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class Pimpl;
|
||||
Pimpl* browser;
|
||||
bool blankPageShown, unloadPageWhenBrowserIsHidden;
|
||||
String lastURL;
|
||||
StringArray lastHeaders;
|
||||
MemoryBlock lastPostData;
|
||||
|
||||
void reloadLastURL();
|
||||
void checkWindowAssociation();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebBrowserComponent)
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
#endif // JUCE_WEBBROWSERCOMPONENT_H_INCLUDED
|
||||
Loading…
Add table
Add a link
Reference in a new issue