git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce

subrepo:
  subdir:   "deps/juce"
  merged:   "b13f9084e"
upstream:
  origin:   "https://github.com/essej/JUCE.git"
  branch:   "sono6good"
  commit:   "b13f9084e"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"
This commit is contained in:
essej
2022-04-18 17:51:22 -04:00
parent 63e175fee6
commit 25bd5d8adb
3210 changed files with 1045392 additions and 0 deletions

1477
deps/juce/examples/GUI/AccessibilityDemo.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,112 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: AnimationAppDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Simple animation application.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: AnimationAppDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
/*
This component lives inside our window, and this is where you should put all
your controls and content.
*/
class AnimationAppDemo : public AnimatedAppComponent
{
public:
//==============================================================================
AnimationAppDemo()
{
setSize (800, 600);
setFramesPerSecond (60);
}
void update() override
{
// This function is called at the frequency specified by the setFramesPerSecond() call
// in the constructor. You can use it to update counters, animate values, etc.
}
void paint (Graphics& g) override
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
g.setColour (getLookAndFeel().findColour (Slider::thumbColourId));
auto fishLength = 15;
Path spinePath;
for (auto i = 0; i < fishLength; ++i)
{
auto radius = 100 + 10 * std::sin ((float) getFrameCounter() * 0.1f + (float) i * 0.5f);
Point<float> p ((float) getWidth() / 2.0f + 1.5f * radius * std::sin ((float) getFrameCounter() * 0.02f + (float) i * 0.12f),
(float) getHeight() / 2.0f + 1.0f * radius * std::cos ((float) getFrameCounter() * 0.04f + (float) i * 0.12f));
// draw the circles along the fish
g.fillEllipse (p.x - (float) i, p.y - (float) i, 2.0f + 2.0f * (float) i, 2.0f + 2.0f * (float) i);
if (i == 0)
spinePath.startNewSubPath (p); // if this is the first point, start a new path..
else
spinePath.lineTo (p); // ...otherwise add the next point
}
// draw an outline around the path that we have created
g.strokePath (spinePath, PathStrokeType (4.0f));
}
void resized() override
{
// This is called when this component is resized.
// If you add any child components, this is where you should
// update their positions.
}
private:
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimationAppDemo)
};

301
deps/juce/examples/GUI/AnimationDemo.h vendored Normal file
View File

@ -0,0 +1,301 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: AnimationDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Displays an animated draggable ball.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: AnimationDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
/** This will be the source of our balls and can be dragged around. */
class BallGeneratorComponent : public Component
{
public:
BallGeneratorComponent() {}
void paint (Graphics& g) override
{
auto area = getLocalBounds().reduced (2);
g.setColour (Colours::orange);
g.drawRoundedRectangle (area.toFloat(), 10.0f, 2.0f);
g.setColour (findColour (TextButton::textColourOffId));
g.drawFittedText ("Drag Me!", area, Justification::centred, 1);
}
void resized() override
{
// Just set the limits of our constrainer so that we don't drag ourselves off the screen
constrainer.setMinimumOnscreenAmounts (getHeight(), getWidth(),
getHeight(), getWidth());
}
void mouseDown (const MouseEvent& e) override
{
// Prepares our dragger to drag this Component
dragger.startDraggingComponent (this, e);
}
void mouseDrag (const MouseEvent& e) override
{
// Moves this Component according to the mouse drag event and applies our constraints to it
dragger.dragComponent (this, e, &constrainer);
}
private:
ComponentBoundsConstrainer constrainer;
ComponentDragger dragger;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BallGeneratorComponent)
};
//==============================================================================
struct BallComponent : public Component
{
BallComponent (Point<float> pos)
: position (pos),
speed (Random::getSystemRandom().nextFloat() * 4.0f - 2.0f,
Random::getSystemRandom().nextFloat() * -6.0f - 2.0f),
colour (Colours::white)
{
setSize (20, 20);
step();
}
bool step()
{
position += speed;
speed.y += 0.1f;
setCentrePosition ((int) position.x,
(int) position.y);
if (auto* parent = getParentComponent())
return isPositiveAndBelow (position.x, (float) parent->getWidth())
&& position.y < (float) parent->getHeight();
return position.y < 400.0f && position.x >= -10.0f;
}
void paint (Graphics& g) override
{
g.setColour (colour);
g.fillEllipse (2.0f, 2.0f, (float) getWidth() - 4.0f, (float) getHeight() - 4.0f);
g.setColour (Colours::darkgrey);
g.drawEllipse (2.0f, 2.0f, (float) getWidth() - 4.0f, (float) getHeight() - 4.0f, 1.0f);
}
Point<float> position, speed;
Colour colour;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BallComponent)
};
//==============================================================================
class AnimationDemo : public Component,
private Timer
{
public:
AnimationDemo()
{
setOpaque (true);
for (auto i = 0; i < 11; ++i)
{
auto* b = createButton();
componentsToAnimate.add (b);
addAndMakeVisible (b);
b->onClick = [this] { triggerAnimation(); };
}
addAndMakeVisible (ballGenerator);
cycleCount = 2;
startTimerHz (60);
setSize (620, 620);
}
void paint (Graphics& g) override
{
g.fillAll (findColour (ResizableWindow::backgroundColourId));
}
void resized() override
{
ballGenerator.centreWithSize (80, 50);
triggerAnimation();
}
private:
OwnedArray<Component> componentsToAnimate;
OwnedArray<BallComponent> balls;
BallGeneratorComponent ballGenerator;
ComponentAnimator animator;
int cycleCount;
bool firstCallback = true;
Button* createRandomButton()
{
DrawablePath normal, over;
Path star1;
star1.addStar ({}, 5, 20.0f, 50.0f, 0.2f);
normal.setPath (star1);
normal.setFill (Colours::red);
Path star2;
star2.addStar ({}, 7, 30.0f, 50.0f, 0.0f);
over.setPath (star2);
over.setFill (Colours::pink);
over.setStrokeFill (Colours::black);
over.setStrokeThickness (5.0f);
auto juceIcon = getImageFromAssets ("juce_icon.png");
DrawableImage down;
down.setImage (juceIcon);
down.setOverlayColour (Colours::black.withAlpha (0.3f));
if (Random::getSystemRandom().nextInt (10) > 2)
{
auto type = Random::getSystemRandom().nextInt (3);
auto* d = new DrawableButton ("Button",
type == 0 ? DrawableButton::ImageOnButtonBackground
: (type == 1 ? DrawableButton::ImageFitted
: DrawableButton::ImageAboveTextLabel));
d->setImages (&normal,
Random::getSystemRandom().nextBool() ? &over : nullptr,
Random::getSystemRandom().nextBool() ? &down : nullptr);
if (Random::getSystemRandom().nextBool())
{
d->setColour (DrawableButton::backgroundColourId, getRandomBrightColour());
d->setColour (DrawableButton::backgroundOnColourId, getRandomBrightColour());
}
d->setClickingTogglesState (Random::getSystemRandom().nextBool());
return d;
}
auto* b = new ImageButton ("ImageButton");
b->setImages (true, true, true,
juceIcon, 0.7f, Colours::transparentBlack,
juceIcon, 1.0f, getRandomDarkColour() .withAlpha (0.2f),
juceIcon, 1.0f, getRandomBrightColour().withAlpha (0.8f),
0.5f);
return b;
}
Button* createButton()
{
auto juceIcon = getImageFromAssets ("juce_icon.png").rescaled (128, 128);
auto* b = new ImageButton ("ImageButton");
b->setImages (true, true, true,
juceIcon, 1.0f, Colours::transparentBlack,
juceIcon, 1.0f, Colours::white,
juceIcon, 1.0f, Colours::white,
0.5f);
return b;
}
void triggerAnimation()
{
auto width = getWidth();
auto height = getHeight();
bool useWidth = (height > width);
for (auto* component : componentsToAnimate)
{
auto newIndex = (componentsToAnimate.indexOf (component) + 3 * cycleCount)
% componentsToAnimate.size();
auto angle = (float) newIndex * MathConstants<float>::twoPi / (float) componentsToAnimate.size();
auto radius = useWidth ? (float) width * 0.35f
: (float) height * 0.35f;
Rectangle<int> r (getWidth() / 2 + (int) (radius * std::sin (angle)) - 50,
getHeight() / 2 + (int) (radius * std::cos (angle)) - 50,
100, 100);
animator.animateComponent (component, r.reduced (10), 1.0f,
900 + (int) (300 * std::sin (angle)),
false, 0.0, 0.0);
}
++cycleCount;
}
void timerCallback() override
{
if (firstCallback)
{
triggerAnimation();
firstCallback = false;
}
// Go through each of our balls and update their position
for (int i = balls.size(); --i >= 0;)
if (! balls.getUnchecked (i)->step())
balls.remove (i);
// Randomly generate new balls
if (Random::getSystemRandom().nextInt (100) < 4)
addAndMakeVisible (balls.add (new BallComponent (ballGenerator.getBounds().getCentre().toFloat())));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimationDemo)
};

View File

@ -0,0 +1,274 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: BouncingBallWavetableDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Wavetable synthesis with a bouncing ball.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: BouncingBallWavetableDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
class BouncingBallWavetableDemo : public AudioAppComponent,
private Timer
{
public:
//==============================================================================
BouncingBallWavetableDemo()
#ifdef JUCE_DEMO_RUNNER
: AudioAppComponent (getSharedAudioDeviceManager (0, 2))
#endif
{
setSize (600, 600);
for (auto i = 0; i < numElementsInArray (waveValues); ++i)
zeromem (waveValues[i], sizeof (waveValues[i]));
// specify the number of input and output channels that we want to open
setAudioChannels (2, 2);
startTimerHz (60);
}
~BouncingBallWavetableDemo() override
{
shutdownAudio();
}
//==============================================================================
void prepareToPlay (int samplesPerBlockExpected, double newSampleRate) override
{
sampleRate = newSampleRate;
expectedSamplesPerBlock = samplesPerBlockExpected;
}
/* This method generates the actual audio samples.
In this example the buffer is filled with a sine wave whose frequency and
amplitude are controlled by the mouse position.
*/
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
bufferToFill.clearActiveBufferRegion();
for (auto chan = 0; chan < bufferToFill.buffer->getNumChannels(); ++chan)
{
auto ind = waveTableIndex;
auto* channelData = bufferToFill.buffer->getWritePointer (chan, bufferToFill.startSample);
for (auto i = 0; i < bufferToFill.numSamples; ++i)
{
if (isPositiveAndBelow (chan, numElementsInArray (waveValues)))
{
channelData[i] = waveValues[chan][ind % wavetableSize];
++ind;
}
}
}
waveTableIndex = (int) (waveTableIndex + bufferToFill.numSamples) % wavetableSize;
}
void releaseResources() override
{
// This gets automatically called when audio device parameters change
// or device is restarted.
stopTimer();
}
//==============================================================================
void paint (Graphics& g) override
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
auto nextPos = pos + delta;
if (nextPos.x < 10 || nextPos.x + 10 > (float) getWidth())
{
delta.x = -delta.x;
nextPos.x = pos.x + delta.x;
}
if (nextPos.y < 50 || nextPos.y + 10 > (float) getHeight())
{
delta.y = -delta.y;
nextPos.y = pos.y + delta.y;
}
if (! dragging)
{
writeInterpolatedValue (pos, nextPos);
pos = nextPos;
}
else
{
pos = lastMousePosition;
}
// draw a circle
g.setColour (getLookAndFeel().findColour (Slider::thumbColourId));
g.fillEllipse (pos.x, pos.y, 20, 20);
drawWaveform (g, 20.0f, 0);
drawWaveform (g, 40.0f, 1);
}
void drawWaveform (Graphics& g, float y, int channel) const
{
auto pathWidth = 2000;
Path wavePath;
wavePath.startNewSubPath (0.0f, y);
for (auto i = 1; i < pathWidth; ++i)
wavePath.lineTo ((float) i, (1.0f + waveValues[channel][i * numElementsInArray (waveValues[0]) / pathWidth]) * 10.0f);
g.strokePath (wavePath, PathStrokeType (1.0f),
wavePath.getTransformToScaleToFit (Rectangle<float> (0.0f, y, (float) getWidth(), 20.0f), false));
}
// Mouse handling..
void mouseDown (const MouseEvent& e) override
{
lastMousePosition = e.position;
mouseDrag (e);
dragging = true;
}
void mouseDrag (const MouseEvent& e) override
{
dragging = true;
if (e.position != lastMousePosition)
{
// calculate movement vector
delta = e.position - lastMousePosition;
waveValues[0][bufferIndex % wavetableSize] = xToAmplitude (e.position.x);
waveValues[1][bufferIndex % wavetableSize] = yToAmplitude (e.position.y);
++bufferIndex;
lastMousePosition = e.position;
}
}
void mouseUp (const MouseEvent&) override
{
dragging = false;
}
void writeInterpolatedValue (Point<float> lastPosition,
Point<float> currentPosition)
{
Point<float> start, finish;
if (lastPosition.getX() > currentPosition.getX())
{
finish = lastPosition;
start = currentPosition;
}
else
{
start = lastPosition;
finish = currentPosition;
}
for (auto i = 0; i < steps; ++i)
{
auto p = start + ((finish - start) * i) / (int) steps;
auto index = (bufferIndex + i) % wavetableSize;
waveValues[1][index] = yToAmplitude (p.y);
waveValues[0][index] = xToAmplitude (p.x);
}
bufferIndex = (bufferIndex + steps) % wavetableSize;
}
float indexToX (int indexValue) const noexcept
{
return (float) indexValue;
}
float amplitudeToY (float amp) const noexcept
{
return (float) getHeight() - (amp + 1.0f) * (float) getHeight() / 2.0f;
}
float xToAmplitude (float x) const noexcept
{
return jlimit (-1.0f, 1.0f, 2.0f * ((float) getWidth() - x) / (float) getWidth() - 1.0f);
}
float yToAmplitude (float y) const noexcept
{
return jlimit (-1.0f, 1.0f, 2.0f * ((float) getHeight() - y) / (float) getHeight() - 1.0f);
}
void timerCallback() override
{
repaint();
}
private:
//==============================================================================
enum
{
wavetableSize = 36000,
steps = 10
};
Point<float> pos = { 299.0f, 299.0f };
Point<float> delta = { 0.0f, 0.0f };
int waveTableIndex = 0;
int bufferIndex = 0;
double sampleRate = 0.0;
int expectedSamplesPerBlock = 0;
Point<float> lastMousePosition;
float waveValues[2][wavetableSize];
bool dragging = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBallWavetableDemo)
};

24
deps/juce/examples/GUI/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,24 @@
# ==============================================================================
#
# This file is part of the JUCE library.
# Copyright (c) 2020 - Raw Material Software Limited
#
# JUCE is an open source library subject to commercial or open-source
# licensing.
#
# By using JUCE, you agree to the terms of both the JUCE 6 End-User License
# Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
#
# End User License Agreement: www.juce.com/juce-6-licence
# Privacy Policy: www.juce.com/juce-privacy-policy
#
# Or: You may also use this code under the terms of the GPL v3 (see
# www.gnu.org/licenses).
#
# JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
# EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
# DISCLAIMED.
#
# ==============================================================================
_juce_add_pips()

391
deps/juce/examples/GUI/CameraDemo.h vendored Normal file
View File

@ -0,0 +1,391 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: CameraDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Showcases camera features.
dependencies: juce_audio_basics, juce_audio_devices, juce_core, juce_cryptography,
juce_data_structures, juce_events, juce_graphics, juce_gui_basics,
juce_gui_extra, juce_video
exporters: xcode_mac, vs2019, androidstudio, xcode_iphone
moduleFlags: JUCE_USE_CAMERA=1, JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: CameraDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
class CameraDemo : public Component
{
public:
CameraDemo()
{
setOpaque (true);
#if JUCE_ANDROID
// Android requires exclusive access to the audio device when recording videos.
audioDeviceManager.closeAudioDevice();
#endif
addAndMakeVisible (cameraSelectorComboBox);
updateCameraList();
cameraSelectorComboBox.setSelectedId (1);
cameraSelectorComboBox.onChange = [this] { cameraChanged(); };
addAndMakeVisible (snapshotButton);
snapshotButton.onClick = [this] { takeSnapshot(); };
snapshotButton.setEnabled (false);
addAndMakeVisible (recordMovieButton);
recordMovieButton.onClick = [this] { startRecording(); };
recordMovieButton.setEnabled (false);
addAndMakeVisible (lastSnapshot);
cameraSelectorComboBox.setSelectedId (2);
setSize (500, 500);
#if JUCE_IOS || JUCE_ANDROID
setPortraitOrientationEnabled (true);
#endif
}
~CameraDemo() override
{
#if JUCE_IOS || JUCE_ANDROID
setPortraitOrientationEnabled (false);
#endif
#if JUCE_ANDROID
audioDeviceManager.restartLastAudioDevice();
#endif
}
//==============================================================================
void paint (Graphics& g) override
{
g.fillAll (Colours::black);
}
void resized() override
{
auto r = getLocalBounds().reduced (5);
auto top = r.removeFromTop (25);
cameraSelectorComboBox.setBounds (top.removeFromLeft (250));
r.removeFromTop (4);
top = r.removeFromTop (25);
snapshotButton.changeWidthToFitText (24);
snapshotButton.setBounds (top.removeFromLeft (snapshotButton.getWidth()));
top.removeFromLeft (4);
recordMovieButton.changeWidthToFitText (24);
recordMovieButton.setBounds (top.removeFromLeft (recordMovieButton.getWidth()));
r.removeFromTop (4);
auto previewArea = shouldUseLandscapeLayout() ? r.removeFromLeft (r.getWidth() / 2)
: r.removeFromTop (r.getHeight() / 2);
if (cameraPreviewComp.get() != nullptr)
cameraPreviewComp->setBounds (previewArea);
if (shouldUseLandscapeLayout())
r.removeFromLeft (4);
else
r.removeFromTop (4);
lastSnapshot.setBounds (r);
}
private:
//==============================================================================
// if this PIP is running inside the demo runner, we'll use the shared device manager instead
#ifndef JUCE_DEMO_RUNNER
AudioDeviceManager audioDeviceManager;
#else
AudioDeviceManager& audioDeviceManager { getSharedAudioDeviceManager (0, 2) };
#endif
std::unique_ptr<CameraDevice> cameraDevice;
std::unique_ptr<Component> cameraPreviewComp;
ImageComponent lastSnapshot;
ComboBox cameraSelectorComboBox { "Camera" };
TextButton snapshotButton { "Take a snapshot" };
#if ! JUCE_ANDROID && ! JUCE_IOS
TextButton recordMovieButton { "Record a movie (to your desktop)..." };
#else
TextButton recordMovieButton { "Record a movie" };
#endif
bool recordingMovie = false;
File recordingFile;
bool contentSharingPending = false;
void setPortraitOrientationEnabled (bool shouldBeEnabled)
{
auto allowedOrientations = Desktop::getInstance().getOrientationsEnabled();
if (shouldBeEnabled)
allowedOrientations |= Desktop::upright;
else
allowedOrientations &= ~Desktop::upright;
Desktop::getInstance().setOrientationsEnabled (allowedOrientations);
}
bool shouldUseLandscapeLayout() const noexcept
{
#if JUCE_ANDROID || JUCE_IOS
auto orientation = Desktop::getInstance().getCurrentOrientation();
return orientation == Desktop::rotatedClockwise || orientation == Desktop::rotatedAntiClockwise;
#else
return false;
#endif
}
void updateCameraList()
{
cameraSelectorComboBox.clear();
cameraSelectorComboBox.addItem ("No camera", 1);
cameraSelectorComboBox.addSeparator();
auto cameras = CameraDevice::getAvailableDevices();
for (int i = 0; i < cameras.size(); ++i)
cameraSelectorComboBox.addItem (cameras[i], i + 2);
}
void cameraChanged()
{
// This is called when the user chooses a camera from the drop-down list.
#if JUCE_IOS
// On iOS, when switching camera, open the new camera first, so that it can
// share the underlying camera session with the old camera. Otherwise, the
// session would have to be closed first, which can take several seconds.
if (cameraSelectorComboBox.getSelectedId() == 1)
cameraDevice.reset();
#else
cameraDevice.reset();
#endif
cameraPreviewComp.reset();
recordingMovie = false;
if (cameraSelectorComboBox.getSelectedId() > 1)
{
#if JUCE_ANDROID || JUCE_IOS
openCameraAsync();
#else
cameraDeviceOpenResult (CameraDevice::openDevice (cameraSelectorComboBox.getSelectedId() - 2), {});
#endif
}
else
{
snapshotButton .setEnabled (cameraDevice != nullptr && ! contentSharingPending);
recordMovieButton.setEnabled (cameraDevice != nullptr && ! contentSharingPending);
resized();
}
}
void openCameraAsync()
{
SafePointer<CameraDemo> safeThis (this);
CameraDevice::openDeviceAsync (cameraSelectorComboBox.getSelectedId() - 2,
[safeThis] (CameraDevice* device, const String& error) mutable
{
if (safeThis)
safeThis->cameraDeviceOpenResult (device, error);
});
}
void cameraDeviceOpenResult (CameraDevice* device, const String& error)
{
// If camera opening worked, create a preview component for it..
cameraDevice.reset (device);
if (cameraDevice.get() != nullptr)
{
#if JUCE_ANDROID
SafePointer<CameraDemo> safeThis (this);
cameraDevice->onErrorOccurred = [safeThis] (const String& cameraError) mutable { if (safeThis) safeThis->errorOccurred (cameraError); };
#endif
cameraPreviewComp.reset (cameraDevice->createViewerComponent());
addAndMakeVisible (cameraPreviewComp.get());
}
else
{
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Camera open failed",
"Camera open failed, reason: " + error);
}
snapshotButton .setEnabled (cameraDevice.get() != nullptr && ! contentSharingPending);
recordMovieButton.setEnabled (cameraDevice.get() != nullptr && ! contentSharingPending);
resized();
}
void startRecording()
{
if (cameraDevice.get() != nullptr)
{
// The user has clicked the record movie button..
if (! recordingMovie)
{
// Start recording to a file on the user's desktop..
recordingMovie = true;
#if JUCE_ANDROID || JUCE_IOS
recordingFile = File::getSpecialLocation (File::tempDirectory)
#else
recordingFile = File::getSpecialLocation (File::userDesktopDirectory)
#endif
.getNonexistentChildFile ("JuceCameraVideoDemo", CameraDevice::getFileExtension());
#if JUCE_ANDROID
// Android does not support taking pictures while recording video.
snapshotButton.setEnabled (false);
#endif
cameraSelectorComboBox.setEnabled (false);
cameraDevice->startRecordingToFile (recordingFile);
recordMovieButton.setButtonText ("Stop Recording");
}
else
{
// Already recording, so stop...
recordingMovie = false;
cameraDevice->stopRecording();
#if ! JUCE_ANDROID && ! JUCE_IOS
recordMovieButton.setButtonText ("Start recording (to a file on your desktop)");
#else
recordMovieButton.setButtonText ("Record a movie");
#endif
cameraSelectorComboBox.setEnabled (true);
#if JUCE_ANDROID
snapshotButton.setEnabled (true);
#endif
#if JUCE_CONTENT_SHARING
URL url (recordingFile);
snapshotButton .setEnabled (false);
recordMovieButton.setEnabled (false);
contentSharingPending = true;
SafePointer<CameraDemo> safeThis (this);
juce::ContentSharer::getInstance()->shareFiles ({url},
[safeThis] (bool success, const String&) mutable
{
if (safeThis)
safeThis->sharingFinished (success, false);
});
#endif
}
}
}
void takeSnapshot()
{
SafePointer<CameraDemo> safeThis (this);
cameraDevice->takeStillPicture ([safeThis] (const Image& image) mutable { safeThis->imageReceived (image); });
}
// This is called by the camera device when a new image arrives
void imageReceived (const Image& image)
{
if (! image.isValid())
return;
lastSnapshot.setImage (image);
#if JUCE_CONTENT_SHARING
auto imageFile = File::getSpecialLocation (File::tempDirectory).getNonexistentChildFile ("JuceCameraPhotoDemo", ".jpg");
FileOutputStream stream (imageFile);
if (stream.openedOk()
&& JPEGImageFormat().writeImageToStream (image, stream))
{
URL url (imageFile);
snapshotButton .setEnabled (false);
recordMovieButton.setEnabled (false);
contentSharingPending = true;
SafePointer<CameraDemo> safeThis (this);
juce::ContentSharer::getInstance()->shareFiles ({url},
[safeThis] (bool success, const String&) mutable
{
if (safeThis)
safeThis->sharingFinished (success, true);
});
}
#endif
}
void errorOccurred (const String& error)
{
AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon,
"Camera Device Error",
"An error has occurred: " + error + " Camera will be closed.");
cameraDevice.reset();
cameraSelectorComboBox.setSelectedId (1);
snapshotButton .setEnabled (false);
recordMovieButton.setEnabled (false);
}
void sharingFinished (bool success, bool isCapture)
{
AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon,
isCapture ? "Image sharing result" : "Video sharing result",
success ? "Success!" : "Failed!");
contentSharingPending = false;
snapshotButton .setEnabled (true);
recordMovieButton.setEnabled (true);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CameraDemo)
};

201
deps/juce/examples/GUI/CodeEditorDemo.h vendored Normal file
View File

@ -0,0 +1,201 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: CodeEditorDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Displays a code editor.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: CodeEditorDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#if JUCE_ANDROID
#error "This demo is not supported on Android!"
#endif
//==============================================================================
class CodeEditorDemo : public Component,
private FilenameComponentListener
{
public:
CodeEditorDemo()
{
setOpaque (true);
// Create the editor..
editor.reset (new CodeEditorComponent (codeDocument, &cppTokeniser));
addAndMakeVisible (editor.get());
editor->loadContent ("\n"
"/* Code editor demo!\n"
"\n"
" To see a real-world example of the code editor\n"
" in action, have a look at the Projucer!\n"
"\n"
"*/\n"
"\n");
// Create a file chooser control to load files into it..
addAndMakeVisible (fileChooser);
fileChooser.addListener (this);
lookAndFeelChanged();
setSize (500, 500);
}
~CodeEditorDemo() override
{
fileChooser.removeListener (this);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
Colours::lightgrey));
}
void resized() override
{
auto r = getLocalBounds().reduced (8);
fileChooser.setBounds (r.removeFromTop (25));
editor->setBounds (r.withTrimmedTop (8));
}
private:
// this is the document that the editor component is showing
CodeDocument codeDocument;
// this is a tokeniser to apply the C++ syntax highlighting
CPlusPlusCodeTokeniser cppTokeniser;
// the editor component
std::unique_ptr<CodeEditorComponent> editor;
FilenameComponent fileChooser { "File", {}, true, false, false, "*.cpp;*.h;*.hpp;*.c;*.mm;*.m", {},
"Choose a C++ file to open it in the editor" };
//==============================================================================
void filenameComponentChanged (FilenameComponent*) override
{
editor->loadContent (fileChooser.getCurrentFile().loadFileAsString());
}
void lookAndFeelChanged() override
{
if (auto* v4 = dynamic_cast<LookAndFeel_V4*> (&LookAndFeel::getDefaultLookAndFeel()))
{
auto useLight = v4->getCurrentColourScheme() == LookAndFeel_V4::getLightColourScheme();
editor->setColourScheme (useLight ? getLightCodeEditorColourScheme()
: getDarkCodeEditorColourScheme());
}
else
{
editor->setColourScheme (cppTokeniser.getDefaultColourScheme());
}
}
CodeEditorComponent::ColourScheme getDarkCodeEditorColourScheme()
{
struct Type
{
const char* name;
juce::uint32 colour;
};
const Type types[] =
{
{ "Error", 0xffe60000 },
{ "Comment", 0xff72d20c },
{ "Keyword", 0xffee6f6f },
{ "Operator", 0xffc4eb19 },
{ "Identifier", 0xffcfcfcf },
{ "Integer", 0xff42c8c4 },
{ "Float", 0xff885500 },
{ "String", 0xffbc45dd },
{ "Bracket", 0xff058202 },
{ "Punctuation", 0xffcfbeff },
{ "Preprocessor Text", 0xfff8f631 }
};
CodeEditorComponent::ColourScheme cs;
for (auto& t : types)
cs.set (t.name, Colour (t.colour));
return cs;
}
CodeEditorComponent::ColourScheme getLightCodeEditorColourScheme()
{
struct Type
{
const char* name;
juce::uint32 colour;
};
const Type types[] =
{
{ "Error", 0xffcc0000 },
{ "Comment", 0xff00aa00 },
{ "Keyword", 0xff0000cc },
{ "Operator", 0xff225500 },
{ "Identifier", 0xff000000 },
{ "Integer", 0xff880000 },
{ "Float", 0xff885500 },
{ "String", 0xff990099 },
{ "Bracket", 0xff000055 },
{ "Punctuation", 0xff004400 },
{ "Preprocessor Text", 0xff660000 }
};
CodeEditorComponent::ColourScheme cs;
for (auto& t : types)
cs.set (t.name, Colour (t.colour));
return cs;
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CodeEditorDemo)
};

162
deps/juce/examples/GUI/ComponentDemo.h vendored Normal file
View File

@ -0,0 +1,162 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: ComponentDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Displays a grid of lights.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics
exporters: xcode_mac, vs2019
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: ComponentDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
/**
This class represents one of the individual lights in our grid.
*/
class ToggleLightComponent : public Component
{
public:
ToggleLightComponent() {}
void paint (Graphics& g) override
{
// Only shows the red ellipse when the button is on.
if (isOn)
{
g.setColour (getLookAndFeel().findColour (Slider::thumbColourId));
g.fillEllipse (getLocalBounds().toFloat());
}
}
void mouseEnter (const MouseEvent&) override
{
// button toggles state on mouse over.
isOn = ! isOn;
repaint();
}
private:
// member variables for the Component
bool isOn = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToggleLightComponent)
};
//==============================================================================
/**
This is the parent class that holds multiple ToggleLightComponents in a grid.
*/
class ToggleLightGridComponent : public Component
{
public:
ToggleLightGridComponent()
{
// Adds the child light components and makes them visible
// within this component.
// (they currently rely on having a default constructor
// so they don't have to be individually initialised)
for (auto i = 0; i < numX * numY; ++i)
addAndMakeVisible (toggleLights[i]);
}
void resized() override
{
// This creates a grid of rectangles to use as the bounds
// for all of our lights. The grid is defined with the
// width and height of this component.
auto stepX = getWidth() / numX;
auto stepY = getHeight() / numY;
for (auto x = 0; x < numX; ++x)
{
for (auto y = 0; y < numY; ++y)
{
// creates the rectangle (x, y, width, height)
Rectangle<int> elementBounds (x * stepX, y * stepY, stepX, stepY);
// set the size and position of the Toggle light to this rectangle.
toggleLights[x + numX * y].setBounds (elementBounds);
}
}
}
private:
// member variables for the Component
static const int numX = 20;
static const int numY = 20;
ToggleLightComponent toggleLights [numX * numY];
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToggleLightGridComponent)
};
//==============================================================================
/*
This component lives inside our window, and this is where you should put all
your controls and content.
*/
class ComponentDemo : public Component
{
public:
//==============================================================================
ComponentDemo()
{
// add the light grid to out main component.
addAndMakeVisible (lightGrid);
setSize (600, 600);
}
void paint (Graphics&) override {}
void resized() override
{
// set the size of the grid to fill the whole window.
lightGrid.setBounds (getLocalBounds());
}
private:
//==============================================================================
ToggleLightGridComponent lightGrid;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentDemo)
};

View File

@ -0,0 +1,161 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: ComponentTransformsDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Applies transformations to components.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: ComponentTransformsDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "WidgetsDemo.h"
//==============================================================================
class ComponentTransformsDemo : public Component
{
public:
ComponentTransformsDemo()
{
content.reset (new WidgetsDemo (true));
addAndMakeVisible (content.get());
content->setSize (750, 500);
for (int i = 0; i < 3; ++i)
{
auto* d = new CornerDragger();
addAndMakeVisible (draggers.add (d));
}
draggers.getUnchecked (0)->relativePos = { 0.10f, 0.15f };
draggers.getUnchecked (1)->relativePos = { 0.95f, 0.05f };
draggers.getUnchecked (2)->relativePos = { 0.05f, 0.85f };
setSize (800, 600);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
g.setColour (Colours::white);
g.setFont (15.0f);
g.drawFittedText ("Drag the corner-points around to show how complex components can have affine-transforms applied...",
getLocalBounds().removeFromBottom (40).reduced (10, 0), Justification::centred, 3);
}
void resized() override
{
for (auto* d : draggers)
d->setCentrePosition (proportionOfWidth (d->relativePos.x),
proportionOfHeight (d->relativePos.y));
}
void childBoundsChanged (Component* child) override
{
if (dynamic_cast<CornerDragger*> (child) != nullptr)
updateTransform();
}
private:
std::unique_ptr<Component> content;
struct CornerDragger : public Component
{
CornerDragger()
{
setSize (30, 30);
setRepaintsOnMouseActivity (true);
}
void paint (Graphics& g) override
{
g.setColour (Colours::white.withAlpha (isMouseOverOrDragging() ? 0.9f : 0.5f));
g.fillEllipse (getLocalBounds().reduced (3).toFloat());
g.setColour (Colours::darkgreen);
g.drawEllipse (getLocalBounds().reduced (3).toFloat(), 2.0f);
}
void resized() override
{
constrainer.setMinimumOnscreenAmounts (getHeight(), getWidth(), getHeight(), getWidth());
}
void moved() override
{
if (isMouseButtonDown())
relativePos = getBounds().getCentre().toFloat() / Point<int> (getParentWidth(), getParentHeight()).toFloat();
}
void mouseDown (const MouseEvent& e) override { dragger.startDraggingComponent (this, e); }
void mouseDrag (const MouseEvent& e) override { dragger.dragComponent (this, e, &constrainer); }
Point<float> relativePos;
private:
ComponentBoundsConstrainer constrainer;
ComponentDragger dragger;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CornerDragger)
};
OwnedArray<CornerDragger> draggers;
Point<float> getDraggerPos (int index) const
{
return draggers.getUnchecked (index)->getBounds().getCentre().toFloat();
}
void updateTransform()
{
auto p0 = getDraggerPos (0);
auto p1 = getDraggerPos (1);
auto p2 = getDraggerPos (2);
if (p0 != p1 && p1 != p2 && p0 != p2)
content->setTransform (AffineTransform::fromTargetPoints (0, 0, p0.x, p0.y,
(float) content->getWidth(), 0, p1.x, p1.y,
0, (float) content->getHeight(), p2.x, p2.y));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentTransformsDemo)
};

519
deps/juce/examples/GUI/DialogsDemo.h vendored Normal file
View File

@ -0,0 +1,519 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: DialogsDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Displays different types of dialog windows.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: DialogsDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
class DemoBackgroundThread : public ThreadWithProgressWindow
{
public:
DemoBackgroundThread()
: ThreadWithProgressWindow ("busy doing some important things...", true, true)
{
setStatusMessage ("Getting ready...");
}
void run() override
{
setProgress (-1.0); // setting a value beyond the range 0 -> 1 will show a spinning bar..
setStatusMessage ("Preparing to do some stuff...");
wait (2000);
int thingsToDo = 10;
for (int i = 0; i < thingsToDo; ++i)
{
// must check this as often as possible, because this is
// how we know if the user's pressed 'cancel'
if (threadShouldExit())
return;
// this will update the progress bar on the dialog box
setProgress (i / (double) thingsToDo);
setStatusMessage (String (thingsToDo - i) + " things left to do...");
wait (500);
}
setProgress (-1.0); // setting a value beyond the range 0 -> 1 will show a spinning bar..
setStatusMessage ("Finishing off the last few bits and pieces!");
wait (2000);
}
// This method gets called on the message thread once our thread has finished..
void threadComplete (bool userPressedCancel) override
{
const String messageString (userPressedCancel ? "You pressed cancel!" : "Thread finished ok!");
AlertWindow::showAsync (MessageBoxOptions()
.withIconType (MessageBoxIconType::InfoIcon)
.withTitle ("Progress window")
.withMessage (messageString)
.withButton ("OK"),
nullptr);
// ..and clean up by deleting our thread object..
delete this;
}
};
//==============================================================================
class DialogsDemo : public Component
{
public:
enum DialogType
{
plainAlertWindow,
warningAlertWindow,
infoAlertWindow,
questionAlertWindow,
okCancelAlertWindow,
extraComponentsAlertWindow,
calloutBoxWindow,
progressWindow,
loadChooser,
loadWithPreviewChooser,
directoryChooser,
saveChooser,
shareText,
shareFile,
shareImage,
numDialogs
};
DialogsDemo()
{
setOpaque (true);
addAndMakeVisible (nativeButton);
nativeButton.setButtonText ("Use Native Windows");
nativeButton.onClick = [this] { getLookAndFeel().setUsingNativeAlertWindows (nativeButton.getToggleState()); };
StringArray windowNames { "Plain Alert Window",
"Alert Window With Warning Icon",
"Alert Window With Info Icon",
"Alert Window With Question Icon",
"OK Cancel Alert Window",
"Alert Window With Extra Components",
"CalloutBox",
"Thread With Progress Window",
"'Load' File Browser",
"'Load' File Browser With Image Preview",
"'Choose Directory' File Browser",
"'Save' File Browser",
"Share Text",
"Share Files",
"Share Images" };
// warn in case we add any windows
jassert (windowNames.size() == numDialogs);
for (auto windowName : windowNames)
{
auto* newButton = new TextButton();
addAndMakeVisible (windowButtons.add (newButton));
newButton->setButtonText (windowName);
auto index = windowNames.indexOf (windowName);
newButton->onClick = [this, index, newButton] { showWindow (*newButton, static_cast<DialogType> (index)); };
}
setSize (500, 500);
RuntimePermissions::request (RuntimePermissions::readExternalStorage,
[] (bool granted)
{
if (! granted)
AlertWindow::showAsync (MessageBoxOptions()
.withIconType (MessageBoxIconType::WarningIcon)
.withTitle ("Permissions warning")
.withMessage ("External storage access permission not granted, some files"
" may be inaccessible.")
.withButton ("OK"),
nullptr);
});
}
//==============================================================================
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
}
void resized() override
{
auto area = getLocalBounds().reduced (5, 15);
Rectangle<int> topRow;
for (auto* b : windowButtons)
{
auto index = windowButtons.indexOf (b);
if (topRow.getWidth() < 10 || index == loadChooser)
topRow = area.removeFromTop (26);
if (index == progressWindow)
area.removeFromTop (20);
b->setBounds (topRow.removeFromLeft (area.getWidth() / 2).reduced (4, 2));
}
area.removeFromTop (15);
nativeButton.setBounds (area.removeFromTop (24));
}
private:
OwnedArray<TextButton> windowButtons;
ToggleButton nativeButton;
struct AlertBoxResultChosen
{
void operator() (int result) const noexcept
{
AlertWindow::showAsync (MessageBoxOptions()
.withIconType (MessageBoxIconType::InfoIcon)
.withTitle ("Alert Box")
.withMessage ("Result code: " + String (result))
.withButton ("OK"),
nullptr);
}
};
struct AsyncAlertBoxResultChosen
{
void operator() (int result) const noexcept
{
auto& aw = *demo.asyncAlertWindow;
aw.exitModalState (result);
aw.setVisible (false);
if (result == 0)
{
AlertBoxResultChosen{} (result);
return;
}
auto optionIndexChosen = aw.getComboBoxComponent ("option")->getSelectedItemIndex();
auto text = aw.getTextEditorContents ("text");
AlertWindow::showAsync (MessageBoxOptions()
.withIconType (MessageBoxIconType::InfoIcon)
.withTitle ("Alert Box")
.withMessage ("Result code: " + String (result) + newLine
+ "Option index chosen: " + String (optionIndexChosen) + newLine
+ "Text: " + text)
.withButton ("OK"),
nullptr);
}
DialogsDemo& demo;
};
void showWindow (Component& button, DialogType type)
{
if (type >= plainAlertWindow && type <= questionAlertWindow)
{
MessageBoxIconType icon = MessageBoxIconType::NoIcon;
if (type == warningAlertWindow) icon = MessageBoxIconType::WarningIcon;
if (type == infoAlertWindow) icon = MessageBoxIconType::InfoIcon;
if (type == questionAlertWindow) icon = MessageBoxIconType::QuestionIcon;
AlertWindow::showMessageBoxAsync (icon, "This is an AlertWindow",
"And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah.",
"OK");
}
else if (type == okCancelAlertWindow)
{
AlertWindow::showOkCancelBox (MessageBoxIconType::QuestionIcon, "This is an ok/cancel AlertWindow",
"And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah.",
{}, {}, {},
ModalCallbackFunction::create (AlertBoxResultChosen{}));
}
else if (type == calloutBoxWindow)
{
auto colourSelector = std::make_unique<ColourSelector>();
colourSelector->setName ("background");
colourSelector->setCurrentColour (findColour (TextButton::buttonColourId));
colourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
colourSelector->setSize (300, 400);
CallOutBox::launchAsynchronously (std::move (colourSelector), button.getScreenBounds(), nullptr);
}
else if (type == extraComponentsAlertWindow)
{
asyncAlertWindow = std::make_unique<AlertWindow> ("AlertWindow demo..",
"This AlertWindow has a couple of extra components added to show how to add drop-down lists and text entry boxes.",
MessageBoxIconType::QuestionIcon);
asyncAlertWindow->addTextEditor ("text", "enter some text here", "text field:");
asyncAlertWindow->addComboBox ("option", { "option 1", "option 2", "option 3", "option 4" }, "some options");
asyncAlertWindow->addButton ("OK", 1, KeyPress (KeyPress::returnKey, 0, 0));
asyncAlertWindow->addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey, 0, 0));
asyncAlertWindow->enterModalState (true, ModalCallbackFunction::create (AsyncAlertBoxResultChosen { *this }));
}
else if (type == progressWindow)
{
// This will launch our ThreadWithProgressWindow in a modal state. (Our subclass
// will take care of deleting the object when the task has finished)
(new DemoBackgroundThread())->launchThread();
}
else if (type >= loadChooser && type <= saveChooser)
{
auto useNativeVersion = nativeButton.getToggleState();
if (type == loadChooser)
{
fc.reset (new FileChooser ("Choose a file to open...", File::getCurrentWorkingDirectory(),
"*", useNativeVersion));
fc->launchAsync (FileBrowserComponent::canSelectMultipleItems | FileBrowserComponent::openMode
| FileBrowserComponent::canSelectFiles,
[] (const FileChooser& chooser)
{
String chosen;
auto results = chooser.getURLResults();
for (auto result : results)
chosen << (result.isLocalFile() ? result.getLocalFile().getFullPathName()
: result.toString (false)) << "\n";
AlertWindow::showAsync (MessageBoxOptions()
.withIconType (MessageBoxIconType::InfoIcon)
.withTitle ("File Chooser...")
.withMessage ("You picked: " + chosen)
.withButton ("OK"),
nullptr);
});
}
else if (type == loadWithPreviewChooser)
{
imagePreview.setSize (200, 200);
fc.reset (new FileChooser ("Choose an image to open...", File::getCurrentWorkingDirectory(),
"*.jpg;*.jpeg;*.png;*.gif", useNativeVersion));
fc->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles
| FileBrowserComponent::canSelectMultipleItems,
[] (const FileChooser& chooser)
{
String chosen;
auto results = chooser.getURLResults();
for (auto result : results)
chosen << (result.isLocalFile() ? result.getLocalFile().getFullPathName()
: result.toString (false)) << "\n";
AlertWindow::showAsync (MessageBoxOptions()
.withIconType (MessageBoxIconType::InfoIcon)
.withTitle ("File Chooser...")
.withMessage ("You picked: " + chosen)
.withButton ("OK"),
nullptr);
},
&imagePreview);
}
else if (type == saveChooser)
{
auto fileToSave = File::createTempFile ("saveChooserDemo");
if (fileToSave.createDirectory().wasOk())
{
fileToSave = fileToSave.getChildFile ("JUCE.png");
fileToSave.deleteFile();
FileOutputStream outStream (fileToSave);
if (outStream.openedOk())
if (auto inStream = createAssetInputStream ("juce_icon.png"))
outStream.writeFromInputStream (*inStream, -1);
}
fc.reset (new FileChooser ("Choose a file to save...",
File::getCurrentWorkingDirectory().getChildFile (fileToSave.getFileName()),
"*", useNativeVersion));
fc->launchAsync (FileBrowserComponent::saveMode | FileBrowserComponent::canSelectFiles,
[fileToSave] (const FileChooser& chooser)
{
auto result = chooser.getURLResult();
auto name = result.isEmpty() ? String()
: (result.isLocalFile() ? result.getLocalFile().getFullPathName()
: result.toString (true));
// Android and iOS file choosers will create placeholder files for chosen
// paths, so we may as well write into those files.
#if JUCE_ANDROID || JUCE_IOS
if (! result.isEmpty())
{
std::unique_ptr<InputStream> wi (fileToSave.createInputStream());
std::unique_ptr<OutputStream> wo (result.createOutputStream());
if (wi.get() != nullptr && wo.get() != nullptr)
{
auto numWritten = wo->writeFromInputStream (*wi, -1);
jassertquiet (numWritten > 0);
wo->flush();
}
}
#endif
AlertWindow::showAsync (MessageBoxOptions()
.withIconType (MessageBoxIconType::InfoIcon)
.withTitle ("File Chooser...")
.withMessage ("You picked: " + name)
.withButton ("OK"),
nullptr);
});
}
else if (type == directoryChooser)
{
fc.reset (new FileChooser ("Choose a directory...",
File::getCurrentWorkingDirectory(),
"*",
useNativeVersion));
fc->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories,
[] (const FileChooser& chooser)
{
auto result = chooser.getURLResult();
auto name = result.isLocalFile() ? result.getLocalFile().getFullPathName()
: result.toString (true);
AlertWindow::showAsync (MessageBoxOptions()
.withIconType (MessageBoxIconType::InfoIcon)
.withTitle ("File Chooser...")
.withMessage ("You picked: " + name)
.withButton ("OK"),
nullptr);
});
}
}
else if (type == shareText)
{
ContentSharer::getInstance()->shareText ("I love JUCE!",
[] (bool success, const String& error)
{
auto resultString = success ? String ("success") : ("failure\n (error: " + error + ")");
AlertWindow::showAsync (MessageBoxOptions()
.withIconType (MessageBoxIconType::InfoIcon)
.withTitle ("Sharing Text Result")
.withMessage ("Sharing text finished\nwith " + resultString)
.withButton ("OK"),
nullptr);
});
}
else if (type == shareFile)
{
File fileToSave = File::createTempFile ("DialogsDemoSharingTest");
if (fileToSave.createDirectory().wasOk())
{
fileToSave = fileToSave.getChildFile ("SharingDemoFile.txt");
fileToSave.replaceWithText ("Make it fast!");
Array<URL> urls;
urls.add (URL (fileToSave));
ContentSharer::getInstance()->shareFiles (urls,
[] (bool success, const String& error)
{
auto resultString = success ? String ("success") : ("failure\n (error: " + error + ")");
AlertWindow::showAsync (MessageBoxOptions()
.withIconType (MessageBoxIconType::InfoIcon)
.withTitle ("Sharing Files Result")
.withMessage ("Sharing files finished\nwith " + resultString)
.withButton ("OK"),
nullptr);
});
}
}
else if (type == shareImage)
{
auto myImage = getImageFromAssets ("juce_icon.png");
Image myImage2 (Image::RGB, 500, 500, true);
Graphics g (myImage2);
g.setColour (Colours::green);
ColourGradient gradient (Colours::yellow, 170, 170, Colours::cyan, 170, 20, true);
g.setGradientFill (gradient);
g.fillEllipse (20, 20, 300, 300);
Array<Image> images { myImage, myImage2 };
ContentSharer::getInstance()->shareImages (images,
[] (bool success, const String& error)
{
String resultString = success ? String ("success")
: ("failure\n (error: " + error + ")");
AlertWindow::showAsync (MessageBoxOptions()
.withIconType (MessageBoxIconType::InfoIcon)
.withTitle ("Sharing Images Result")
.withMessage ("Sharing images finished\nwith " + resultString)
.withButton ("OK"),
nullptr);
});
}
}
ImagePreviewComponent imagePreview;
std::unique_ptr<FileChooser> fc;
std::unique_ptr<AlertWindow> asyncAlertWindow;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DialogsDemo)
};

320
deps/juce/examples/GUI/FlexBoxDemo.h vendored Normal file
View File

@ -0,0 +1,320 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: FlexBoxDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Responsive layouts using FlexBox.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: FlexBoxDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
struct DemoFlexPanel : public Component
{
DemoFlexPanel (Colour col, FlexItem& item)
: flexItem (item), colour (col)
{
int x = 70;
int y = 3;
setupTextEditor (flexOrderEditor, { x, y, 20, 18 }, "0", [this] { flexItem.order = (int) flexOrderEditor.getText().getFloatValue(); });
addLabel ("order", flexOrderEditor);
y += 20;
setupTextEditor (flexGrowEditor, { x, y, 20, 18 }, "0", [this] { flexItem.flexGrow = flexGrowEditor.getText().getFloatValue(); });
addLabel ("flex-grow", flexGrowEditor);
y += 20;
setupTextEditor (flexShrinkEditor, { x, y, 20, 18 }, "1", [this] { flexItem.flexShrink = flexShrinkEditor.getText().getFloatValue(); });
addLabel ("flex-shrink", flexShrinkEditor);
y += 20;
setupTextEditor (flexBasisEditor, { x, y, 33, 18 }, "100", [this] { flexItem.flexBasis = flexBasisEditor.getText().getFloatValue(); });
addLabel ("flex-basis", flexBasisEditor);
y += 20;
alignSelfCombo.addItem ("auto", 1);
alignSelfCombo.addItem ("flex-start", 2);
alignSelfCombo.addItem ("flex-end", 3);
alignSelfCombo.addItem ("center", 4);
alignSelfCombo.addItem ("stretch", 5);
alignSelfCombo.setBounds (x, y, 90, 18);
alignSelfCombo.onChange = [this] { updateAssignSelf(); };
alignSelfCombo.setSelectedId (5);
alignSelfCombo.setColour (ComboBox::outlineColourId, Colours::transparentBlack);
addAndMakeVisible (alignSelfCombo);
addLabel ("align-self", alignSelfCombo);
}
void setupTextEditor (TextEditor& te, Rectangle<int> b, StringRef initialText, std::function<void()> updateFn)
{
te.setBounds (b);
te.setText (initialText);
te.onTextChange = [this, updateFn]
{
updateFn();
refreshLayout();
};
addAndMakeVisible (te);
}
void addLabel (const String& name, Component& target)
{
auto label = new Label (name, name);
label->attachToComponent (&target, true);
labels.add (label);
addAndMakeVisible (label);
}
void updateAssignSelf()
{
switch (alignSelfCombo.getSelectedId())
{
case 1: flexItem.alignSelf = FlexItem::AlignSelf::autoAlign; break;
case 2: flexItem.alignSelf = FlexItem::AlignSelf::flexStart; break;
case 3: flexItem.alignSelf = FlexItem::AlignSelf::flexEnd; break;
case 4: flexItem.alignSelf = FlexItem::AlignSelf::center; break;
case 5: flexItem.alignSelf = FlexItem::AlignSelf::stretch; break;
default: break;
}
refreshLayout();
}
void refreshLayout()
{
if (auto parent = getParentComponent())
parent->resized();
}
void paint (Graphics& g) override
{
auto r = getLocalBounds();
g.setColour (colour);
g.fillRect (r);
g.setColour (Colours::black);
g.drawFittedText ("w: " + String (r.getWidth()) + newLine + "h: " + String (r.getHeight()),
r.reduced (4), Justification::bottomRight, 2);
}
void lookAndFeelChanged() override
{
flexOrderEditor .applyFontToAllText (flexOrderEditor .getFont());
flexGrowEditor .applyFontToAllText (flexGrowEditor .getFont());
flexShrinkEditor.applyFontToAllText (flexShrinkEditor.getFont());
flexBasisEditor .applyFontToAllText (flexBasisEditor .getFont());
}
FlexItem& flexItem;
TextEditor flexOrderEditor, flexGrowEditor, flexShrinkEditor, flexBasisEditor;
ComboBox alignSelfCombo;
Colour colour;
OwnedArray<Label> labels;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoFlexPanel)
};
//==============================================================================
struct FlexBoxDemo : public juce::Component
{
FlexBoxDemo()
{
setupPropertiesPanel();
setupFlexBoxItems();
setSize (1000, 500);
}
void resized() override
{
flexBox.performLayout (getFlexBoxBounds());
}
Rectangle<float> getFlexBoxBounds() const
{
return getLocalBounds().withTrimmedLeft (300)
.reduced (10)
.toFloat();
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
Colours::lightgrey));
g.setColour (Colours::white);
g.fillRect (getFlexBoxBounds());
}
void setupPropertiesPanel()
{
auto directionGroup = addControl (new GroupComponent ("direction", "flex-direction"));
directionGroup->setBounds (10, 30, 140, 110);
int i = 0;
int groupID = 1234;
int leftMargin = 15;
int topMargin = 45;
createToggleButton ("row", groupID, leftMargin, topMargin + i++ * 22, true, [this] { flexBox.flexDirection = FlexBox::Direction::row; }).setToggleState (true, dontSendNotification);
createToggleButton ("row-reverse", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.flexDirection = FlexBox::Direction::rowReverse; });
createToggleButton ("column", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.flexDirection = FlexBox::Direction::column; });
createToggleButton ("column-reverse", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.flexDirection = FlexBox::Direction::columnReverse; });
auto wrapGroup = addControl (new GroupComponent ("wrap", "flex-wrap"));
wrapGroup->setBounds (160, 30, 140, 110);
i = 0;
++groupID;
leftMargin = 165;
createToggleButton ("nowrap", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.flexWrap = FlexBox::Wrap::noWrap; });
createToggleButton ("wrap", groupID, leftMargin, topMargin + i++ * 22, true, [this] { flexBox.flexWrap = FlexBox::Wrap::wrap; });
createToggleButton ("wrap-reverse", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.flexWrap = FlexBox::Wrap::wrapReverse; });
auto justifyGroup = addControl (new GroupComponent ("justify", "justify-content"));
justifyGroup->setBounds (10, 150, 140, 140);
i = 0;
++groupID;
leftMargin = 15;
topMargin = 165;
createToggleButton ("flex-start", groupID, leftMargin, topMargin + i++ * 22, true, [this] { flexBox.justifyContent = FlexBox::JustifyContent::flexStart; });
createToggleButton ("flex-end", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.justifyContent = FlexBox::JustifyContent::flexEnd; });
createToggleButton ("center", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.justifyContent = FlexBox::JustifyContent::center; });
createToggleButton ("space-between", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.justifyContent = FlexBox::JustifyContent::spaceBetween; });
createToggleButton ("space-around", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.justifyContent = FlexBox::JustifyContent::spaceAround; });
auto alignGroup = addControl (new GroupComponent ("align", "align-items"));
alignGroup->setBounds (160, 150, 140, 140);
i = 0;
++groupID;
leftMargin = 165;
topMargin = 165;
createToggleButton ("stretch", groupID, leftMargin, topMargin + i++ * 22, true, [this] { flexBox.alignItems = FlexBox::AlignItems::stretch; });
createToggleButton ("flex-start", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.alignItems = FlexBox::AlignItems::flexStart; });
createToggleButton ("flex-end", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.alignItems = FlexBox::AlignItems::flexEnd; });
createToggleButton ("center", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.alignItems = FlexBox::AlignItems::center; });
auto alignContentGroup = addControl (new GroupComponent ("content", "align-content"));
alignContentGroup->setBounds (10, 300, 140, 160);
i = 0;
++groupID;
leftMargin = 15;
topMargin = 315;
createToggleButton ("stretch", groupID, leftMargin, topMargin + i++ * 22, true, [this] { flexBox.alignContent = FlexBox::AlignContent::stretch; });
createToggleButton ("flex-start", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.alignContent = FlexBox::AlignContent::flexStart; });
createToggleButton ("flex-end", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.alignContent = FlexBox::AlignContent::flexEnd; });
createToggleButton ("center", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.alignContent = FlexBox::AlignContent::center; });
createToggleButton ("space-between", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.alignContent = FlexBox::AlignContent::spaceBetween; });
createToggleButton ("space-around", groupID, leftMargin, topMargin + i++ * 22, false, [this] { flexBox.alignContent = FlexBox::AlignContent::spaceAround; });
}
void setupFlexBoxItems()
{
addItem (Colours::orange);
addItem (Colours::aqua);
addItem (Colours::lightcoral);
addItem (Colours::aquamarine);
addItem (Colours::forestgreen);
}
void addItem (Colour colour)
{
flexBox.items.add (FlexItem (100, 150)
.withMargin (10)
.withWidth (200));
auto& flexItem = flexBox.items.getReference (flexBox.items.size() - 1);
auto panel = panels.add (new DemoFlexPanel (colour, flexItem));
flexItem.associatedComponent = panel;
addAndMakeVisible (panel);
}
ToggleButton& createToggleButton (StringRef text, int groupID, int x, int y, bool toggleOn, std::function<void()> fn)
{
auto* tb = buttons.add (new ToggleButton());
tb->setButtonText (text);
tb->setRadioGroupId (groupID);
tb->setToggleState (toggleOn, dontSendNotification);
tb->onClick = [this, fn]
{
fn();
resized();
};
tb->setBounds (x, y, 130, 22);
addAndMakeVisible (tb);
return *tb;
}
template <typename ComponentType>
ComponentType* addControl (ComponentType* newControlComp)
{
controls.add (newControlComp);
addAndMakeVisible (newControlComp);
return newControlComp;
}
FlexBox flexBox;
OwnedArray<DemoFlexPanel> panels;
OwnedArray<Component> controls;
OwnedArray<ToggleButton> buttons;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlexBoxDemo)
};

352
deps/juce/examples/GUI/FontsDemo.h vendored Normal file
View File

@ -0,0 +1,352 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: FontsDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Displays different font styles and types.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: FontsDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
class FontsDemo : public Component,
private ListBoxModel,
private Slider::Listener
{
public:
FontsDemo()
{
setOpaque (true);
addAndMakeVisible (listBox);
addAndMakeVisible (demoTextBox);
addAndMakeVisible (heightSlider);
addAndMakeVisible (heightLabel);
addAndMakeVisible (kerningLabel);
addAndMakeVisible (kerningSlider);
addAndMakeVisible (scaleLabel);
addAndMakeVisible (horizontalJustificationLabel);
addAndMakeVisible (verticalJustificationLabel);
addAndMakeVisible (scaleSlider);
addAndMakeVisible (boldToggle);
addAndMakeVisible (italicToggle);
addAndMakeVisible (underlineToggle);
addAndMakeVisible (styleBox);
addAndMakeVisible (horizontalJustificationBox);
addAndMakeVisible (verticalJustificationBox);
addAndMakeVisible (resetButton);
kerningLabel .attachToComponent (&kerningSlider, true);
heightLabel .attachToComponent (&heightSlider, true);
scaleLabel .attachToComponent (&scaleSlider, true);
styleLabel .attachToComponent (&styleBox, true);
horizontalJustificationLabel.attachToComponent (&horizontalJustificationBox, true);
verticalJustificationLabel .attachToComponent (&verticalJustificationBox, true);
heightSlider .addListener (this);
kerningSlider.addListener (this);
scaleSlider .addListener (this);
boldToggle .onClick = [this] { refreshPreviewBoxFont(); };
italicToggle .onClick = [this] { refreshPreviewBoxFont(); };
underlineToggle.onClick = [this] { refreshPreviewBoxFont(); };
styleBox .onChange = [this] { refreshPreviewBoxFont(); };
Font::findFonts (fonts); // Generate the list of fonts
listBox.setTitle ("Fonts");
listBox.setRowHeight (20);
listBox.setModel (this); // Tell the listbox where to get its data model
listBox.setColour (ListBox::textColourId, Colours::black);
listBox.setColour (ListBox::backgroundColourId, Colours::white);
heightSlider .setRange (3.0, 150.0, 0.01);
scaleSlider .setRange (0.2, 3.0, 0.01);
kerningSlider.setRange (-2.0, 2.0, 0.01);
// set up the layout and resizer bars..
verticalLayout.setItemLayout (0, -0.2, -0.8, -0.35); // width of the font list must be
// between 20% and 80%, preferably 50%
verticalLayout.setItemLayout (1, 8, 8, 8); // the vertical divider drag-bar thing is always 8 pixels wide
verticalLayout.setItemLayout (2, 150, -1.0, -0.65); // the components on the right must be
// at least 150 pixels wide, preferably 50% of the total width
verticalDividerBar.reset (new StretchableLayoutResizerBar (&verticalLayout, 1, true));
addAndMakeVisible (verticalDividerBar.get());
// ..and pick a random font to select initially
listBox.selectRow (Random::getSystemRandom().nextInt (fonts.size()));
demoTextBox.setMultiLine (true);
demoTextBox.setReturnKeyStartsNewLine (true);
demoTextBox.setText ("Aa Bb Cc Dd Ee Ff Gg Hh Ii\n"
"Jj Kk Ll Mm Nn Oo Pp Qq Rr\n"
"Ss Tt Uu Vv Ww Xx Yy Zz\n"
"0123456789\n\n"
"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt "
"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco "
"laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in "
"voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat "
"non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
demoTextBox.setCaretPosition (0);
demoTextBox.setColour (TextEditor::textColourId, Colours::black);
demoTextBox.setColour (TextEditor::backgroundColourId, Colours::white);
demoTextBox.setWhitespaceUnderlined (false);
resetButton.onClick = [this] { resetToDefaultParameters(); };
setupJustificationOptions();
resetToDefaultParameters();
setSize (750, 750);
}
//==============================================================================
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
}
void resized() override
{
auto r = getLocalBounds().reduced (5);
// lay out the list box and vertical divider..
Component* vcomps[] = { &listBox, verticalDividerBar.get(), nullptr };
verticalLayout.layOutComponents (vcomps, 3,
r.getX(), r.getY(), r.getWidth(), r.getHeight(),
false, // lay out side-by-side
true); // resize the components' heights as well as widths
r.removeFromLeft (verticalDividerBar->getRight());
resetButton.setBounds (r.removeFromBottom (30).reduced (jmax (20, r.getWidth() / 5), 0));
r.removeFromBottom (8);
const int labelWidth = 60;
auto styleArea = r.removeFromBottom (26);
styleArea.removeFromLeft (labelWidth);
styleBox.setBounds (styleArea);
r.removeFromBottom (8);
auto row = r.removeFromBottom (30);
row.removeFromLeft (labelWidth);
auto toggleWidth = row.getWidth() / 3;
boldToggle .setBounds (row.removeFromLeft (toggleWidth));
italicToggle .setBounds (row.removeFromLeft (toggleWidth));
underlineToggle.setBounds (row);
r.removeFromBottom (8);
horizontalJustificationBox.setBounds (r.removeFromBottom (30).withTrimmedLeft (labelWidth * 3));
r.removeFromBottom (8);
verticalJustificationBox.setBounds (r.removeFromBottom (30).withTrimmedLeft (labelWidth * 3));
r.removeFromBottom (8);
scaleSlider.setBounds (r.removeFromBottom (30).withTrimmedLeft (labelWidth));
r.removeFromBottom (8);
kerningSlider.setBounds (r.removeFromBottom (30).withTrimmedLeft (labelWidth));
r.removeFromBottom (8);
heightSlider.setBounds (r.removeFromBottom (30).withTrimmedLeft (labelWidth));
r.removeFromBottom (8);
demoTextBox.setBounds (r);
}
void sliderValueChanged (Slider* sliderThatWasMoved) override
{
if (sliderThatWasMoved == &heightSlider) refreshPreviewBoxFont();
else if (sliderThatWasMoved == &kerningSlider) refreshPreviewBoxFont();
else if (sliderThatWasMoved == &scaleSlider) refreshPreviewBoxFont();
}
// The following methods implement the ListBoxModel virtual methods:
int getNumRows() override
{
return fonts.size();
}
void paintListBoxItem (int rowNumber, Graphics& g,
int width, int height, bool rowIsSelected) override
{
if (rowIsSelected)
g.fillAll (Colours::lightblue);
auto font = fonts[rowNumber];
AttributedString s;
s.setWordWrap (AttributedString::none);
s.setJustification (Justification::centredLeft);
s.append (getNameForRow (rowNumber), font.withHeight ((float) height * 0.7f), Colours::black);
s.append (" " + font.getTypefaceName(), Font ((float) height * 0.5f, Font::italic), Colours::grey);
s.draw (g, Rectangle<int> (width, height).expanded (-4, 50).toFloat());
}
String getNameForRow (int rowNumber) override
{
return fonts[rowNumber].getTypefaceName();
}
void selectedRowsChanged (int /*lastRowselected*/) override
{
refreshPreviewBoxFont();
}
private:
Array<Font> fonts;
StringArray currentStyleList;
ListBox listBox;
TextEditor demoTextBox;
const double defaultScale = 1.0, defaultHeight = 20.0, defaultKerning = 0.0;
const bool defaultBold = false, defaultItalic = false, defaultUnderlined = false;
const int defaultStyle = 0, defaultHorizontalJustification = 0, defaultVerticalJustification = 0;
Label heightLabel { {}, "Height:" },
kerningLabel { {}, "Kerning:" },
scaleLabel { {}, "Scale:" },
styleLabel { {}, "Style:" },
horizontalJustificationLabel { {}, "Justification (horizontal):" },
verticalJustificationLabel { {}, "Justification (vertical):" };
ToggleButton boldToggle { "Bold" },
italicToggle { "Italic" },
underlineToggle { "Underlined" };
TextButton resetButton { "Reset" };
Slider heightSlider, kerningSlider, scaleSlider;
ComboBox styleBox, horizontalJustificationBox, verticalJustificationBox;
StretchableLayoutManager verticalLayout;
std::unique_ptr<StretchableLayoutResizerBar> verticalDividerBar;
StringArray horizontalJustificationStrings { "Left", "Centred", "Right" },
verticalJustificationStrings { "Top", "Centred", "Bottom" };
Array<int> horizontalJustificationFlags { Justification::left, Justification::horizontallyCentred, Justification::right },
verticalJustificationFlags { Justification::top, Justification::verticallyCentred, Justification::bottom};
//==============================================================================
void resetToDefaultParameters()
{
scaleSlider .setValue (defaultScale);
heightSlider .setValue (defaultHeight);
kerningSlider.setValue (defaultKerning);
boldToggle .setToggleState (defaultBold, sendNotificationSync);
italicToggle .setToggleState (defaultItalic, sendNotificationSync);
underlineToggle.setToggleState (defaultUnderlined, sendNotificationSync);
styleBox.setSelectedItemIndex (defaultStyle);
horizontalJustificationBox.setSelectedItemIndex (defaultHorizontalJustification);
verticalJustificationBox .setSelectedItemIndex (defaultVerticalJustification);
}
void setupJustificationOptions()
{
horizontalJustificationBox.addItemList (horizontalJustificationStrings, 1);
verticalJustificationBox .addItemList (verticalJustificationStrings, 1);
auto updateJustification = [this]()
{
auto horizontalIndex = horizontalJustificationBox.getSelectedItemIndex();
auto verticalIndex = verticalJustificationBox.getSelectedItemIndex();
auto horizontalJustification = horizontalJustificationFlags[horizontalIndex];
auto verticalJustification = verticalJustificationFlags[verticalIndex];
demoTextBox.setJustification (horizontalJustification | verticalJustification);
};
horizontalJustificationBox.onChange = updateJustification;
verticalJustificationBox .onChange = updateJustification;
}
void refreshPreviewBoxFont()
{
auto bold = boldToggle .getToggleState();
auto italic = italicToggle.getToggleState();
auto useStyle = ! (bold || italic);
auto font = fonts[listBox.getSelectedRow()];
font = font.withPointHeight ((float) heightSlider .getValue())
.withExtraKerningFactor ((float) kerningSlider.getValue())
.withHorizontalScale ((float) scaleSlider .getValue());
if (bold) font = font.boldened();
if (italic) font = font.italicised();
updateStylesList (font);
styleBox.setEnabled (useStyle);
if (useStyle)
font = font.withTypefaceStyle (styleBox.getText());
font.setUnderline (underlineToggle.getToggleState());
demoTextBox.applyFontToAllText (font);
}
void updateStylesList (const Font& newFont)
{
auto newStyles = newFont.getAvailableStyles();
if (newStyles != currentStyleList)
{
currentStyleList = newStyles;
styleBox.clear();
styleBox.addItemList (newStyles, 1);
styleBox.setSelectedItemIndex (defaultStyle);
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FontsDemo)
};

736
deps/juce/examples/GUI/GraphicsDemo.h vendored Normal file
View File

@ -0,0 +1,736 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: GraphicsDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Showcases various graphics features.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: GraphicsDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
/** Holds the various toggle buttons for the animation modes. */
class ControllersComponent : public Component
{
public:
ControllersComponent()
{
setOpaque (true);
initialiseToggle (animatePosition, "Animate Position", true);
initialiseToggle (animateRotation, "Animate Rotation", true);
initialiseToggle (animateSize, "Animate Size", false);
initialiseToggle (animateShear, "Animate Shearing", false);
initialiseToggle (animateAlpha, "Animate Alpha", false);
initialiseToggle (clipToRectangle, "Clip to Rectangle", false);
initialiseToggle (clipToPath, "Clip to Path", false);
initialiseToggle (clipToImage, "Clip to Image", false);
initialiseToggle (quality, "Higher quality image interpolation", false);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
}
void resized() override
{
auto r = getLocalBounds().reduced (4);
int buttonHeight = 22;
auto columns = r.removeFromTop (buttonHeight * 4);
auto col = columns.removeFromLeft (200);
animatePosition.setBounds (col.removeFromTop (buttonHeight));
animateRotation.setBounds (col.removeFromTop (buttonHeight));
animateSize .setBounds (col.removeFromTop (buttonHeight));
animateShear .setBounds (col.removeFromTop (buttonHeight));
columns.removeFromLeft (20);
col = columns.removeFromLeft (200);
animateAlpha .setBounds (col.removeFromTop (buttonHeight));
clipToRectangle.setBounds (col.removeFromTop (buttonHeight));
clipToPath .setBounds (col.removeFromTop (buttonHeight));
clipToImage .setBounds (col.removeFromTop (buttonHeight));
r.removeFromBottom (6);
quality.setBounds (r.removeFromTop (buttonHeight));
}
void initialiseToggle (ToggleButton& b, const char* name, bool on)
{
addAndMakeVisible (b);
b.setButtonText (name);
b.setToggleState (on, dontSendNotification);
}
ToggleButton animateRotation, animatePosition, animateAlpha, animateSize, animateShear;
ToggleButton clipToRectangle, clipToPath, clipToImage, quality;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ControllersComponent)
};
//==============================================================================
class GraphicsDemoBase : public Component
{
public:
GraphicsDemoBase (ControllersComponent& cc, const String& name)
: Component (name),
controls (cc)
{
displayFont = Font (Font::getDefaultMonospacedFontName(), 12.0f, Font::bold);
}
AffineTransform getTransform()
{
auto hw = 0.5f * (float) getWidth();
auto hh = 0.5f * (float) getHeight();
AffineTransform t;
if (controls.animateRotation.getToggleState())
t = t.rotated (rotation.getValue() * MathConstants<float>::twoPi);
if (controls.animateSize.getToggleState())
t = t.scaled (0.3f + size.getValue() * 2.0f);
if (controls.animatePosition.getToggleState())
t = t.translated (hw + hw * (offsetX.getValue() - 0.5f),
hh + hh * (offsetY.getValue() - 0.5f));
else
t = t.translated (hw, hh);
if (controls.animateShear.getToggleState())
t = t.sheared (shear.getValue() * 2.0f - 1.0f, 0.0f);
return t;
}
float getAlpha() const
{
if (controls.animateAlpha.getToggleState())
return alpha.getValue();
return 1.0f;
}
void paint (Graphics& g) override
{
auto startTime = 0.0;
{
// A ScopedSaveState will return the Graphics context to the state it was at the time of
// construction when it goes out of scope. We use it here to avoid clipping the fps text
const Graphics::ScopedSaveState state (g);
if (controls.clipToRectangle.getToggleState()) clipToRectangle (g);
if (controls.clipToPath .getToggleState()) clipToPath (g);
if (controls.clipToImage .getToggleState()) clipToImage (g);
g.setImageResamplingQuality (controls.quality.getToggleState() ? Graphics::highResamplingQuality
: Graphics::mediumResamplingQuality);
// take a note of the time before the render
startTime = Time::getMillisecondCounterHiRes();
// then let the demo draw itself..
drawDemo (g);
}
auto now = Time::getMillisecondCounterHiRes();
auto filtering = 0.08;
auto elapsedMs = now - startTime;
averageTimeMs += (elapsedMs - averageTimeMs) * filtering;
auto sinceLastRender = now - lastRenderStartTime;
lastRenderStartTime = now;
auto effectiveFPS = 1000.0 / averageTimeMs;
auto actualFPS = sinceLastRender > 0 ? (1000.0 / sinceLastRender) : 0;
averageActualFPS += (actualFPS - averageActualFPS) * filtering;
GlyphArrangement ga;
ga.addFittedText (displayFont,
"Time: " + String (averageTimeMs, 2)
+ " ms\nEffective FPS: " + String (effectiveFPS, 1)
+ "\nActual FPS: " + String (averageActualFPS, 1),
0, 10.0f, (float) getWidth() - 10.0f, (float) getHeight(), Justification::topRight, 3);
g.setColour (Colours::white.withAlpha (0.5f));
g.fillRect (ga.getBoundingBox (0, ga.getNumGlyphs(), true).getSmallestIntegerContainer().expanded (4));
g.setColour (Colours::black);
ga.draw (g);
}
virtual void drawDemo (Graphics&) = 0;
void clipToRectangle (Graphics& g)
{
auto w = getWidth() / 2;
auto h = getHeight() / 2;
auto x = (int) ((float) w * clipRectX.getValue());
auto y = (int) ((float) h * clipRectY.getValue());
g.reduceClipRegion (x, y, w, h);
}
void clipToPath (Graphics& g)
{
auto pathSize = (float) jmin (getWidth(), getHeight());
Path p;
p.addStar (Point<float> (clipPathX.getValue(),
clipPathY.getValue()) * pathSize,
7,
pathSize * (0.5f + clipPathDepth.getValue()),
pathSize * 0.5f,
clipPathAngle.getValue());
g.reduceClipRegion (p, AffineTransform());
}
void clipToImage (Graphics& g)
{
if (! clipImage.isValid())
createClipImage();
AffineTransform transform (AffineTransform::translation ((float) clipImage.getWidth() / -2.0f,
(float) clipImage.getHeight() / -2.0f)
.rotated (clipImageAngle.getValue() * MathConstants<float>::twoPi)
.scaled (2.0f + clipImageSize.getValue() * 3.0f)
.translated ((float) getWidth() * 0.5f,
(float) getHeight() * 0.5f));
g.reduceClipRegion (clipImage, transform);
}
void createClipImage()
{
clipImage = Image (Image::ARGB, 300, 300, true);
Graphics g (clipImage);
g.setGradientFill (ColourGradient (Colours::transparentBlack, 0, 0,
Colours::black, 0, 300, false));
for (int i = 0; i < 20; ++i)
g.fillRect (Random::getSystemRandom().nextInt (200),
Random::getSystemRandom().nextInt (200),
Random::getSystemRandom().nextInt (100),
Random::getSystemRandom().nextInt (100));
}
//==============================================================================
ControllersComponent& controls;
SlowerBouncingNumber offsetX, offsetY, rotation, size, shear, alpha, clipRectX,
clipRectY, clipPathX, clipPathY, clipPathDepth, clipPathAngle,
clipImageX, clipImageY, clipImageAngle, clipImageSize;
double lastRenderStartTime = 0.0, averageTimeMs = 0.0, averageActualFPS = 0.0;
Image clipImage;
Font displayFont;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GraphicsDemoBase)
};
//==============================================================================
class RectangleFillTypesDemo : public GraphicsDemoBase
{
public:
RectangleFillTypesDemo (ControllersComponent& cc)
: GraphicsDemoBase (cc, "Fill Types: Rectangles")
{}
void drawDemo (Graphics& g) override
{
g.addTransform (getTransform());
const int rectSize = jmin (getWidth(), getHeight()) / 2 - 20;
g.setColour (colour1.withAlpha (getAlpha()));
g.fillRect (-rectSize, -rectSize, rectSize, rectSize);
g.setGradientFill (ColourGradient (colour1, 10.0f, (float) -rectSize,
colour2, 10.0f + (float) rectSize, 0.0f, false));
g.setOpacity (getAlpha());
g.fillRect (10, -rectSize, rectSize, rectSize);
g.setGradientFill (ColourGradient (colour1, (float) rectSize * -0.5f, 10.0f + (float) rectSize * 0.5f,
colour2, 0, 10.0f + (float) rectSize, true));
g.setOpacity (getAlpha());
g.fillRect (-rectSize, 10, rectSize, rectSize);
g.setGradientFill (ColourGradient (colour1, 10.0f, 10.0f,
colour2, 10.0f + (float) rectSize, 10.0f + (float) rectSize, false));
g.setOpacity (getAlpha());
g.drawRect (10, 10, rectSize, rectSize, 5);
}
Colour colour1 { Colours::red }, colour2 { Colours::green };
};
//==============================================================================
class PathsDemo : public GraphicsDemoBase
{
public:
PathsDemo (ControllersComponent& cc, bool linear, bool radial)
: GraphicsDemoBase (cc, String ("Paths") + (radial ? ": Radial Gradients"
: (linear ? ": Linear Gradients"
: ": Solid"))),
useLinearGradient (linear), useRadialGradient (radial)
{
logoPath = getJUCELogoPath();
// rescale the logo path so that it's centred about the origin and has the right size.
logoPath.applyTransform (RectanglePlacement (RectanglePlacement::centred)
.getTransformToFit (logoPath.getBounds(),
Rectangle<float> (-120.0f, -120.0f, 240.0f, 240.0f)));
// Surround it with some other shapes..
logoPath.addStar ({ -300.0f, -50.0f }, 7, 30.0f, 70.0f, 0.1f);
logoPath.addStar ({ 300.0f, 50.0f }, 6, 40.0f, 70.0f, 0.1f);
logoPath.addEllipse (-100.0f, 150.0f, 200.0f, 140.0f);
logoPath.addRectangle (-100.0f, -280.0f, 200.0f, 140.0f);
}
void drawDemo (Graphics& g) override
{
auto& p = logoPath;
if (useLinearGradient || useRadialGradient)
{
Colour c1 (gradientColours[0].getValue(), gradientColours[1].getValue(), gradientColours[2].getValue(), 1.0f);
Colour c2 (gradientColours[3].getValue(), gradientColours[4].getValue(), gradientColours[5].getValue(), 1.0f);
Colour c3 (gradientColours[6].getValue(), gradientColours[7].getValue(), gradientColours[8].getValue(), 1.0f);
auto x1 = gradientPositions[0].getValue() * (float) getWidth() * 0.25f;
auto y1 = gradientPositions[1].getValue() * (float) getHeight() * 0.25f;
auto x2 = gradientPositions[2].getValue() * (float) getWidth() * 0.75f;
auto y2 = gradientPositions[3].getValue() * (float) getHeight() * 0.75f;
ColourGradient gradient (c1, x1, y1,
c2, x2, y2,
useRadialGradient);
gradient.addColour (gradientIntermediate.getValue(), c3);
g.setGradientFill (gradient);
}
else
{
g.setColour (Colours::blue);
}
g.setOpacity (getAlpha());
g.fillPath (p, getTransform());
}
Path logoPath;
bool useLinearGradient, useRadialGradient;
SlowerBouncingNumber gradientColours[9], gradientPositions[4], gradientIntermediate;
};
//==============================================================================
class StrokesDemo : public GraphicsDemoBase
{
public:
StrokesDemo (ControllersComponent& cc)
: GraphicsDemoBase (cc, "Paths: Stroked")
{}
void drawDemo (Graphics& g) override
{
auto w = (float) getWidth();
auto h = (float) getHeight();
Path p;
p.startNewSubPath (points[0].getValue() * w,
points[1].getValue() * h);
for (int i = 2; i < numElementsInArray (points); i += 4)
p.quadraticTo (points[i] .getValue() * w,
points[i + 1].getValue() * h,
points[i + 2].getValue() * w,
points[i + 3].getValue() * h);
p.closeSubPath();
PathStrokeType stroke (0.5f + 10.0f * thickness.getValue());
g.setColour (Colours::purple.withAlpha (getAlpha()));
g.strokePath (p, stroke, AffineTransform());
}
SlowerBouncingNumber points[2 + 4 * 8], thickness;
};
//==============================================================================
class ImagesRenderingDemo : public GraphicsDemoBase
{
public:
ImagesRenderingDemo (ControllersComponent& cc, bool argb, bool tiled)
: GraphicsDemoBase (cc, String ("Images") + (argb ? ": ARGB" : ": RGB") + (tiled ? " Tiled" : String() )),
isArgb (argb), isTiled (tiled)
{
argbImage = getImageFromAssets ("juce_icon.png");
rgbImage = getImageFromAssets ("portmeirion.jpg");
}
void drawDemo (Graphics& g) override
{
auto image = isArgb ? argbImage : rgbImage;
AffineTransform transform (AffineTransform::translation ((float) (image.getWidth() / -2),
(float) (image.getHeight() / -2))
.followedBy (getTransform()));
if (isTiled)
{
FillType fill (image, transform);
fill.setOpacity (getAlpha());
g.setFillType (fill);
g.fillAll();
}
else
{
g.setOpacity (getAlpha());
g.drawImageTransformed (image, transform, false);
}
}
bool isArgb, isTiled;
Image rgbImage, argbImage;
};
//==============================================================================
class GlyphsDemo : public GraphicsDemoBase
{
public:
GlyphsDemo (ControllersComponent& cc)
: GraphicsDemoBase (cc, "Glyphs")
{
glyphs.addFittedText ({ 20.0f }, "The Quick Brown Fox Jumped Over The Lazy Dog",
-120, -50, 240, 100, Justification::centred, 2, 1.0f);
}
void drawDemo (Graphics& g) override
{
g.setColour (Colours::black.withAlpha (getAlpha()));
glyphs.draw (g, getTransform());
}
GlyphArrangement glyphs;
};
//==============================================================================
class SVGDemo : public GraphicsDemoBase
{
public:
SVGDemo (ControllersComponent& cc)
: GraphicsDemoBase (cc, "SVG")
{
createSVGDrawable();
}
void drawDemo (Graphics& g) override
{
if (Time::getCurrentTime().toMilliseconds() > lastSVGLoadTime.toMilliseconds() + 2000)
createSVGDrawable();
svgDrawable->draw (g, getAlpha(), getTransform());
}
void createSVGDrawable()
{
lastSVGLoadTime = Time::getCurrentTime();
ZipFile icons (createAssetInputStream ("icons.zip").release(), true);
// Load a random SVG file from our embedded icons.zip file.
const std::unique_ptr<InputStream> svgFileStream (icons.createStreamForEntry (Random::getSystemRandom().nextInt (icons.getNumEntries())));
if (svgFileStream.get() != nullptr)
{
svgDrawable = Drawable::createFromImageDataStream (*svgFileStream);
if (svgDrawable != nullptr)
{
// to make our icon the right size, we'll set its bounding box to the size and position that we want.
if (auto comp = dynamic_cast<DrawableComposite*> (svgDrawable.get()))
comp->setBoundingBox ({ -100.0f, -100.0f, 200.0f, 200.0f });
}
}
}
Time lastSVGLoadTime;
std::unique_ptr<Drawable> svgDrawable;
};
//==============================================================================
class LinesDemo : public GraphicsDemoBase
{
public:
LinesDemo (ControllersComponent& cc)
: GraphicsDemoBase (cc, "Lines")
{}
void drawDemo (Graphics& g) override
{
{
RectangleList<float> verticalLines;
verticalLines.ensureStorageAllocated (getWidth());
auto pos = offset.getValue();
for (int x = 0; x < getWidth(); ++x)
{
auto y = (float) getHeight() * 0.3f;
auto length = y * std::abs (std::sin ((float) x / 100.0f + 2.0f * pos));
verticalLines.addWithoutMerging (Rectangle<float> ((float) x, y - length * 0.5f, 1.0f, length));
}
g.setColour (Colours::blue.withAlpha (getAlpha()));
g.fillRectList (verticalLines);
}
{
RectangleList<float> horizontalLines;
horizontalLines.ensureStorageAllocated (getHeight());
auto pos = offset.getValue();
for (int y = 0; y < getHeight(); ++y)
{
auto x = (float) getWidth() * 0.3f;
auto length = x * std::abs (std::sin ((float) y / 100.0f + 2.0f * pos));
horizontalLines.addWithoutMerging (Rectangle<float> (x - length * 0.5f, (float) y, length, 1.0f));
}
g.setColour (Colours::green.withAlpha (getAlpha()));
g.fillRectList (horizontalLines);
}
g.setColour (Colours::red.withAlpha (getAlpha()));
auto w = (float) getWidth();
auto h = (float) getHeight();
g.drawLine (positions[0].getValue() * w,
positions[1].getValue() * h,
positions[2].getValue() * w,
positions[3].getValue() * h);
g.drawLine (positions[4].getValue() * w,
positions[5].getValue() * h,
positions[6].getValue() * w,
positions[7].getValue() * h);
}
SlowerBouncingNumber offset, positions[8];
};
//==============================================================================
class DemoHolderComponent : public Component,
private Timer
{
public:
DemoHolderComponent()
{
setOpaque (true);
}
void paint (Graphics& g) override
{
g.fillCheckerBoard (getLocalBounds().toFloat(), 48.0f, 48.0f,
Colours::lightgrey, Colours::white);
}
void timerCallback() override
{
if (currentDemo != nullptr)
currentDemo->repaint();
}
void setDemo (GraphicsDemoBase* newDemo)
{
if (currentDemo != nullptr)
removeChildComponent (currentDemo);
currentDemo = newDemo;
if (currentDemo != nullptr)
{
addAndMakeVisible (currentDemo);
startTimerHz (60);
resized();
}
}
void resized() override
{
if (currentDemo != nullptr)
currentDemo->setBounds (getLocalBounds());
}
private:
GraphicsDemoBase* currentDemo = nullptr;
};
//==============================================================================
class TestListComponent : public Component,
private ListBoxModel
{
public:
TestListComponent (DemoHolderComponent& holder, ControllersComponent& controls)
: demoHolder (holder)
{
demos.add (new PathsDemo (controls, false, true));
demos.add (new PathsDemo (controls, true, false));
demos.add (new PathsDemo (controls, false, false));
demos.add (new RectangleFillTypesDemo (controls));
demos.add (new StrokesDemo (controls));
demos.add (new ImagesRenderingDemo (controls, false, false));
demos.add (new ImagesRenderingDemo (controls, false, true));
demos.add (new ImagesRenderingDemo (controls, true, false));
demos.add (new ImagesRenderingDemo (controls, true, true));
demos.add (new GlyphsDemo (controls));
demos.add (new SVGDemo (controls));
demos.add (new LinesDemo (controls));
addAndMakeVisible (listBox);
listBox.setTitle ("Test List");
listBox.setModel (this);
listBox.selectRow (0);
}
void resized() override
{
listBox.setBounds (getLocalBounds());
}
int getNumRows() override
{
return demos.size();
}
void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override
{
if (demos[rowNumber] == nullptr)
return;
if (rowIsSelected)
g.fillAll (Colour::contrasting (findColour (ListBox::textColourId),
findColour (ListBox::backgroundColourId)));
g.setColour (findColour (ListBox::textColourId));
g.setFont (14.0f);
g.drawFittedText (getNameForRow (rowNumber), 8, 0, width - 10, height, Justification::centredLeft, 2);
}
String getNameForRow (int rowNumber) override
{
if (auto* demo = demos[rowNumber])
return demo->getName();
return {};
}
void selectedRowsChanged (int lastRowSelected) override
{
demoHolder.setDemo (demos [lastRowSelected]);
}
private:
DemoHolderComponent& demoHolder;
ListBox listBox;
OwnedArray<GraphicsDemoBase> demos;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestListComponent)
};
//==============================================================================
class GraphicsDemo : public Component
{
public:
GraphicsDemo()
: testList (demoHolder, controllersComponent)
{
setOpaque (true);
addAndMakeVisible (demoHolder);
addAndMakeVisible (controllersComponent);
addAndMakeVisible (performanceDisplay);
addAndMakeVisible (testList);
setSize (750, 750);
}
void paint (Graphics& g) override
{
g.fillAll (Colours::grey);
}
void resized() override
{
auto area = getLocalBounds();
controllersComponent.setBounds (area.removeFromBottom (150));
testList .setBounds (area.removeFromRight (150));
demoHolder .setBounds (area);
performanceDisplay .setBounds (area.removeFromTop (20).removeFromRight (100));
}
private:
ControllersComponent controllersComponent;
DemoHolderComponent demoHolder;
Label performanceDisplay;
TestListComponent testList;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GraphicsDemo)
};

141
deps/juce/examples/GUI/GridDemo.h vendored Normal file
View File

@ -0,0 +1,141 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: GridDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Responsive layouts using Grid.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: GridDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
struct GridDemo : public Component
{
GridDemo()
{
addGridItemPanel (Colours::aquamarine, "0");
addGridItemPanel (Colours::red, "1");
addGridItemPanel (Colours::blue, "2");
addGridItemPanel (Colours::green, "3");
addGridItemPanel (Colours::orange, "4");
addGridItemPanel (Colours::white, "5");
addGridItemPanel (Colours::aquamarine, "6");
addGridItemPanel (Colours::red, "7");
addGridItemPanel (Colours::blue, "8");
addGridItemPanel (Colours::green, "9");
addGridItemPanel (Colours::orange, "10");
addGridItemPanel (Colours::white, "11");
setSize (750, 750);
}
void addGridItemPanel (Colour colour, const char* text)
{
addAndMakeVisible (items.add (new GridItemPanel (colour, text)));
}
void paint (Graphics& g) override
{
g.fillAll (Colours::black);
}
void resized() override
{
Grid grid;
grid.rowGap = 20_px;
grid.columnGap = 20_px;
using Track = Grid::TrackInfo;
grid.templateRows = { Track (1_fr), Track (1_fr), Track (1_fr) };
grid.templateColumns = { Track (1_fr),
Track (1_fr),
Track (1_fr) };
grid.autoColumns = Track (1_fr);
grid.autoRows = Track (1_fr);
grid.autoFlow = Grid::AutoFlow::column;
grid.items.addArray ({ GridItem (items[0]).withArea (2, 2, 4, 4),
GridItem (items[1]),
GridItem (items[2]).withArea ({}, 3),
GridItem (items[3]),
GridItem (items[4]).withArea (GridItem::Span (2), {}),
GridItem (items[5]),
GridItem (items[6]),
GridItem (items[7]),
GridItem (items[8]),
GridItem (items[9]),
GridItem (items[10]),
GridItem (items[11])
});
grid.performLayout (getLocalBounds());
}
//==============================================================================
struct GridItemPanel : public Component
{
GridItemPanel (Colour colourToUse, const String& textToUse)
: colour (colourToUse),
text (textToUse)
{}
void paint (Graphics& g) override
{
g.fillAll (colour.withAlpha (0.5f));
g.setColour (Colours::black);
g.drawText (text, getLocalBounds().withSizeKeepingCentre (100, 100), Justification::centred, false);
}
Colour colour;
String text;
};
OwnedArray<GridItemPanel> items;
};

108
deps/juce/examples/GUI/HelloWorldDemo.h vendored Normal file
View File

@ -0,0 +1,108 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: HelloWorldDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Simple HelloWorld application.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics
exporters: xcode_mac, vs2019, linux_make, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: HelloWorldDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
class HelloWorldDemo : public Component
{
public:
//==============================================================================
HelloWorldDemo()
{
addAndMakeVisible (helloWorldLabel);
helloWorldLabel.setFont (Font (40.00f, Font::bold));
helloWorldLabel.setJustificationType (Justification::centred);
helloWorldLabel.setEditable (false, false, false);
helloWorldLabel.setColour (Label::textColourId, Colours::black);
helloWorldLabel.setColour (TextEditor::textColourId, Colours::black);
helloWorldLabel.setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (quitButton);
quitButton.onClick = [] { JUCEApplication::quit(); };
setSize (600, 300);
}
//==============================================================================
void paint (Graphics& g) override
{
g.fillAll (Colour (0xffc1d0ff));
g.setColour (Colours::white);
g.fillPath (internalPath);
g.setColour (Colour (0xff6f6f6f));
g.strokePath (internalPath, PathStrokeType (5.200f));
}
void resized() override
{
helloWorldLabel.setBounds (152, 80, 296, 48);
quitButton.setBounds (getWidth() - 176, getHeight() - 60, 120, 32);
internalPath.clear();
internalPath.startNewSubPath (136.0f, 80.0f);
internalPath.quadraticTo (176.0f, 24.0f, 328.0f, 32.0f);
internalPath.quadraticTo (472.0f, 40.0f, 472.0f, 104.0f);
internalPath.quadraticTo (472.0f, 192.0f, 232.0f, 176.0f);
internalPath.lineTo (184.0f, 216.0f);
internalPath.lineTo (200.0f, 168.0f);
internalPath.quadraticTo (96.0f, 136.0f, 136.0f, 80.0f);
internalPath.closeSubPath();
}
private:
//==============================================================================
Label helloWorldLabel { {}, TRANS("Hello World!") };
TextButton quitButton { TRANS("Quit") };
Path internalPath;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HelloWorldDemo)
};

139
deps/juce/examples/GUI/ImagesDemo.h vendored Normal file
View File

@ -0,0 +1,139 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: ImagesDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Displays image files.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: ImagesDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
class ImagesDemo : public Component,
public FileBrowserListener
{
public:
ImagesDemo()
{
setOpaque (true);
imageList.setDirectory (File::getSpecialLocation (File::userPicturesDirectory), true, true);
directoryThread.startThread (1);
fileTree.setTitle ("Files");
fileTree.addListener (this);
fileTree.setColour (TreeView::backgroundColourId, Colours::grey);
addAndMakeVisible (fileTree);
addAndMakeVisible (resizerBar);
addAndMakeVisible (imagePreview);
// we have to set up our StretchableLayoutManager so it know the limits and preferred sizes of it's contents
stretchableManager.setItemLayout (0, // for the fileTree
-0.1, -0.9, // must be between 50 pixels and 90% of the available space
-0.3); // and its preferred size is 30% of the total available space
stretchableManager.setItemLayout (1, // for the resize bar
5, 5, 5); // hard limit to 5 pixels
stretchableManager.setItemLayout (2, // for the imagePreview
-0.1, -0.9, // size must be between 50 pixels and 90% of the available space
-0.7); // and its preferred size is 70% of the total available space
setSize (500, 500);
}
~ImagesDemo() override
{
fileTree.removeListener (this);
}
void paint (Graphics& g) override
{
g.fillAll (Colours::white);
}
void resized() override
{
auto r = getLocalBounds().reduced (4);
// make a list of two of our child components that we want to reposition
Component* comps[] = { &fileTree, &resizerBar, &imagePreview };
// this will position the 3 components, one above the other, to fit
// vertically into the rectangle provided.
stretchableManager.layOutComponents (comps, 3,
r.getX(), r.getY(), r.getWidth(), r.getHeight(),
true, true);
}
private:
WildcardFileFilter imagesWildcardFilter { "*.jpeg;*.jpg;*.png;*.gif", "*", "Image File Filter"};
TimeSliceThread directoryThread { "Image File Scanner Thread" };
DirectoryContentsList imageList { &imagesWildcardFilter, directoryThread };
FileTreeComponent fileTree { imageList };
ImageComponent imagePreview;
StretchableLayoutManager stretchableManager;
StretchableLayoutResizerBar resizerBar { &stretchableManager, 1, false };
void selectionChanged() override
{
// we're only really interested in when the selection changes, regardless of if it was
// clicked or not so we'll only override this method
auto selectedFile = fileTree.getSelectedFile();
if (selectedFile.existsAsFile())
imagePreview.setImage (ImageCache::getFromFile (selectedFile));
// the image cache is a handy way to load images from files or directly from memory and
// will keep them hanging around for a few seconds in case they are requested elsewhere
}
void fileClicked (const File&, const MouseEvent&) override {}
void fileDoubleClicked (const File&) override {}
void browserRootChanged (const File&) override {}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagesDemo)
};

271
deps/juce/examples/GUI/KeyMappingsDemo.h vendored Normal file
View File

@ -0,0 +1,271 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: KeyMappingsDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Showcases key mapping features.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: KeyMappingsDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
/** A list of the command IDs that this demo can perform. */
enum KeyPressCommandIDs
{
buttonMoveUp = 1,
buttonMoveRight,
buttonMoveDown,
buttonMoveLeft,
nextButtonColour,
previousButtonColour,
nextBackgroundColour,
previousBackgroundColour
};
//==============================================================================
/**
This is a simple target for the key-presses which will live inside the demo component
and contains a button that can be moved around with the arrow keys.
*/
class KeyPressTarget : public Component,
public ApplicationCommandTarget
{
public:
KeyPressTarget()
{
Array<Colour> coloursToUse { Colours::darkblue, Colours::darkgrey, Colours::red,
Colours::green, Colours::blue, Colours::hotpink };
colours.addArray (coloursToUse);
addAndMakeVisible (button);
}
//==============================================================================
void resized() override
{
auto bounds = getLocalBounds();
// keep the button on-screen
if (buttonX < -150 || buttonX > bounds.getWidth()
|| buttonY < -30 || buttonY > bounds.getHeight())
{
buttonX = bounds.getCentreX() - 75;
buttonY = bounds.getCentreY() - 15;
}
button.setBounds (buttonX, buttonY, 150, 30);
}
void paint (Graphics& g) override
{
g.fillAll (colours.getUnchecked (backgroundColourIndex));
}
//==============================================================================
/** No other command targets in this simple example so just return nullptr. */
ApplicationCommandTarget* getNextCommandTarget() override { return nullptr; }
void getAllCommands (Array<CommandID>& commands) override
{
Array<CommandID> ids { KeyPressCommandIDs::buttonMoveUp, KeyPressCommandIDs::buttonMoveRight,
KeyPressCommandIDs::buttonMoveDown, KeyPressCommandIDs::buttonMoveLeft,
KeyPressCommandIDs::nextButtonColour, KeyPressCommandIDs::previousButtonColour,
KeyPressCommandIDs::nextBackgroundColour, KeyPressCommandIDs::previousBackgroundColour };
commands.addArray (ids);
}
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
{
switch (commandID)
{
case KeyPressCommandIDs::buttonMoveUp:
result.setInfo ("Move up", "Move the button up", "Button", 0);
result.addDefaultKeypress (KeyPress::upKey, 0);
break;
case KeyPressCommandIDs::buttonMoveRight:
result.setInfo ("Move right", "Move the button right", "Button", 0);
result.addDefaultKeypress (KeyPress::rightKey, 0);
break;
case KeyPressCommandIDs::buttonMoveDown:
result.setInfo ("Move down", "Move the button down", "Button", 0);
result.addDefaultKeypress (KeyPress::downKey, 0);
break;
case KeyPressCommandIDs::buttonMoveLeft:
result.setInfo ("Move left", "Move the button left", "Button", 0);
result.addDefaultKeypress (KeyPress::leftKey, 0);
break;
case KeyPressCommandIDs::nextButtonColour:
result.setInfo ("Next colour", "Change the colour of the button to the next in the list", "Button", 0);
result.addDefaultKeypress (KeyPress::rightKey, ModifierKeys::shiftModifier);
break;
case KeyPressCommandIDs::previousButtonColour:
result.setInfo ("Previous colour", "Change the colour of the button to the previous in the list", "Button", 0);
result.addDefaultKeypress (KeyPress::leftKey, ModifierKeys::shiftModifier);
break;
case KeyPressCommandIDs::nextBackgroundColour:
result.setInfo ("Next colour", "Change the colour of the background to the next in the list", "Other", 0);
result.addDefaultKeypress (KeyPress::rightKey, ModifierKeys::commandModifier);
break;
case KeyPressCommandIDs::previousBackgroundColour:
result.setInfo ("Previous colour", "Change the colour of the background to the previous in the list", "Other", 0);
result.addDefaultKeypress (KeyPress::leftKey, ModifierKeys::commandModifier);
break;
default:
break;
}
}
bool perform (const InvocationInfo& info) override
{
switch (info.commandID)
{
case KeyPressCommandIDs::buttonMoveUp:
buttonY -= 5;
resized();
break;
case KeyPressCommandIDs::buttonMoveRight:
buttonX += 5;
resized();
break;
case KeyPressCommandIDs::buttonMoveDown:
buttonY += 5;
resized();
break;
case KeyPressCommandIDs::buttonMoveLeft:
buttonX -= 5;
resized();
break;
case KeyPressCommandIDs::nextButtonColour:
++buttonColourIndex %= colours.size();
button.setColour (TextButton::buttonColourId, colours.getUnchecked (buttonColourIndex));
break;
case KeyPressCommandIDs::previousButtonColour:
--buttonColourIndex;
if (buttonColourIndex < 0)
buttonColourIndex = colours.size() - 1;
button.setColour (TextButton::buttonColourId, colours.getUnchecked (buttonColourIndex));
break;
case KeyPressCommandIDs::nextBackgroundColour:
++backgroundColourIndex %= colours.size();
repaint();
break;
case KeyPressCommandIDs::previousBackgroundColour:
--backgroundColourIndex;
if (backgroundColourIndex < 0)
backgroundColourIndex = colours.size() - 1;
repaint();
break;
default:
return false;
}
return true;
}
private:
TextButton button;
int buttonX = -200, buttonY = -200;
Array<Colour> colours;
int buttonColourIndex = 0;
int backgroundColourIndex = 1;
};
//==============================================================================
class KeyMappingsDemo : public Component
{
public:
KeyMappingsDemo()
{
// register the commands that the target component can perform
commandManager.registerAllCommandsForTarget (&keyTarget);
setOpaque (true);
addAndMakeVisible (keyMappingEditor);
addAndMakeVisible (keyTarget);
// add command manager key mappings as a KeyListener to the top-level component
// so it is notified of key presses
getTopLevelComponent()->addKeyListener (commandManager.getKeyMappings());
setSize (500, 500);
Timer::callAfterDelay (300, [this] { keyTarget.grabKeyboardFocus(); }); // ensure that key presses are sent to the KeyPressTarget object
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
Colour::greyLevel (0.93f)));
}
void resized() override
{
auto bounds = getLocalBounds();
keyTarget .setBounds (bounds.removeFromTop (bounds.getHeight() / 2).reduced (4));
keyMappingEditor.setBounds (bounds.reduced (4));
}
private:
#if JUCE_DEMO_RUNNER
ApplicationCommandManager& commandManager = getGlobalCommandManager();
#else
ApplicationCommandManager commandManager;
#endif
KeyMappingEditorComponent keyMappingEditor { *commandManager.getKeyMappings(), true};
KeyPressTarget keyTarget;
void lookAndFeelChanged() override
{
auto* lf = &LookAndFeel::getDefaultLookAndFeel();
keyMappingEditor.setColours (lf->findColour (KeyMappingEditorComponent::backgroundColourId),
lf->findColour (KeyMappingEditorComponent::textColourId));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KeyMappingsDemo)
};

631
deps/juce/examples/GUI/LookAndFeelDemo.h vendored Normal file
View File

@ -0,0 +1,631 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: LookAndFeelDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Showcases custom look and feel components.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: LookAndFeelDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
/** Custom Look And Feel subclasss.
Simply override the methods you need to, anything else will be inherited from the base class.
It's a good idea not to hard code your colours, use the findColour method along with appropriate
ColourIds so you can set these on a per-component basis.
*/
struct CustomLookAndFeel : public LookAndFeel_V4
{
void drawRoundThumb (Graphics& g, float x, float y, float diameter, Colour colour, float outlineThickness)
{
auto halfThickness = outlineThickness * 0.5f;
Path p;
p.addEllipse (x + halfThickness,
y + halfThickness,
diameter - outlineThickness,
diameter - outlineThickness);
DropShadow (Colours::black, 1, {}).drawForPath (g, p);
g.setColour (colour);
g.fillPath (p);
g.setColour (colour.brighter());
g.strokePath (p, PathStrokeType (outlineThickness));
}
void drawButtonBackground (Graphics& g, Button& button, const Colour& backgroundColour,
bool isMouseOverButton, bool isButtonDown) override
{
auto baseColour = backgroundColour.withMultipliedSaturation (button.hasKeyboardFocus (true) ? 1.3f : 0.9f)
.withMultipliedAlpha (button.isEnabled() ? 0.9f : 0.5f);
if (isButtonDown || isMouseOverButton)
baseColour = baseColour.contrasting (isButtonDown ? 0.2f : 0.1f);
auto flatOnLeft = button.isConnectedOnLeft();
auto flatOnRight = button.isConnectedOnRight();
auto flatOnTop = button.isConnectedOnTop();
auto flatOnBottom = button.isConnectedOnBottom();
auto width = (float) button.getWidth() - 1.0f;
auto height = (float) button.getHeight() - 1.0f;
if (width > 0 && height > 0)
{
auto cornerSize = jmin (15.0f, jmin (width, height) * 0.45f);
auto lineThickness = cornerSize * 0.1f;
auto halfThickness = lineThickness * 0.5f;
Path outline;
outline.addRoundedRectangle (0.5f + halfThickness, 0.5f + halfThickness, width - lineThickness, height - lineThickness,
cornerSize, cornerSize,
! (flatOnLeft || flatOnTop),
! (flatOnRight || flatOnTop),
! (flatOnLeft || flatOnBottom),
! (flatOnRight || flatOnBottom));
auto outlineColour = button.findColour (button.getToggleState() ? TextButton::textColourOnId
: TextButton::textColourOffId);
g.setColour (baseColour);
g.fillPath (outline);
if (! button.getToggleState())
{
g.setColour (outlineColour);
g.strokePath (outline, PathStrokeType (lineThickness));
}
}
}
void drawTickBox (Graphics& g, Component& component,
float x, float y, float w, float h,
bool ticked,
bool isEnabled,
bool isMouseOverButton,
bool isButtonDown) override
{
auto boxSize = w * 0.7f;
auto isDownOrDragging = component.isEnabled() && (component.isMouseOverOrDragging() || component.isMouseButtonDown());
auto colour = component.findColour (TextButton::buttonColourId)
.withMultipliedSaturation ((component.hasKeyboardFocus (false) || isDownOrDragging) ? 1.3f : 0.9f)
.withMultipliedAlpha (component.isEnabled() ? 1.0f : 0.7f);
drawRoundThumb (g, x, y + (h - boxSize) * 0.5f, boxSize, colour,
isEnabled ? ((isButtonDown || isMouseOverButton) ? 1.1f : 0.5f) : 0.3f);
if (ticked)
{
g.setColour (isEnabled ? findColour (TextButton::buttonOnColourId) : Colours::grey);
auto scale = 9.0f;
auto trans = AffineTransform::scale (w / scale, h / scale).translated (x - 2.5f, y + 1.0f);
g.fillPath (LookAndFeel_V4::getTickShape (6.0f), trans);
}
}
void drawLinearSliderThumb (Graphics& g, int x, int y, int width, int height,
float sliderPos, float minSliderPos, float maxSliderPos,
const Slider::SliderStyle style, Slider& slider) override
{
auto sliderRadius = (float) (getSliderThumbRadius (slider) - 2);
auto isDownOrDragging = slider.isEnabled() && (slider.isMouseOverOrDragging() || slider.isMouseButtonDown());
auto knobColour = slider.findColour (Slider::thumbColourId)
.withMultipliedSaturation ((slider.hasKeyboardFocus (false) || isDownOrDragging) ? 1.3f : 0.9f)
.withMultipliedAlpha (slider.isEnabled() ? 1.0f : 0.7f);
if (style == Slider::LinearHorizontal || style == Slider::LinearVertical)
{
float kx, ky;
if (style == Slider::LinearVertical)
{
kx = (float) x + (float) width * 0.5f;
ky = sliderPos;
}
else
{
kx = sliderPos;
ky = (float) y + (float) height * 0.5f;
}
auto outlineThickness = slider.isEnabled() ? 0.8f : 0.3f;
drawRoundThumb (g,
kx - sliderRadius,
ky - sliderRadius,
sliderRadius * 2.0f,
knobColour, outlineThickness);
}
else
{
// Just call the base class for the demo
LookAndFeel_V2::drawLinearSliderThumb (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
}
}
void drawLinearSlider (Graphics& g, int x, int y, int width, int height,
float sliderPos, float minSliderPos, float maxSliderPos,
const Slider::SliderStyle style, Slider& slider) override
{
g.fillAll (slider.findColour (Slider::backgroundColourId));
if (style == Slider::LinearBar || style == Slider::LinearBarVertical)
{
Path p;
if (style == Slider::LinearBarVertical)
p.addRectangle ((float) x, sliderPos, (float) width, 1.0f + (float) height - sliderPos);
else
p.addRectangle ((float) x, (float) y, sliderPos - (float) x, (float) height);
auto baseColour = slider.findColour (Slider::rotarySliderFillColourId)
.withMultipliedSaturation (slider.isEnabled() ? 1.0f : 0.5f)
.withMultipliedAlpha (0.8f);
g.setColour (baseColour);
g.fillPath (p);
auto lineThickness = jmin (15.0f, (float) jmin (width, height) * 0.45f) * 0.1f;
g.drawRect (slider.getLocalBounds().toFloat(), lineThickness);
}
else
{
drawLinearSliderBackground (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
drawLinearSliderThumb (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
}
}
void drawLinearSliderBackground (Graphics& g, int x, int y, int width, int height,
float /*sliderPos*/,
float /*minSliderPos*/,
float /*maxSliderPos*/,
const Slider::SliderStyle /*style*/, Slider& slider) override
{
auto sliderRadius = (float) getSliderThumbRadius (slider) - 5.0f;
Path on, off;
if (slider.isHorizontal())
{
auto iy = (float) y + (float) height * 0.5f - sliderRadius * 0.5f;
Rectangle<float> r ((float) x - sliderRadius * 0.5f, iy, (float) width + sliderRadius, sliderRadius);
auto onW = r.getWidth() * ((float) slider.valueToProportionOfLength (slider.getValue()));
on.addRectangle (r.removeFromLeft (onW));
off.addRectangle (r);
}
else
{
auto ix = (float) x + (float) width * 0.5f - sliderRadius * 0.5f;
Rectangle<float> r (ix, (float) y - sliderRadius * 0.5f, sliderRadius, (float) height + sliderRadius);
auto onH = r.getHeight() * ((float) slider.valueToProportionOfLength (slider.getValue()));
on.addRectangle (r.removeFromBottom (onH));
off.addRectangle (r);
}
g.setColour (slider.findColour (Slider::rotarySliderFillColourId));
g.fillPath (on);
g.setColour (slider.findColour (Slider::trackColourId));
g.fillPath (off);
}
void drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos,
float rotaryStartAngle, float rotaryEndAngle, Slider& slider) override
{
auto radius = (float) jmin (width / 2, height / 2) - 2.0f;
auto centreX = (float) x + (float) width * 0.5f;
auto centreY = (float) y + (float) height * 0.5f;
auto rx = centreX - radius;
auto ry = centreY - radius;
auto rw = radius * 2.0f;
auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
auto isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled();
if (slider.isEnabled())
g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f));
else
g.setColour (Colour (0x80808080));
{
Path filledArc;
filledArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, angle, 0.0);
g.fillPath (filledArc);
}
{
auto lineThickness = jmin (15.0f, (float) jmin (width, height) * 0.45f) * 0.1f;
Path outlineArc;
outlineArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, rotaryEndAngle, 0.0);
g.strokePath (outlineArc, PathStrokeType (lineThickness));
}
}
};
//==============================================================================
/** Another really simple look and feel that is very flat and square.
This inherits from CustomLookAndFeel above for the linear bar and slider backgrounds.
*/
struct SquareLookAndFeel : public CustomLookAndFeel
{
void drawButtonBackground (Graphics& g, Button& button, const Colour& backgroundColour,
bool isMouseOverButton, bool isButtonDown) override
{
auto baseColour = backgroundColour.withMultipliedSaturation (button.hasKeyboardFocus (true) ? 1.3f : 0.9f)
.withMultipliedAlpha (button.isEnabled() ? 0.9f : 0.5f);
if (isButtonDown || isMouseOverButton)
baseColour = baseColour.contrasting (isButtonDown ? 0.2f : 0.1f);
auto width = (float) button.getWidth() - 1.0f;
auto height = (float) button.getHeight() - 1.0f;
if (width > 0 && height > 0)
{
g.setGradientFill (ColourGradient::vertical (baseColour, 0.0f,
baseColour.darker (0.1f), height));
g.fillRect (button.getLocalBounds());
}
}
void drawTickBox (Graphics& g, Component& component,
float x, float y, float w, float h,
bool ticked,
bool isEnabled,
bool /*isMouseOverButton*/,
bool /*isButtonDown*/) override
{
auto boxSize = w * 0.7f;
auto isDownOrDragging = component.isEnabled() && (component.isMouseOverOrDragging() || component.isMouseButtonDown());
auto colour = component.findColour (TextButton::buttonOnColourId)
.withMultipliedSaturation ((component.hasKeyboardFocus (false) || isDownOrDragging) ? 1.3f : 0.9f)
.withMultipliedAlpha (component.isEnabled() ? 1.0f : 0.7f);
g.setColour (colour);
Rectangle<float> r (x, y + (h - boxSize) * 0.5f, boxSize, boxSize);
g.fillRect (r);
if (ticked)
{
auto tickPath = LookAndFeel_V4::getTickShape (6.0f);
g.setColour (isEnabled ? findColour (TextButton::buttonColourId) : Colours::grey);
auto transform = RectanglePlacement (RectanglePlacement::centred)
.getTransformToFit (tickPath.getBounds(),
r.reduced (r.getHeight() * 0.05f));
g.fillPath (tickPath, transform);
}
}
void drawLinearSliderThumb (Graphics& g, int x, int y, int width, int height,
float sliderPos, float minSliderPos, float maxSliderPos,
const Slider::SliderStyle style, Slider& slider) override
{
auto sliderRadius = (float) getSliderThumbRadius (slider);
bool isDownOrDragging = slider.isEnabled() && (slider.isMouseOverOrDragging() || slider.isMouseButtonDown());
auto knobColour = slider.findColour (Slider::rotarySliderFillColourId)
.withMultipliedSaturation ((slider.hasKeyboardFocus (false) || isDownOrDragging) ? 1.3f : 0.9f)
.withMultipliedAlpha (slider.isEnabled() ? 1.0f : 0.7f);
g.setColour (knobColour);
if (style == Slider::LinearHorizontal || style == Slider::LinearVertical)
{
float kx, ky;
if (style == Slider::LinearVertical)
{
kx = (float) x + (float) width * 0.5f;
ky = sliderPos;
g.fillRect (Rectangle<float> (kx - sliderRadius, ky - 2.5f, sliderRadius * 2.0f, 5.0f));
}
else
{
kx = sliderPos;
ky = (float) y + (float) height * 0.5f;
g.fillRect (Rectangle<float> (kx - 2.5f, ky - sliderRadius, 5.0f, sliderRadius * 2.0f));
}
}
else
{
// Just call the base class for the demo
LookAndFeel_V2::drawLinearSliderThumb (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
}
}
void drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos,
float rotaryStartAngle, float rotaryEndAngle, Slider& slider) override
{
auto diameter = (float) jmin (width, height) - 4.0f;
auto radius = (diameter / 2.0f) * std::cos (MathConstants<float>::pi / 4.0f);
auto centreX = (float) x + (float) width * 0.5f;
auto centreY = (float) y + (float) height * 0.5f;
auto rx = centreX - radius;
auto ry = centreY - radius;
auto rw = radius * 2.0f;
auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled();
auto baseColour = slider.isEnabled() ? slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 0.8f : 1.0f)
: Colour (0x80808080);
Rectangle<float> r (rx, ry, rw, rw);
auto transform = AffineTransform::rotation (angle, r.getCentreX(), r.getCentreY());
auto x1 = r.getTopLeft() .getX();
auto y1 = r.getTopLeft() .getY();
auto x2 = r.getBottomLeft().getX();
auto y2 = r.getBottomLeft().getY();
transform.transformPoints (x1, y1, x2, y2);
g.setGradientFill (ColourGradient (baseColour, x1, y1,
baseColour.darker (0.1f), x2, y2,
false));
Path knob;
knob.addRectangle (r);
g.fillPath (knob, transform);
Path needle;
auto r2 = r * 0.1f;
needle.addRectangle (r2.withPosition ({ r.getCentreX() - (r2.getWidth() / 2.0f), r.getY() }));
g.setColour (slider.findColour (Slider::rotarySliderOutlineColourId));
g.fillPath (needle, AffineTransform::rotation (angle, r.getCentreX(), r.getCentreY()));
}
};
//==============================================================================
struct LookAndFeelDemoComponent : public Component
{
LookAndFeelDemoComponent()
{
addAndMakeVisible (rotarySlider);
rotarySlider.setValue (2.5);
addAndMakeVisible (verticalSlider);
verticalSlider.setValue (6.2);
addAndMakeVisible (barSlider);
barSlider.setValue (4.5);
addAndMakeVisible (incDecSlider);
incDecSlider.setRange (0.0, 10.0, 1.0);
incDecSlider.setIncDecButtonsMode (Slider::incDecButtonsDraggable_Horizontal);
addAndMakeVisible (button1);
addAndMakeVisible (button2);
button2.setClickingTogglesState (true);
button2.setToggleState (true, dontSendNotification);
addAndMakeVisible (button3);
addAndMakeVisible (button4);
button4.setToggleState (true, dontSendNotification);
for (int i = 0; i < 3; ++i)
{
auto* b = radioButtons.add (new TextButton ("Button " + String (i + 1)));
addAndMakeVisible (b);
b->setRadioGroupId (42);
b->setClickingTogglesState (true);
switch (i)
{
case 0: b->setConnectedEdges (Button::ConnectedOnRight); break;
case 1: b->setConnectedEdges (Button::ConnectedOnRight + Button::ConnectedOnLeft); break;
case 2: b->setConnectedEdges (Button::ConnectedOnLeft); break;
default: break;
}
}
radioButtons.getUnchecked (2)->setToggleState (true, dontSendNotification);
}
void resized() override
{
auto area = getLocalBounds().reduced (10);
auto row = area.removeFromTop (100);
rotarySlider .setBounds (row.removeFromLeft (100).reduced (5));
verticalSlider.setBounds (row.removeFromLeft (100).reduced (5));
barSlider .setBounds (row.removeFromLeft (100).reduced (5, 25));
incDecSlider .setBounds (row.removeFromLeft (100).reduced (5, 28));
row = area.removeFromTop (100);
button1.setBounds (row.removeFromLeft (100).reduced (5));
auto row2 = row.removeFromTop (row.getHeight() / 2).reduced (0, 10);
button2.setBounds (row2.removeFromLeft (100).reduced (5, 0));
button3.setBounds (row2.removeFromLeft (100).reduced (5, 0));
button4.setBounds (row2.removeFromLeft (100).reduced (5, 0));
row2 = (row.removeFromTop (row2.getHeight() + 20).reduced (5, 10));
for (auto* b : radioButtons)
b->setBounds (row2.removeFromLeft (100));
}
Slider rotarySlider { Slider::RotaryHorizontalVerticalDrag, Slider::NoTextBox},
verticalSlider { Slider::LinearVertical, Slider::NoTextBox },
barSlider { Slider::LinearBar, Slider::NoTextBox },
incDecSlider { Slider::IncDecButtons, Slider::TextBoxBelow };
TextButton button1 { "Hello World!" },
button2 { "Hello World!" },
button3 { "Hello World!" };
ToggleButton button4 { "Toggle Me" };
OwnedArray<TextButton> radioButtons;
};
//==============================================================================
class LookAndFeelDemo : public Component
{
public:
LookAndFeelDemo()
{
descriptionLabel.setMinimumHorizontalScale (1.0f);
descriptionLabel.setText ("This demonstrates how to create a custom look and feel by overriding only the desired methods.\n\n"
"Components can have their look and feel individually assigned or they will inherit it from their parent. "
"Colours work in a similar way, they can be set for individual components or a look and feel as a whole.",
dontSendNotification);
addAndMakeVisible (descriptionLabel);
addAndMakeVisible (lafBox);
addAndMakeVisible (demoComp);
addLookAndFeel (new LookAndFeel_V1(), "LookAndFeel_V1");
addLookAndFeel (new LookAndFeel_V2(), "LookAndFeel_V2");
addLookAndFeel (new LookAndFeel_V3(), "LookAndFeel_V3");
addLookAndFeel (new LookAndFeel_V4(), "LookAndFeel_V4 (Dark)");
addLookAndFeel (new LookAndFeel_V4 (LookAndFeel_V4::getMidnightColourScheme()), "LookAndFeel_V4 (Midnight)");
addLookAndFeel (new LookAndFeel_V4 (LookAndFeel_V4::getGreyColourScheme()), "LookAndFeel_V4 (Grey)");
addLookAndFeel (new LookAndFeel_V4 (LookAndFeel_V4::getLightColourScheme()), "LookAndFeel_V4 (Light)");
auto* claf = new CustomLookAndFeel();
addLookAndFeel (claf, "Custom Look And Feel");
setupCustomLookAndFeelColours (*claf);
auto* slaf = new SquareLookAndFeel();
addLookAndFeel (slaf, "Square Look And Feel");
setupSquareLookAndFeelColours (*slaf);
lafBox.onChange = [this] { setAllLookAndFeels (lookAndFeels[lafBox.getSelectedItemIndex()]); };
lafBox.setSelectedItemIndex (3);
addAndMakeVisible (randomButton);
randomButton.onClick = [this] { lafBox.setSelectedItemIndex (Random().nextInt (lafBox.getNumItems())); };
setSize (500, 500);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
Colour::greyLevel (0.4f)));
}
void resized() override
{
auto r = getLocalBounds().reduced (10);
descriptionLabel.setBounds (r.removeFromTop (150));
lafBox .setBounds (r.removeFromTop (22).removeFromLeft (250));
randomButton .setBounds (lafBox.getBounds().withX (lafBox.getRight() + 20).withWidth (140));
demoComp .setBounds (r.withTrimmedTop (10));
}
private:
Label descriptionLabel;
ComboBox lafBox;
TextButton randomButton { "Assign Randomly" };
OwnedArray<LookAndFeel> lookAndFeels;
LookAndFeelDemoComponent demoComp;
void addLookAndFeel (LookAndFeel* laf, const String& name)
{
lookAndFeels.add (laf);
lafBox.addItem (name, lafBox.getNumItems() + 1);
}
void setupCustomLookAndFeelColours (LookAndFeel& laf)
{
laf.setColour (Slider::thumbColourId, Colour::greyLevel (0.95f));
laf.setColour (Slider::textBoxOutlineColourId, Colours::transparentWhite);
laf.setColour (Slider::rotarySliderFillColourId, Colour (0xff00b5f6));
laf.setColour (Slider::rotarySliderOutlineColourId, Colours::white);
laf.setColour (TextButton::buttonColourId, Colours::white);
laf.setColour (TextButton::textColourOffId, Colour (0xff00b5f6));
laf.setColour (TextButton::buttonOnColourId, laf.findColour (TextButton::textColourOffId));
laf.setColour (TextButton::textColourOnId, laf.findColour (TextButton::buttonColourId));
}
void setupSquareLookAndFeelColours (LookAndFeel& laf)
{
auto baseColour = Colours::red;
laf.setColour (Slider::thumbColourId, Colour::greyLevel (0.95f));
laf.setColour (Slider::textBoxOutlineColourId, Colours::transparentWhite);
laf.setColour (Slider::rotarySliderFillColourId, baseColour);
laf.setColour (Slider::rotarySliderOutlineColourId, Colours::white);
laf.setColour (Slider::trackColourId, Colours::black);
laf.setColour (TextButton::buttonColourId, Colours::white);
laf.setColour (TextButton::textColourOffId, baseColour);
laf.setColour (TextButton::buttonOnColourId, laf.findColour (TextButton::textColourOffId));
laf.setColour (TextButton::textColourOnId, laf.findColour (TextButton::buttonColourId));
}
void setAllLookAndFeels (LookAndFeel* laf)
{
for (auto* child : demoComp.getChildren())
child->setLookAndFeel (laf);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookAndFeelDemo)
};

271
deps/juce/examples/GUI/MDIDemo.h vendored Normal file
View File

@ -0,0 +1,271 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: MDIDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Displays and edits MDI files.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: MDIDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
/** The Note class contains text editor used to display and edit the note's contents and will
also listen to changes in the text and mark the FileBasedDocument as 'dirty'. This 'dirty'
flag is used to prompt the user to save the note when it is closed.
*/
class Note : public Component,
public FileBasedDocument
{
public:
Note (const String& name, const String& contents)
: FileBasedDocument (".jnote", "*.jnote",
"Browse for note to load",
"Choose file to save note to"),
textValueObject (contents)
{
// we need to use an separate Value object as our text source so it doesn't get marked
// as changed immediately
setName (name);
editor.setMultiLine (true);
editor.setReturnKeyStartsNewLine (true);
editor.getTextValue().referTo (textValueObject);
addAndMakeVisible (editor);
editor.onTextChange = [this] { changed(); };
}
void resized() override
{
editor.setBounds (getLocalBounds());
}
String getDocumentTitle() override
{
return getName();
}
Result loadDocument (const File& file) override
{
editor.setText (file.loadFileAsString());
return Result::ok();
}
Result saveDocument (const File& file) override
{
// attempt to save the contents into the given file
if (file.replaceWithText (editor.getText()))
return Result::ok();
return Result::fail ("Can't write to file");
}
File getLastDocumentOpened() override
{
// not interested in this for now
return {};
}
void setLastDocumentOpened (const File& /*file*/) override
{
// not interested in this for now
}
File getSuggestedSaveAsFile (const File&) override
{
return File::getSpecialLocation (File::userDesktopDirectory)
.getChildFile (getName())
.withFileExtension ("jnote");
}
private:
Value textValueObject;
TextEditor editor;
void lookAndFeelChanged() override
{
editor.applyFontToAllText (editor.getFont());
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Note)
};
//==============================================================================
/** Simple MultiDocumentPanel that just tries to save our notes when they are closed.
*/
class DemoMultiDocumentPanel : public MultiDocumentPanel
{
public:
DemoMultiDocumentPanel() = default;
void tryToCloseDocumentAsync (Component* component, std::function<void (bool)> callback) override
{
if (auto* note = dynamic_cast<Note*> (component))
{
SafePointer<DemoMultiDocumentPanel> parent { this };
note->saveIfNeededAndUserAgreesAsync ([parent, callback] (FileBasedDocument::SaveResult result)
{
if (parent != nullptr)
callback (result == FileBasedDocument::savedOk);
});
}
}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoMultiDocumentPanel)
};
//==============================================================================
/** Simple multi-document panel that manages a number of notes that you can store to files.
By default this will look for notes saved to the desktop and load them up.
*/
class MDIDemo : public Component,
public FileDragAndDropTarget
{
public:
MDIDemo()
{
setOpaque (true);
showInTabsButton.setToggleState (false, dontSendNotification);
showInTabsButton.onClick = [this] { updateLayoutMode(); };
addAndMakeVisible (showInTabsButton);
addNoteButton.onClick = [this] { addNote ("Note " + String (multiDocumentPanel.getNumDocuments() + 1), "Hello World!"); };
addAndMakeVisible (addNoteButton);
closeApplicationButton.onClick = [this]
{
multiDocumentPanel.closeAllDocumentsAsync (true, [] (bool allSaved)
{
if (allSaved)
JUCEApplicationBase::quit();
});
};
addAndMakeVisible (closeApplicationButton);
addAndMakeVisible (multiDocumentPanel);
multiDocumentPanel.setBackgroundColour (Colours::transparentBlack);
updateLayoutMode();
addNote ("Notes Demo", "You can drag-and-drop text files onto this page to open them as notes..");
addExistingNotes();
setSize (500, 500);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
}
void resized() override
{
auto area = getLocalBounds();
auto buttonArea = area.removeFromTop (28).reduced (2);
closeApplicationButton.setBounds (buttonArea.removeFromRight (150));
addNoteButton .setBounds (buttonArea.removeFromRight (150));
showInTabsButton .setBounds (buttonArea);
multiDocumentPanel.setBounds (area);
}
bool isInterestedInFileDrag (const StringArray&) override
{
return true;
}
void filesDropped (const StringArray& filenames, int /*x*/, int /*y*/) override
{
Array<File> files;
for (auto& f : filenames)
files.add ({ f });
createNotesForFiles (files);
}
void createNotesForFiles (const Array<File>& files)
{
for (auto& file : files)
{
auto content = file.loadFileAsString();
if (content.length() > 20000)
content = "Too long!";
addNote (file.getFileName(), content);
}
}
private:
void updateLayoutMode()
{
multiDocumentPanel.setLayoutMode (showInTabsButton.getToggleState() ? MultiDocumentPanel::MaximisedWindowsWithTabs
: MultiDocumentPanel::FloatingWindows);
}
void addNote (const String& name, const String& content)
{
auto* newNote = new Note (name, content);
newNote->setSize (200, 200);
multiDocumentPanel.addDocument (newNote, Colours::lightblue.withAlpha (0.6f), true);
}
void addExistingNotes()
{
Array<File> files;
File::getSpecialLocation (File::userDesktopDirectory).findChildFiles (files, File::findFiles, false, "*.jnote");
createNotesForFiles (files);
}
ToggleButton showInTabsButton { "Show with tabs" };
TextButton addNoteButton { "Create a new note" },
closeApplicationButton { "Close app" };
DemoMultiDocumentPanel multiDocumentPanel;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MDIDemo)
};

523
deps/juce/examples/GUI/MenusDemo.h vendored Normal file
View File

@ -0,0 +1,523 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: MenusDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Showcases menu features.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: MenusDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
/**
This struct contains a header component that will be used when the burger menu
is enabled. It contains an icon that can be used to show the side panel containing
the menu.
*/
struct BurgerMenuHeader : public Component
{
BurgerMenuHeader (SidePanel& sp)
: sidePanel (sp)
{
static const unsigned char burgerMenuPathData[]
= { 110,109,0,0,128,64,0,0,32,65,108,0,0,224,65,0,0,32,65,98,254,212,232,65,0,0,32,65,0,0,240,65,252,
169,17,65,0,0,240,65,0,0,0,65,98,0,0,240,65,8,172,220,64,254,212,232,65,0,0,192,64,0,0,224,65,0,0,
192,64,108,0,0,128,64,0,0,192,64,98,16,88,57,64,0,0,192,64,0,0,0,64,8,172,220,64,0,0,0,64,0,0,0,65,
98,0,0,0,64,252,169,17,65,16,88,57,64,0,0,32,65,0,0,128,64,0,0,32,65,99,109,0,0,224,65,0,0,96,65,108,
0,0,128,64,0,0,96,65,98,16,88,57,64,0,0,96,65,0,0,0,64,4,86,110,65,0,0,0,64,0,0,128,65,98,0,0,0,64,
254,212,136,65,16,88,57,64,0,0,144,65,0,0,128,64,0,0,144,65,108,0,0,224,65,0,0,144,65,98,254,212,232,
65,0,0,144,65,0,0,240,65,254,212,136,65,0,0,240,65,0,0,128,65,98,0,0,240,65,4,86,110,65,254,212,232,
65,0,0,96,65,0,0,224,65,0,0,96,65,99,109,0,0,224,65,0,0,176,65,108,0,0,128,64,0,0,176,65,98,16,88,57,
64,0,0,176,65,0,0,0,64,2,43,183,65,0,0,0,64,0,0,192,65,98,0,0,0,64,254,212,200,65,16,88,57,64,0,0,208,
65,0,0,128,64,0,0,208,65,108,0,0,224,65,0,0,208,65,98,254,212,232,65,0,0,208,65,0,0,240,65,254,212,
200,65,0,0,240,65,0,0,192,65,98,0,0,240,65,2,43,183,65,254,212,232,65,0,0,176,65,0,0,224,65,0,0,176,
65,99,101,0,0 };
Path p;
p.loadPathFromData (burgerMenuPathData, sizeof (burgerMenuPathData));
burgerButton.setShape (p, true, true, false);
burgerButton.onClick = [this] { showOrHide(); };
addAndMakeVisible (burgerButton);
}
~BurgerMenuHeader() override
{
sidePanel.showOrHide (false);
}
private:
void paint (Graphics& g) override
{
auto titleBarBackgroundColour = getLookAndFeel().findColour (ResizableWindow::backgroundColourId)
.darker();
g.setColour (titleBarBackgroundColour);
g.fillRect (getLocalBounds());
}
void resized() override
{
auto r = getLocalBounds();
burgerButton.setBounds (r.removeFromRight (40).withSizeKeepingCentre (20, 20));
titleLabel.setFont (Font ((float) getHeight() * 0.5f, Font::plain));
titleLabel.setBounds (r);
}
void showOrHide()
{
sidePanel.showOrHide (! sidePanel.isPanelShowing());
}
SidePanel& sidePanel;
Label titleLabel { "titleLabel", "JUCE Demo" };
ShapeButton burgerButton { "burgerButton", Colours::lightgrey, Colours::lightgrey, Colours::white };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BurgerMenuHeader)
};
//==============================================================================
class MenusDemo : public Component,
public ApplicationCommandTarget,
public MenuBarModel
{
public:
//==============================================================================
/** A list of the commands that this demo responds to. */
enum CommandIDs
{
menuPositionInsideWindow = 1,
menuPositionGlobalMenuBar,
menuPositionBurgerMenu,
outerColourRed,
outerColourGreen,
outerColourBlue,
innerColourRed,
innerColourGreen,
innerColourBlue
};
//==============================================================================
/** Represents the possible menu positions. */
enum class MenuBarPosition
{
window,
global,
burger
};
//==============================================================================
MenusDemo()
{
menuBar.reset (new MenuBarComponent (this));
addAndMakeVisible (menuBar.get());
setApplicationCommandManagerToWatch (&commandManager);
commandManager.registerAllCommandsForTarget (this);
// this ensures that commands invoked on the DemoRunner application are correctly
// forwarded to this demo
commandManager.setFirstCommandTarget (this);
// this lets the command manager use keypresses that arrive in our window to send out commands
addKeyListener (commandManager.getKeyMappings());
addChildComponent (menuHeader);
addAndMakeVisible (outerCommandTarget);
addAndMakeVisible (sidePanel);
setWantsKeyboardFocus (true);
setSize (500, 500);
}
~MenusDemo() override
{
#if JUCE_MAC
MenuBarModel::setMacMainMenu (nullptr);
#endif
commandManager.setFirstCommandTarget (nullptr);
}
void resized() override
{
auto b = getLocalBounds();
if (menuBarPosition == MenuBarPosition::window)
{
menuBar->setBounds (b.removeFromTop (LookAndFeel::getDefaultLookAndFeel()
.getDefaultMenuBarHeight()));
}
else if (menuBarPosition == MenuBarPosition::burger)
{
menuHeader.setBounds (b.removeFromTop (40));
}
outerCommandTarget.setBounds (b);
}
//==============================================================================
StringArray getMenuBarNames() override
{
return { "Menu Position", "Outer Colour", "Inner Colour" };
}
PopupMenu getMenuForIndex (int menuIndex, const String& /*menuName*/) override
{
PopupMenu menu;
if (menuIndex == 0)
{
menu.addCommandItem (&commandManager, CommandIDs::menuPositionInsideWindow);
#if JUCE_MAC
menu.addCommandItem (&commandManager, CommandIDs::menuPositionGlobalMenuBar);
#endif
menu.addCommandItem (&commandManager, CommandIDs::menuPositionBurgerMenu);
}
else if (menuIndex == 1)
{
menu.addCommandItem (&commandManager, CommandIDs::outerColourRed);
menu.addCommandItem (&commandManager, CommandIDs::outerColourGreen);
menu.addCommandItem (&commandManager, CommandIDs::outerColourBlue);
}
else if (menuIndex == 2)
{
menu.addCommandItem (&commandManager, CommandIDs::innerColourRed);
menu.addCommandItem (&commandManager, CommandIDs::innerColourGreen);
menu.addCommandItem (&commandManager, CommandIDs::innerColourBlue);
}
return menu;
}
void menuItemSelected (int /*menuItemID*/, int /*topLevelMenuIndex*/) override {}
//==============================================================================
// The following methods implement the ApplicationCommandTarget interface, allowing
// this window to publish a set of actions it can perform, and which can be mapped
// onto menus, keypresses, etc.
ApplicationCommandTarget* getNextCommandTarget() override
{
return &outerCommandTarget;
}
void getAllCommands (Array<CommandID>& c) override
{
Array<CommandID> commands { CommandIDs::menuPositionInsideWindow,
CommandIDs::menuPositionGlobalMenuBar,
CommandIDs::menuPositionBurgerMenu };
c.addArray (commands);
}
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
{
switch (commandID)
{
case CommandIDs::menuPositionInsideWindow:
result.setInfo ("Inside Window", "Places the menu bar inside the application window", "Menu", 0);
result.setTicked (menuBarPosition == MenuBarPosition::window);
result.addDefaultKeypress ('w', ModifierKeys::shiftModifier);
break;
case CommandIDs::menuPositionGlobalMenuBar:
result.setInfo ("Global", "Uses a global menu bar", "Menu", 0);
result.setTicked (menuBarPosition == MenuBarPosition::global);
result.addDefaultKeypress ('g', ModifierKeys::shiftModifier);
break;
case CommandIDs::menuPositionBurgerMenu:
result.setInfo ("Burger Menu", "Uses a burger menu", "Menu", 0);
result.setTicked (menuBarPosition == MenuBarPosition::burger);
result.addDefaultKeypress ('b', ModifierKeys::shiftModifier);
break;
default:
break;
}
}
bool perform (const InvocationInfo& info) override
{
switch (info.commandID)
{
case CommandIDs::menuPositionInsideWindow:
setMenuBarPosition (MenuBarPosition::window);
break;
case CommandIDs::menuPositionGlobalMenuBar:
setMenuBarPosition (MenuBarPosition::global);
break;
case CommandIDs::menuPositionBurgerMenu:
setMenuBarPosition (MenuBarPosition::burger);
break;
default:
return false;
}
return true;
}
void setMenuBarPosition (MenuBarPosition newPosition)
{
if (menuBarPosition != newPosition)
{
menuBarPosition = newPosition;
if (menuBarPosition != MenuBarPosition::burger)
sidePanel.showOrHide (false);
#if JUCE_MAC
MenuBarModel::setMacMainMenu (menuBarPosition == MenuBarPosition::global ? this : nullptr);
#endif
menuBar->setVisible (menuBarPosition == MenuBarPosition::window);
burgerMenu.setModel (menuBarPosition == MenuBarPosition::burger ? this : nullptr);
menuHeader.setVisible (menuBarPosition == MenuBarPosition::burger);
sidePanel.setContent (menuBarPosition == MenuBarPosition::burger ? &burgerMenu : nullptr, false);
menuItemsChanged();
resized();
}
}
private:
#if JUCE_DEMO_RUNNER
ApplicationCommandManager& commandManager = getGlobalCommandManager();
#else
ApplicationCommandManager commandManager;
#endif
std::unique_ptr<MenuBarComponent> menuBar;
MenuBarPosition menuBarPosition = MenuBarPosition::window;
SidePanel sidePanel { "Menu", 300, false };
BurgerMenuComponent burgerMenu;
BurgerMenuHeader menuHeader { sidePanel };
//==============================================================================
/**
Command messages that aren't handled in the main component will be passed
to this class to respond to.
*/
class OuterCommandTarget : public Component,
public ApplicationCommandTarget
{
public:
OuterCommandTarget (ApplicationCommandManager& m)
: commandManager (m),
innerCommandTarget (commandManager)
{
commandManager.registerAllCommandsForTarget (this);
addAndMakeVisible (innerCommandTarget);
}
void resized() override
{
innerCommandTarget.setBounds (getLocalBounds().reduced (50));
}
void paint (Graphics& g) override
{
g.fillAll (currentColour);
}
//==============================================================================
ApplicationCommandTarget* getNextCommandTarget() override
{
return &innerCommandTarget;
}
void getAllCommands (Array<CommandID>& c) override
{
Array<CommandID> commands { CommandIDs::outerColourRed,
CommandIDs::outerColourGreen,
CommandIDs::outerColourBlue };
c.addArray (commands);
}
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
{
switch (commandID)
{
case CommandIDs::outerColourRed:
result.setInfo ("Red", "Sets the outer colour to red", "Outer", 0);
result.setTicked (currentColour == Colours::red);
result.addDefaultKeypress ('r', ModifierKeys::commandModifier);
break;
case CommandIDs::outerColourGreen:
result.setInfo ("Green", "Sets the outer colour to green", "Outer", 0);
result.setTicked (currentColour == Colours::green);
result.addDefaultKeypress ('g', ModifierKeys::commandModifier);
break;
case CommandIDs::outerColourBlue:
result.setInfo ("Blue", "Sets the outer colour to blue", "Outer", 0);
result.setTicked (currentColour == Colours::blue);
result.addDefaultKeypress ('b', ModifierKeys::commandModifier);
break;
default:
break;
}
}
bool perform (const InvocationInfo& info) override
{
switch (info.commandID)
{
case CommandIDs::outerColourRed:
currentColour = Colours::red;
break;
case CommandIDs::outerColourGreen:
currentColour = Colours::green;
break;
case CommandIDs::outerColourBlue:
currentColour = Colours::blue;
break;
default:
return false;
}
repaint();
return true;
}
private:
//==============================================================================
/**
Command messages that aren't handled in the OuterCommandTarget will be passed
to this class to respond to.
*/
struct InnerCommandTarget : public Component,
public ApplicationCommandTarget
{
InnerCommandTarget (ApplicationCommandManager& m)
: commandManager (m)
{
commandManager.registerAllCommandsForTarget (this);
}
void paint (Graphics& g) override
{
g.fillAll (currentColour);
}
//==============================================================================
ApplicationCommandTarget* getNextCommandTarget() override
{
// this will return the next parent component that is an ApplicationCommandTarget
return findFirstTargetParentComponent();
}
void getAllCommands (Array<CommandID>& c) override
{
Array<CommandID> commands { CommandIDs::innerColourRed,
CommandIDs::innerColourGreen,
CommandIDs::innerColourBlue };
c.addArray (commands);
}
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
{
switch (commandID)
{
case CommandIDs::innerColourRed:
result.setInfo ("Red", "Sets the inner colour to red", "Inner", 0);
result.setTicked (currentColour == Colours::red);
result.addDefaultKeypress ('r', ModifierKeys::commandModifier | ModifierKeys::shiftModifier);
break;
case CommandIDs::innerColourGreen:
result.setInfo ("Green", "Sets the inner colour to green", "Inner", 0);
result.setTicked (currentColour == Colours::green);
result.addDefaultKeypress ('g', ModifierKeys::commandModifier | ModifierKeys::shiftModifier);
break;
case CommandIDs::innerColourBlue:
result.setInfo ("Blue", "Sets the inner colour to blue", "Inner", 0);
result.setTicked (currentColour == Colours::blue);
result.addDefaultKeypress ('b', ModifierKeys::commandModifier | ModifierKeys::shiftModifier);
break;
default:
break;
}
}
bool perform (const InvocationInfo& info) override
{
switch (info.commandID)
{
case CommandIDs::innerColourRed:
currentColour = Colours::red;
break;
case CommandIDs::innerColourGreen:
currentColour = Colours::green;
break;
case CommandIDs::innerColourBlue:
currentColour = Colours::blue;
break;
default:
return false;
}
repaint();
return true;
}
ApplicationCommandManager& commandManager;
Colour currentColour { Colours::blue };
};
ApplicationCommandManager& commandManager;
InnerCommandTarget innerCommandTarget;
Colour currentColour { Colours::red };
};
OuterCommandTarget outerCommandTarget { commandManager };
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenusDemo)
};

182
deps/juce/examples/GUI/MultiTouchDemo.h vendored Normal file
View File

@ -0,0 +1,182 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: MultiTouchDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Showcases multi-touch features.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: MultiTouchDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
class MultiTouchDemo : public Component
{
public:
MultiTouchDemo()
{
setOpaque (true);
setSize (500, 500);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
Colour::greyLevel (0.4f)));
g.setColour (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::defaultText,
Colours::lightgrey));
g.setFont (14.0f);
g.drawFittedText ("Drag here with as many fingers as you have!",
getLocalBounds().reduced (30), Justification::centred, 4);
for (auto* trail : trails)
drawTrail (*trail, g);
}
void mouseDrag (const MouseEvent& e) override
{
auto* t = getTrail (e.source);
if (t == nullptr)
{
t = new Trail (e.source);
t->path.startNewSubPath (e.position);
trails.add (t);
}
t->pushPoint (e.position, e.mods, e.pressure);
repaint();
}
void mouseUp (const MouseEvent& e) override
{
trails.removeObject (getTrail (e.source));
repaint();
}
struct Trail
{
Trail (const MouseInputSource& ms)
: source (ms)
{}
void pushPoint (Point<float> newPoint, ModifierKeys newMods, float pressure)
{
currentPosition = newPoint;
modifierKeys = newMods;
if (lastPoint.getDistanceFrom (newPoint) > 5.0f)
{
if (lastPoint != Point<float>())
{
Path newSegment;
newSegment.startNewSubPath (lastPoint);
newSegment.lineTo (newPoint);
auto diameter = 20.0f * (pressure > 0 && pressure < 1.0f ? pressure : 1.0f);
PathStrokeType (diameter, PathStrokeType::curved, PathStrokeType::rounded).createStrokedPath (newSegment, newSegment);
path.addPath (newSegment);
}
lastPoint = newPoint;
}
}
MouseInputSource source;
Path path;
Colour colour { getRandomBrightColour().withAlpha (0.6f) };
Point<float> lastPoint, currentPosition;
ModifierKeys modifierKeys;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Trail)
};
OwnedArray<Trail> trails;
void drawTrail (Trail& trail, Graphics& g)
{
g.setColour (trail.colour);
g.fillPath (trail.path);
auto radius = 40.0f;
g.setColour (Colours::black);
g.drawEllipse (trail.currentPosition.x - radius,
trail.currentPosition.y - radius,
radius * 2.0f, radius * 2.0f, 2.0f);
g.setFont (14.0f);
String desc ("Mouse #");
desc << trail.source.getIndex();
auto pressure = trail.source.getCurrentPressure();
if (pressure > 0.0f && pressure < 1.0f)
desc << " (pressure: " << (int) (pressure * 100.0f) << "%)";
if (trail.modifierKeys.isCommandDown()) desc << " (CMD)";
if (trail.modifierKeys.isShiftDown()) desc << " (SHIFT)";
if (trail.modifierKeys.isCtrlDown()) desc << " (CTRL)";
if (trail.modifierKeys.isAltDown()) desc << " (ALT)";
g.drawText (desc,
Rectangle<int> ((int) trail.currentPosition.x - 200,
(int) trail.currentPosition.y - 60,
400, 20),
Justification::centredTop, false);
}
Trail* getTrail (const MouseInputSource& source)
{
for (auto* trail : trails)
{
if (trail->source == source)
return trail;
}
return nullptr;
}
};

437
deps/juce/examples/GUI/OpenGLAppDemo.h vendored Normal file
View File

@ -0,0 +1,437 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: OpenGLAppDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Simple OpenGL application.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra, juce_opengl
exporters: xcode_mac, vs2019, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: OpenGLAppDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/WavefrontObjParser.h"
//==============================================================================
/*
This component lives inside our window, and this is where you should put all
your controls and content.
*/
class OpenGLAppDemo : public OpenGLAppComponent
{
public:
//==============================================================================
OpenGLAppDemo()
{
setSize (800, 600);
}
~OpenGLAppDemo() override
{
shutdownOpenGL();
}
void initialise() override
{
createShaders();
}
void shutdown() override
{
shader .reset();
shape .reset();
attributes.reset();
uniforms .reset();
}
Matrix3D<float> getProjectionMatrix() const
{
const ScopedLock lock (mutex);
auto w = 1.0f / (0.5f + 0.1f);
auto h = w * bounds.toFloat().getAspectRatio (false);
return Matrix3D<float>::fromFrustum (-w, w, -h, h, 4.0f, 30.0f);
}
Matrix3D<float> getViewMatrix() const
{
Matrix3D<float> viewMatrix ({ 0.0f, 0.0f, -10.0f });
Matrix3D<float> rotationMatrix = viewMatrix.rotation ({ -0.3f, 5.0f * std::sin ((float) getFrameCounter() * 0.01f), 0.0f });
return rotationMatrix * viewMatrix;
}
void render() override
{
using namespace ::juce::gl;
jassert (OpenGLHelpers::isContextActive());
auto desktopScale = (float) openGLContext.getRenderingScale();
OpenGLHelpers::clear (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
{
const ScopedLock lock (mutex);
glViewport (0, 0,
roundToInt (desktopScale * (float) bounds.getWidth()),
roundToInt (desktopScale * (float) bounds.getHeight()));
}
shader->use();
if (uniforms->projectionMatrix.get() != nullptr)
uniforms->projectionMatrix->setMatrix4 (getProjectionMatrix().mat, 1, false);
if (uniforms->viewMatrix.get() != nullptr)
uniforms->viewMatrix->setMatrix4 (getViewMatrix().mat, 1, false);
shape->draw (*attributes);
// Reset the element buffers so child Components draw correctly
glBindBuffer (GL_ARRAY_BUFFER, 0);
glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
}
void paint (Graphics& g) override
{
// You can add your component specific drawing code here!
// This will draw over the top of the openGL background.
g.setColour (getLookAndFeel().findColour (Label::textColourId));
g.setFont (20);
g.drawText ("OpenGL Example", 25, 20, 300, 30, Justification::left);
g.drawLine (20, 20, 170, 20);
g.drawLine (20, 50, 170, 50);
}
void resized() override
{
// This is called when this component is resized.
// If you add any child components, this is where you should
// update their positions.
const ScopedLock lock (mutex);
bounds = getLocalBounds();
}
void createShaders()
{
vertexShader =
"attribute vec4 position;\n"
"attribute vec4 sourceColour;\n"
"attribute vec2 textureCoordIn;\n"
"\n"
"uniform mat4 projectionMatrix;\n"
"uniform mat4 viewMatrix;\n"
"\n"
"varying vec4 destinationColour;\n"
"varying vec2 textureCoordOut;\n"
"\n"
"void main()\n"
"{\n"
" destinationColour = sourceColour;\n"
" textureCoordOut = textureCoordIn;\n"
" gl_Position = projectionMatrix * viewMatrix * position;\n"
"}\n";
fragmentShader =
#if JUCE_OPENGL_ES
"varying lowp vec4 destinationColour;\n"
"varying lowp vec2 textureCoordOut;\n"
#else
"varying vec4 destinationColour;\n"
"varying vec2 textureCoordOut;\n"
#endif
"\n"
"void main()\n"
"{\n"
#if JUCE_OPENGL_ES
" lowp vec4 colour = vec4(0.95, 0.57, 0.03, 0.7);\n"
#else
" vec4 colour = vec4(0.95, 0.57, 0.03, 0.7);\n"
#endif
" gl_FragColor = colour;\n"
"}\n";
std::unique_ptr<OpenGLShaderProgram> newShader (new OpenGLShaderProgram (openGLContext));
String statusText;
if (newShader->addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (vertexShader))
&& newShader->addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (fragmentShader))
&& newShader->link())
{
shape .reset();
attributes.reset();
uniforms .reset();
shader.reset (newShader.release());
shader->use();
shape .reset (new Shape());
attributes.reset (new Attributes (*shader));
uniforms .reset (new Uniforms (*shader));
statusText = "GLSL: v" + String (OpenGLShaderProgram::getLanguageVersion(), 2);
}
else
{
statusText = newShader->getLastError();
}
}
private:
//==============================================================================
struct Vertex
{
float position[3];
float normal[3];
float colour[4];
float texCoord[2];
};
//==============================================================================
// This class just manages the attributes that the shaders use.
struct Attributes
{
explicit Attributes (OpenGLShaderProgram& shaderProgram)
{
position .reset (createAttribute (shaderProgram, "position"));
normal .reset (createAttribute (shaderProgram, "normal"));
sourceColour .reset (createAttribute (shaderProgram, "sourceColour"));
textureCoordIn.reset (createAttribute (shaderProgram, "textureCoordIn"));
}
void enable()
{
using namespace ::juce::gl;
if (position.get() != nullptr)
{
glVertexAttribPointer (position->attributeID, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex), nullptr);
glEnableVertexAttribArray (position->attributeID);
}
if (normal.get() != nullptr)
{
glVertexAttribPointer (normal->attributeID, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 3));
glEnableVertexAttribArray (normal->attributeID);
}
if (sourceColour.get() != nullptr)
{
glVertexAttribPointer (sourceColour->attributeID, 4, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 6));
glEnableVertexAttribArray (sourceColour->attributeID);
}
if (textureCoordIn.get() != nullptr)
{
glVertexAttribPointer (textureCoordIn->attributeID, 2, GL_FLOAT, GL_FALSE, sizeof (Vertex), (GLvoid*) (sizeof (float) * 10));
glEnableVertexAttribArray (textureCoordIn->attributeID);
}
}
void disable()
{
using namespace ::juce::gl;
if (position.get() != nullptr) glDisableVertexAttribArray (position->attributeID);
if (normal.get() != nullptr) glDisableVertexAttribArray (normal->attributeID);
if (sourceColour.get() != nullptr) glDisableVertexAttribArray (sourceColour->attributeID);
if (textureCoordIn.get() != nullptr) glDisableVertexAttribArray (textureCoordIn->attributeID);
}
std::unique_ptr<OpenGLShaderProgram::Attribute> position, normal, sourceColour, textureCoordIn;
private:
static OpenGLShaderProgram::Attribute* createAttribute (OpenGLShaderProgram& shader,
const char* attributeName)
{
using namespace ::juce::gl;
if (glGetAttribLocation (shader.getProgramID(), attributeName) < 0)
return nullptr;
return new OpenGLShaderProgram::Attribute (shader, attributeName);
}
};
//==============================================================================
// This class just manages the uniform values that the demo shaders use.
struct Uniforms
{
explicit Uniforms (OpenGLShaderProgram& shaderProgram)
{
projectionMatrix.reset (createUniform (shaderProgram, "projectionMatrix"));
viewMatrix .reset (createUniform (shaderProgram, "viewMatrix"));
}
std::unique_ptr<OpenGLShaderProgram::Uniform> projectionMatrix, viewMatrix;
private:
static OpenGLShaderProgram::Uniform* createUniform (OpenGLShaderProgram& shaderProgram,
const char* uniformName)
{
using namespace ::juce::gl;
if (glGetUniformLocation (shaderProgram.getProgramID(), uniformName) < 0)
return nullptr;
return new OpenGLShaderProgram::Uniform (shaderProgram, uniformName);
}
};
//==============================================================================
/** This loads a 3D model from an OBJ file and converts it into some vertex buffers
that we can draw.
*/
struct Shape
{
Shape()
{
if (shapeFile.load (loadEntireAssetIntoString ("teapot.obj")).wasOk())
for (auto* shapeVertices : shapeFile.shapes)
vertexBuffers.add (new VertexBuffer (*shapeVertices));
}
void draw (Attributes& glAttributes)
{
using namespace ::juce::gl;
for (auto* vertexBuffer : vertexBuffers)
{
vertexBuffer->bind();
glAttributes.enable();
glDrawElements (GL_TRIANGLES, vertexBuffer->numIndices, GL_UNSIGNED_INT, nullptr);
glAttributes.disable();
}
}
private:
struct VertexBuffer
{
explicit VertexBuffer (WavefrontObjFile::Shape& aShape)
{
using namespace ::juce::gl;
numIndices = aShape.mesh.indices.size();
glGenBuffers (1, &vertexBuffer);
glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
Array<Vertex> vertices;
createVertexListFromMesh (aShape.mesh, vertices, Colours::green);
glBufferData (GL_ARRAY_BUFFER,
static_cast<GLsizeiptr> (static_cast<size_t> (vertices.size()) * sizeof (Vertex)),
vertices.getRawDataPointer(), GL_STATIC_DRAW);
glGenBuffers (1, &indexBuffer);
glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData (GL_ELEMENT_ARRAY_BUFFER,
static_cast<GLsizeiptr> (static_cast<size_t> (numIndices) * sizeof (juce::uint32)),
aShape.mesh.indices.getRawDataPointer(), GL_STATIC_DRAW);
}
~VertexBuffer()
{
using namespace ::juce::gl;
glDeleteBuffers (1, &vertexBuffer);
glDeleteBuffers (1, &indexBuffer);
}
void bind()
{
using namespace ::juce::gl;
glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
}
GLuint vertexBuffer, indexBuffer;
int numIndices;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VertexBuffer)
};
WavefrontObjFile shapeFile;
OwnedArray<VertexBuffer> vertexBuffers;
static void createVertexListFromMesh (const WavefrontObjFile::Mesh& mesh, Array<Vertex>& list, Colour colour)
{
auto scale = 0.2f;
WavefrontObjFile::TextureCoord defaultTexCoord { 0.5f, 0.5f };
WavefrontObjFile::Vertex defaultNormal { 0.5f, 0.5f, 0.5f };
for (auto i = 0; i < mesh.vertices.size(); ++i)
{
const auto& v = mesh.vertices.getReference (i);
const auto& n = i < mesh.normals.size() ? mesh.normals.getReference (i) : defaultNormal;
const auto& tc = i < mesh.textureCoords.size() ? mesh.textureCoords.getReference (i) : defaultTexCoord;
list.add ({ { scale * v.x, scale * v.y, scale * v.z, },
{ scale * n.x, scale * n.y, scale * n.z, },
{ colour.getFloatRed(), colour.getFloatGreen(), colour.getFloatBlue(), colour.getFloatAlpha() },
{ tc.x, tc.y } });
}
}
};
const char* vertexShader;
const char* fragmentShader;
std::unique_ptr<OpenGLShaderProgram> shader;
std::unique_ptr<Shape> shape;
std::unique_ptr<Attributes> attributes;
std::unique_ptr<Uniforms> uniforms;
String newVertexShader, newFragmentShader;
Rectangle<int> bounds;
CriticalSection mutex;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLAppDemo)
};

1295
deps/juce/examples/GUI/OpenGLDemo.h vendored Normal file

File diff suppressed because it is too large Load Diff

261
deps/juce/examples/GUI/OpenGLDemo2D.h vendored Normal file
View File

@ -0,0 +1,261 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: OpenGLDemo2D
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Simple 2D OpenGL application.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra, juce_opengl
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: OpenGLDemo2D
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
class OpenGLDemo2D : public Component,
private CodeDocument::Listener,
private Timer
{
public:
OpenGLDemo2D()
{
setOpaque (true);
if (auto* peer = getPeer())
peer->setCurrentRenderingEngine (0);
openGLContext.attachTo (*getTopLevelComponent());
addAndMakeVisible (statusLabel);
statusLabel.setJustificationType (Justification::topLeft);
statusLabel.setFont (Font (14.0f));
auto presets = getPresets();
for (int i = 0; i < presets.size(); ++i)
presetBox.addItem (presets[i].name, i + 1);
addAndMakeVisible (presetLabel);
presetLabel.attachToComponent (&presetBox, true);
addAndMakeVisible (presetBox);
presetBox.onChange = [this] { selectPreset (presetBox.getSelectedItemIndex()); };
fragmentEditorComp.setOpaque (false);
fragmentDocument.addListener (this);
addAndMakeVisible (fragmentEditorComp);
presetBox.setSelectedItemIndex (0);
setSize (500, 500);
}
~OpenGLDemo2D() override
{
openGLContext.detach();
shader.reset();
}
void paint (Graphics& g) override
{
g.fillCheckerBoard (getLocalBounds().toFloat(), 48.0f, 48.0f, Colours::lightgrey, Colours::white);
if (shader.get() == nullptr || shader->getFragmentShaderCode() != fragmentCode)
{
shader.reset();
if (fragmentCode.isNotEmpty())
{
shader.reset (new OpenGLGraphicsContextCustomShader (fragmentCode));
auto result = shader->checkCompilation (g.getInternalContext());
if (result.failed())
{
statusLabel.setText (result.getErrorMessage(), dontSendNotification);
shader.reset();
}
}
}
if (shader.get() != nullptr)
{
statusLabel.setText ({}, dontSendNotification);
shader->fillRect (g.getInternalContext(), getLocalBounds());
}
}
void resized() override
{
auto area = getLocalBounds().reduced (4);
statusLabel.setBounds (area.removeFromTop (75));
area.removeFromTop (area.getHeight() / 2);
auto presets = area.removeFromTop (25);
presets.removeFromLeft (100);
presetBox.setBounds (presets.removeFromLeft (150));
area.removeFromTop (4);
fragmentEditorComp.setBounds (area);
}
void selectPreset (int preset)
{
fragmentDocument.replaceAllContent (getPresets()[preset].fragmentShader);
startTimer (1);
}
std::unique_ptr<OpenGLGraphicsContextCustomShader> shader;
Label statusLabel, presetLabel { {}, "Shader Preset:" };
ComboBox presetBox;
CodeDocument fragmentDocument;
CodeEditorComponent fragmentEditorComp { fragmentDocument, nullptr };
String fragmentCode;
private:
OpenGLContext openGLContext;
enum { shaderLinkDelay = 500 };
void codeDocumentTextInserted (const String& /*newText*/, int /*insertIndex*/) override
{
startTimer (shaderLinkDelay);
}
void codeDocumentTextDeleted (int /*startIndex*/, int /*endIndex*/) override
{
startTimer (shaderLinkDelay);
}
void timerCallback() override
{
stopTimer();
fragmentCode = fragmentDocument.getAllContent();
repaint();
}
struct ShaderPreset
{
const char* name;
const char* fragmentShader;
};
static Array<ShaderPreset> getPresets()
{
#define SHADER_2DDEMO_HEADER \
"/* This demo shows the use of the OpenGLGraphicsContextCustomShader,\n" \
" which allows a 2D area to be filled using a GL shader program.\n" \
"\n" \
" Edit the shader program below and it will be \n" \
" recompiled in real-time!\n" \
"*/\n\n"
ShaderPreset presets[] =
{
{
"Simple Gradient",
SHADER_2DDEMO_HEADER
"void main()\n"
"{\n"
" " JUCE_MEDIUMP " vec4 colour1 = vec4 (1.0, 0.4, 0.6, 1.0);\n"
" " JUCE_MEDIUMP " vec4 colour2 = vec4 (0.0, 0.8, 0.6, 1.0);\n"
" " JUCE_MEDIUMP " float alpha = pixelPos.x / 1000.0;\n"
" gl_FragColor = pixelAlpha * mix (colour1, colour2, alpha);\n"
"}\n"
},
{
"Circular Gradient",
SHADER_2DDEMO_HEADER
"void main()\n"
"{\n"
" " JUCE_MEDIUMP " vec4 colour1 = vec4 (1.0, 0.4, 0.6, 1.0);\n"
" " JUCE_MEDIUMP " vec4 colour2 = vec4 (0.3, 0.4, 0.4, 1.0);\n"
" " JUCE_MEDIUMP " float alpha = distance (pixelPos, vec2 (600.0, 500.0)) / 400.0;\n"
" gl_FragColor = pixelAlpha * mix (colour1, colour2, alpha);\n"
"}\n"
},
{
"Circle",
SHADER_2DDEMO_HEADER
"void main()\n"
"{\n"
" " JUCE_MEDIUMP " vec4 colour1 = vec4 (0.1, 0.1, 0.9, 1.0);\n"
" " JUCE_MEDIUMP " vec4 colour2 = vec4 (0.0, 0.8, 0.6, 1.0);\n"
" " JUCE_MEDIUMP " float distance = distance (pixelPos, vec2 (600.0, 500.0));\n"
"\n"
" " JUCE_MEDIUMP " float innerRadius = 200.0;\n"
" " JUCE_MEDIUMP " float outerRadius = 210.0;\n"
"\n"
" if (distance < innerRadius)\n"
" gl_FragColor = colour1;\n"
" else if (distance > outerRadius)\n"
" gl_FragColor = colour2;\n"
" else\n"
" gl_FragColor = mix (colour1, colour2, (distance - innerRadius) / (outerRadius - innerRadius));\n"
"\n"
" gl_FragColor *= pixelAlpha;\n"
"}\n"
},
{
"Solid Colour",
SHADER_2DDEMO_HEADER
"void main()\n"
"{\n"
" gl_FragColor = vec4 (1.0, 0.6, 0.1, pixelAlpha);\n"
"}\n"
}
};
return Array<ShaderPreset> (presets, numElementsInArray (presets));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLDemo2D)
};

228
deps/juce/examples/GUI/PropertiesDemo.h vendored Normal file
View File

@ -0,0 +1,228 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: PropertiesDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Displays various property components.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: PropertiesDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
class DemoButtonPropertyComponent : public ButtonPropertyComponent
{
public:
DemoButtonPropertyComponent (const String& propertyName)
: ButtonPropertyComponent (propertyName, true)
{
refresh();
}
void buttonClicked() override
{
++counter;
AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, "Action Button Pressed",
"Pressing this type of property component can trigger an action such as showing an alert window!");
refresh();
}
String getButtonText() const override
{
return "Button clicked " + String (counter) + " times";
}
private:
int counter = 0;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoButtonPropertyComponent)
};
//==============================================================================
class DemoSliderPropertyComponent : public SliderPropertyComponent
{
public:
DemoSliderPropertyComponent (const String& propertyName)
: SliderPropertyComponent (propertyName, 0.0, 100.0, 0.001)
{
setValue (Random::getSystemRandom().nextDouble() * 42.0);
}
void setValue (double newValue) override
{
slider.setValue (newValue);
}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoSliderPropertyComponent)
};
//==============================================================================
static Array<PropertyComponent*> createTextEditors()
{
return { new TextPropertyComponent (Value (var ("This is a single-line Text Property")), "Text 1", 200, false),
new TextPropertyComponent (Value (var ("Another one")), "Text 2", 200, false),
new TextPropertyComponent (Value (var ( "Lorem ipsum dolor sit amet, cu mei labore admodum facilisi. Iriure iuvaret invenire ea vim, cum quod"
"si intellegat delicatissimi an. Cetero recteque ei eos, his an scripta fastidii placerat. Nec et anc"
"illae nominati corrumpit. Vis dictas audire accumsan ad, elit fabulas saperet mel eu.\n"
"\n"
"Dicam utroque ius ne, eum choro phaedrum eu. Ut mel omnes virtute appareat, semper quodsi labitur in"
" cum. Est aeque eripuit deleniti in, amet ferri recusabo ea nec. Cu persius maiorum corrumpit mei, i"
"n ridens perpetua mea, pri nobis tation inermis an. Vis alii autem cotidieque ut, ius harum salutatu"
"s ut. Mel eu purto veniam dissentias, malis doctus bonorum ne vel, mundi aperiam adversarium cu eum."
" Mei quando graeci te, dolore accusata mei te.")),
"Multi-line text",
1000, true) };
}
static Array<PropertyComponent*> createSliders (int howMany)
{
Array<PropertyComponent*> comps;
for (int i = 0; i < howMany; ++i)
comps.add (new DemoSliderPropertyComponent ("Slider " + String (i + 1)));
return comps;
}
static Array<PropertyComponent*> createButtons (int howMany)
{
Array<PropertyComponent*> comps;
for (int i = 0; i < howMany; ++i)
comps.add (new DemoButtonPropertyComponent ("Button " + String (i + 1)));
for (int i = 0; i < howMany; ++i)
comps.add (new BooleanPropertyComponent (Value (Random::getSystemRandom().nextBool()), "Toggle " + String (i + 1), "Description of toggleable thing"));
return comps;
}
static Array<PropertyComponent*> createChoices (int howMany)
{
Array<PropertyComponent*> comps;
StringArray choices;
Array<var> choiceVars;
for (int i = 0; i < 12; ++i)
{
choices.add ("Item " + String (i));
choiceVars.add (i);
}
for (int i = 0; i < howMany; ++i)
comps.add (new ChoicePropertyComponent (Value (Random::getSystemRandom().nextInt (12)), "Choice Property " + String (i + 1), choices, choiceVars));
for (int i = 0; i < howMany; ++i)
comps.add (new MultiChoicePropertyComponent (Value (Array<var>()), "Multi-Choice Property " + String (i + 1), choices, choiceVars));
return comps;
}
//==============================================================================
class PropertiesDemo : public Component,
private Timer
{
public:
PropertiesDemo()
{
setOpaque (true);
addAndMakeVisible (concertinaPanel);
{
auto* panel = new PropertyPanel ("Text Editors");
panel->addProperties (createTextEditors());
addPanel (panel);
}
{
auto* panel = new PropertyPanel ("Sliders");
panel->addSection ("Section 1", createSliders (4), true);
panel->addSection ("Section 2", createSliders (3), true);
addPanel (panel);
}
{
auto* panel = new PropertyPanel ("Choice Properties");
panel->addProperties (createChoices (3));
addPanel (panel);
}
{
auto* panel = new PropertyPanel ("Buttons & Toggles");
panel->addProperties (createButtons (6));
addPanel (panel);
}
setSize (750, 650);
startTimer (300);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
Colour::greyLevel (0.8f)));
}
void resized() override
{
concertinaPanel.setBounds (getLocalBounds().reduced (4));
}
void timerCallback() override
{
stopTimer();
concertinaPanel.expandPanelFully (concertinaPanel.getPanel (0), true);
}
private:
ConcertinaPanel concertinaPanel;
void addPanel (PropertyPanel* panel)
{
concertinaPanel.addPanel (-1, panel, true);
concertinaPanel.setMaximumPanelSize (panel, panel->getTotalContentHeight());
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertiesDemo)
};

708
deps/juce/examples/GUI/VideoDemo.h vendored Normal file
View File

@ -0,0 +1,708 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: VideoDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Plays video files.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra, juce_video
exporters: xcode_mac, vs2019, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: VideoDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#if JUCE_MAC || JUCE_WINDOWS
//==============================================================================
// so that we can easily have two video windows each with a file browser, wrap this up as a class..
class MovieComponentWithFileBrowser : public Component,
public DragAndDropTarget,
private FilenameComponentListener
{
public:
MovieComponentWithFileBrowser()
: videoComp (true)
{
addAndMakeVisible (videoComp);
addAndMakeVisible (fileChooser);
fileChooser.addListener (this);
fileChooser.setBrowseButtonText ("browse");
}
void setFile (const File& file)
{
fileChooser.setCurrentFile (file, true);
}
void paintOverChildren (Graphics& g) override
{
if (isDragOver)
{
g.setColour (Colours::red);
g.drawRect (fileChooser.getBounds(), 2);
}
}
void resized() override
{
videoComp.setBounds (getLocalBounds().reduced (10));
}
bool isInterestedInDragSource (const SourceDetails&) override { return true; }
void itemDragEnter (const SourceDetails&) override
{
isDragOver = true;
repaint();
}
void itemDragExit (const SourceDetails&) override
{
isDragOver = false;
repaint();
}
void itemDropped (const SourceDetails& dragSourceDetails) override
{
setFile (dragSourceDetails.description.toString());
isDragOver = false;
repaint();
}
private:
VideoComponent videoComp;
bool isDragOver = false;
FilenameComponent fileChooser { "movie", {}, true, false, false, "*", {}, "(choose a video file to play)"};
void filenameComponentChanged (FilenameComponent*) override
{
auto url = URL (fileChooser.getCurrentFile());
// this is called when the user changes the filename in the file chooser box
auto result = videoComp.load (url);
videoLoadingFinished (url, result);
}
void videoLoadingFinished (const URL& url, Result result)
{
ignoreUnused (url);
if (result.wasOk())
{
// loaded the file ok, so let's start it playing..
videoComp.play();
resized(); // update to reflect the video's aspect ratio
}
else
{
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
"Couldn't load the file!",
result.getErrorMessage());
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MovieComponentWithFileBrowser)
};
//==============================================================================
class VideoDemo : public Component,
public DragAndDropContainer,
private FileBrowserListener
{
public:
VideoDemo()
{
setOpaque (true);
movieList.setDirectory (File::getSpecialLocation (File::userMoviesDirectory), true, true);
directoryThread.startThread (1);
fileTree.setTitle ("Files");
fileTree.addListener (this);
fileTree.setColour (FileTreeComponent::backgroundColourId, Colours::lightgrey.withAlpha (0.6f));
addAndMakeVisible (fileTree);
addAndMakeVisible (resizerBar);
loadLeftButton .onClick = [this] { movieCompLeft .setFile (fileTree.getSelectedFile (0)); };
loadRightButton.onClick = [this] { movieCompRight.setFile (fileTree.getSelectedFile (0)); };
addAndMakeVisible (loadLeftButton);
addAndMakeVisible (loadRightButton);
addAndMakeVisible (movieCompLeft);
addAndMakeVisible (movieCompRight);
// we have to set up our StretchableLayoutManager so it know the limits and preferred sizes of it's contents
stretchableManager.setItemLayout (0, // for the fileTree
-0.1, -0.9, // must be between 50 pixels and 90% of the available space
-0.3); // and its preferred size is 30% of the total available space
stretchableManager.setItemLayout (1, // for the resize bar
5, 5, 5); // hard limit to 5 pixels
stretchableManager.setItemLayout (2, // for the movie components
-0.1, -0.9, // size must be between 50 pixels and 90% of the available space
-0.7); // and its preferred size is 70% of the total available space
setSize (500, 500);
}
~VideoDemo() override
{
fileTree.removeListener (this);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
}
void resized() override
{
// make a list of two of our child components that we want to reposition
Component* comps[] = { &fileTree, &resizerBar, nullptr };
// this will position the 3 components, one above the other, to fit
// vertically into the rectangle provided.
stretchableManager.layOutComponents (comps, 3,
0, 0, getWidth(), getHeight(),
true, true);
// now position out two video components in the space that's left
auto area = getLocalBounds().removeFromBottom (getHeight() - resizerBar.getBottom());
{
auto buttonArea = area.removeFromTop (30);
loadLeftButton .setBounds (buttonArea.removeFromLeft (buttonArea.getWidth() / 2).reduced (5));
loadRightButton.setBounds (buttonArea.reduced (5));
}
movieCompLeft .setBounds (area.removeFromLeft (area.getWidth() / 2).reduced (5));
movieCompRight.setBounds (area.reduced (5));
}
private:
std::unique_ptr<FileChooser> fileChooser;
WildcardFileFilter moviesWildcardFilter { "*", "*", "Movies File Filter" };
TimeSliceThread directoryThread { "Movie File Scanner Thread" };
DirectoryContentsList movieList { &moviesWildcardFilter, directoryThread };
FileTreeComponent fileTree { movieList };
StretchableLayoutManager stretchableManager;
StretchableLayoutResizerBar resizerBar { &stretchableManager, 1, false };
TextButton loadLeftButton { "Load Left" },
loadRightButton { "Load Right" };
MovieComponentWithFileBrowser movieCompLeft, movieCompRight;
void selectionChanged() override
{
// we're just going to update the drag description of out tree so that rows can be dragged onto the file players
fileTree.setDragAndDropDescription (fileTree.getSelectedFile().getFullPathName());
}
void fileClicked (const File&, const MouseEvent&) override {}
void fileDoubleClicked (const File&) override {}
void browserRootChanged (const File&) override {}
void selectVideoFile()
{
fileChooser.reset (new FileChooser ("Choose a file to open...", File::getCurrentWorkingDirectory(),
"*", false));
fileChooser->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles,
[this] (const FileChooser& chooser)
{
String chosen;
auto results = chooser.getURLResults();
// TODO: support non local files too
if (results.size() > 0)
movieCompLeft.setFile (results[0].getLocalFile());
});
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VideoDemo)
};
#elif JUCE_IOS || JUCE_ANDROID
//==============================================================================
class VideoDemo : public Component,
private Timer
{
public:
VideoDemo()
: videoCompWithNativeControls (true),
videoCompNoNativeControls (false)
{
loadLocalButton .onClick = [this] { selectVideoFile(); };
loadUrlButton .onClick = [this] { showVideoUrlPrompt(); };
seekToStartButton.onClick = [this] { seekVideoToStart(); };
playButton .onClick = [this] { playVideo(); };
pauseButton .onClick = [this] { pauseVideo(); };
unloadButton .onClick = [this] { unloadVideoFile(); };
volumeLabel .setColour (Label::textColourId, Colours::white);
currentPositionLabel.setColour (Label::textColourId, Colours::white);
volumeLabel .setJustificationType (Justification::right);
currentPositionLabel.setJustificationType (Justification::right);
volumeSlider .setRange (0.0, 1.0);
positionSlider.setRange (0.0, 1.0);
volumeSlider .setSliderSnapsToMousePosition (false);
positionSlider.setSliderSnapsToMousePosition (false);
volumeSlider.setSkewFactor (1.5);
volumeSlider.setValue (1.0, dontSendNotification);
#if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
curVideoComp->onGlobalMediaVolumeChanged = [this]() { volumeSlider.setValue (curVideoComp->getAudioVolume(), dontSendNotification); };
#endif
volumeSlider .onValueChange = [this]() { curVideoComp->setAudioVolume ((float) volumeSlider.getValue()); };
positionSlider.onValueChange = [this]() { seekVideoToNormalisedPosition (positionSlider.getValue()); };
positionSlider.onDragStart = [this]()
{
positionSliderDragging = true;
wasPlayingBeforeDragStart = curVideoComp->isPlaying();
if (wasPlayingBeforeDragStart)
curVideoComp->stop();
};
positionSlider.onDragEnd = [this]()
{
if (wasPlayingBeforeDragStart)
curVideoComp->play();
wasPlayingBeforeDragStart = false;
// Ensure the slider does not temporarily jump back on consecutive timer callback.
Timer::callAfterDelay (500, [this]() { positionSliderDragging = false; });
};
playSpeedComboBox.addItem ("25%", 25);
playSpeedComboBox.addItem ("50%", 50);
playSpeedComboBox.addItem ("100%", 100);
playSpeedComboBox.addItem ("200%", 200);
playSpeedComboBox.addItem ("400%", 400);
playSpeedComboBox.setSelectedId (100, dontSendNotification);
playSpeedComboBox.onChange = [this]() { curVideoComp->setPlaySpeed (playSpeedComboBox.getSelectedId() / 100.0); };
setTransportControlsEnabled (false);
addAndMakeVisible (loadLocalButton);
addAndMakeVisible (loadUrlButton);
addAndMakeVisible (volumeLabel);
addAndMakeVisible (volumeSlider);
addChildComponent (videoCompWithNativeControls);
addChildComponent (videoCompNoNativeControls);
addAndMakeVisible (positionSlider);
addAndMakeVisible (currentPositionLabel);
addAndMakeVisible (playSpeedComboBox);
addAndMakeVisible (seekToStartButton);
addAndMakeVisible (playButton);
addAndMakeVisible (unloadButton);
addChildComponent (pauseButton);
setSize (500, 500);
RuntimePermissions::request (RuntimePermissions::readExternalStorage,
[] (bool granted)
{
if (! granted)
{
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
"Permissions warning",
"External storage access permission not granted, some files"
" may be inaccessible.");
}
});
setPortraitOrientationEnabled (true);
}
~VideoDemo() override
{
curVideoComp->onPlaybackStarted = nullptr;
curVideoComp->onPlaybackStopped = nullptr;
curVideoComp->onErrorOccurred = nullptr;
curVideoComp->onGlobalMediaVolumeChanged = nullptr;
setPortraitOrientationEnabled (false);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
}
void resized() override
{
auto area = getLocalBounds();
int marginSize = 5;
int buttonHeight = 20;
area.reduce (0, marginSize);
auto topArea = area.removeFromTop (buttonHeight);
loadLocalButton.setBounds (topArea.removeFromLeft (topArea.getWidth() / 6));
loadUrlButton.setBounds (topArea.removeFromLeft (loadLocalButton.getWidth()));
volumeLabel.setBounds (topArea.removeFromLeft (loadLocalButton.getWidth()));
volumeSlider.setBounds (topArea.reduced (10, 0));
auto transportArea = area.removeFromBottom (buttonHeight);
auto positionArea = area.removeFromBottom (buttonHeight).reduced (marginSize, 0);
playSpeedComboBox.setBounds (transportArea.removeFromLeft (jmax (50, transportArea.getWidth() / 5)));
auto controlWidth = transportArea.getWidth() / 3;
currentPositionLabel.setBounds (positionArea.removeFromRight (jmax (150, controlWidth)));
positionSlider.setBounds (positionArea);
seekToStartButton.setBounds (transportArea.removeFromLeft (controlWidth));
playButton .setBounds (transportArea.removeFromLeft (controlWidth));
unloadButton .setBounds (transportArea.removeFromLeft (controlWidth));
pauseButton.setBounds (playButton.getBounds());
area.removeFromTop (marginSize);
area.removeFromBottom (marginSize);
videoCompWithNativeControls.setBounds (area);
videoCompNoNativeControls.setBounds (area);
if (positionSlider.getWidth() > 0)
positionSlider.setMouseDragSensitivity (positionSlider.getWidth());
}
private:
TextButton loadLocalButton { "Load Local" };
TextButton loadUrlButton { "Load URL" };
Label volumeLabel { "volumeLabel", "Vol:" };
Slider volumeSlider { Slider::LinearHorizontal, Slider::NoTextBox };
VideoComponent videoCompWithNativeControls;
VideoComponent videoCompNoNativeControls;
#if JUCE_IOS || JUCE_MAC
VideoComponent* curVideoComp = &videoCompWithNativeControls;
#else
VideoComponent* curVideoComp = &videoCompNoNativeControls;
#endif
bool isFirstSetup = true;
Slider positionSlider { Slider::LinearHorizontal, Slider::NoTextBox };
bool positionSliderDragging = false;
bool wasPlayingBeforeDragStart = false;
Label currentPositionLabel { "currentPositionLabel", "-:- / -:-" };
ComboBox playSpeedComboBox { "playSpeedComboBox" };
TextButton seekToStartButton { "|<" };
TextButton playButton { "Play" };
TextButton pauseButton { "Pause" };
TextButton unloadButton { "Unload" };
std::unique_ptr<FileChooser> fileChooser;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VideoDemo)
JUCE_DECLARE_WEAK_REFERENCEABLE (VideoDemo)
//==============================================================================
void setPortraitOrientationEnabled (bool shouldBeEnabled)
{
auto allowedOrientations = Desktop::getInstance().getOrientationsEnabled();
if (shouldBeEnabled)
allowedOrientations |= Desktop::upright;
else
allowedOrientations &= ~Desktop::upright;
Desktop::getInstance().setOrientationsEnabled (allowedOrientations);
}
void setTransportControlsEnabled (bool shouldBeEnabled)
{
positionSlider .setEnabled (shouldBeEnabled);
playSpeedComboBox.setEnabled (shouldBeEnabled);
seekToStartButton.setEnabled (shouldBeEnabled);
playButton .setEnabled (shouldBeEnabled);
unloadButton .setEnabled (shouldBeEnabled);
pauseButton .setEnabled (shouldBeEnabled);
}
void selectVideoFile()
{
fileChooser.reset (new FileChooser ("Choose a video file to open...", File::getCurrentWorkingDirectory(),
"*", true));
fileChooser->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles,
[this] (const FileChooser& chooser)
{
auto results = chooser.getURLResults();
if (results.size() > 0)
loadVideo (results[0]);
});
}
void loadVideo (const URL& url)
{
unloadVideoFile();
#if JUCE_IOS || JUCE_MAC
askIfUseNativeControls (url);
#else
loadUrl (url);
setupVideoComp (false);
#endif
}
void askIfUseNativeControls (const URL& url)
{
auto* aw = new AlertWindow ("Choose viewer type", {}, MessageBoxIconType::NoIcon);
aw->addButton ("Yes", 1, KeyPress (KeyPress::returnKey));
aw->addButton ("No", 0, KeyPress (KeyPress::escapeKey));
aw->addTextBlock ("Do you want to use the viewer with native controls?");
auto callback = ModalCallbackFunction::forComponent (videoViewerTypeChosen, this, url);
aw->enterModalState (true, callback, true);
}
static void videoViewerTypeChosen (int result, VideoDemo* owner, URL url)
{
if (owner != nullptr)
{
owner->setupVideoComp (result != 0);
owner->loadUrl (url);
}
}
void setupVideoComp (bool useNativeViewerWithNativeControls)
{
auto* oldVideoComp = curVideoComp;
if (useNativeViewerWithNativeControls)
curVideoComp = &videoCompWithNativeControls;
else
curVideoComp = &videoCompNoNativeControls;
if (isFirstSetup || oldVideoComp != curVideoComp)
{
oldVideoComp->onPlaybackStarted = nullptr;
oldVideoComp->onPlaybackStopped = nullptr;
oldVideoComp->onErrorOccurred = nullptr;
oldVideoComp->setVisible (false);
curVideoComp->onPlaybackStarted = [this]() { processPlaybackStarted(); };
curVideoComp->onPlaybackStopped = [this]() { processPlaybackPaused(); };
curVideoComp->onErrorOccurred = [this](const String& errorMessage) { errorOccurred (errorMessage); };
curVideoComp->setVisible (true);
#if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
oldVideoComp->onGlobalMediaVolumeChanged = nullptr;
curVideoComp->onGlobalMediaVolumeChanged = [this]() { volumeSlider.setValue (curVideoComp->getAudioVolume(), dontSendNotification); };
#endif
}
isFirstSetup = false;
}
void loadUrl (const URL& url)
{
curVideoComp->loadAsync (url, [this] (const URL& u, Result r) { videoLoadingFinished (u, r); });
}
void showVideoUrlPrompt()
{
auto* aw = new AlertWindow ("Enter URL for video to load", {}, MessageBoxIconType::NoIcon);
aw->addButton ("OK", 1, KeyPress (KeyPress::returnKey));
aw->addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey));
aw->addTextEditor ("videoUrlTextEditor", "https://www.rmp-streaming.com/media/bbb-360p.mp4");
auto callback = ModalCallbackFunction::forComponent (videoUrlPromptClosed, this, Component::SafePointer<AlertWindow> (aw));
aw->enterModalState (true, callback, true);
}
static void videoUrlPromptClosed (int result, VideoDemo* owner, Component::SafePointer<AlertWindow> aw)
{
if (result != 0 && owner != nullptr && aw != nullptr)
{
auto url = aw->getTextEditorContents ("videoUrlTextEditor");
if (url.isNotEmpty())
owner->loadVideo (url);
}
}
void videoLoadingFinished (const URL& url, Result result)
{
ignoreUnused (url);
if (result.wasOk())
{
resized(); // update to reflect the video's aspect ratio
setTransportControlsEnabled (true);
currentPositionLabel.setText (getPositionString (0.0, curVideoComp->getVideoDuration()), sendNotification);
positionSlider.setValue (0.0, dontSendNotification);
playSpeedComboBox.setSelectedId (100, dontSendNotification);
}
else
{
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
"Couldn't load the file!",
result.getErrorMessage());
}
}
static String getPositionString (double playPositionSeconds, double durationSeconds)
{
auto positionMs = static_cast<int> (1000 * playPositionSeconds);
int posMinutes = positionMs / 60000;
int posSeconds = (positionMs % 60000) / 1000;
int posMillis = positionMs % 1000;
auto totalMs = static_cast<int> (1000 * durationSeconds);
int totMinutes = totalMs / 60000;
int totSeconds = (totalMs % 60000) / 1000;
int totMillis = totalMs % 1000;
return String::formatted ("%02d:%02d:%03d / %02d:%02d:%03d",
posMinutes, posSeconds, posMillis,
totMinutes, totSeconds, totMillis);
}
void updatePositionSliderAndLabel()
{
auto position = curVideoComp->getPlayPosition();
auto duration = curVideoComp->getVideoDuration();
currentPositionLabel.setText (getPositionString (position, duration), sendNotification);
if (! positionSliderDragging)
positionSlider.setValue (duration != 0 ? (position / duration) : 0.0, dontSendNotification);
}
void seekVideoToStart()
{
seekVideoToNormalisedPosition (0.0);
}
void seekVideoToNormalisedPosition (double normalisedPos)
{
normalisedPos = jlimit (0.0, 1.0, normalisedPos);
auto duration = curVideoComp->getVideoDuration();
auto newPos = jlimit (0.0, duration, duration * normalisedPos);
curVideoComp->setPlayPosition (newPos);
currentPositionLabel.setText (getPositionString (newPos, curVideoComp->getVideoDuration()), sendNotification);
positionSlider.setValue (normalisedPos, dontSendNotification);
}
void playVideo()
{
curVideoComp->play();
}
void processPlaybackStarted()
{
playButton.setVisible (false);
pauseButton.setVisible (true);
startTimer (20);
}
void pauseVideo()
{
curVideoComp->stop();
}
void processPlaybackPaused()
{
// On seeking to a new pos, the playback may be temporarily paused.
if (positionSliderDragging)
return;
pauseButton.setVisible (false);
playButton.setVisible (true);
}
void errorOccurred (const String& errorMessage)
{
AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon,
"An error has occurred",
errorMessage + ", video will be unloaded.");
unloadVideoFile();
}
void unloadVideoFile()
{
curVideoComp->closeVideo();
setTransportControlsEnabled (false);
stopTimer();
pauseButton.setVisible (false);
playButton.setVisible (true);
currentPositionLabel.setText ("-:- / -:-", sendNotification);
positionSlider.setValue (0.0, dontSendNotification);
}
void timerCallback() override
{
updatePositionSliderAndLabel();
}
};
#elif JUCE_LINUX || JUCE_BSD
#error "This demo is not supported on Linux!"
#endif

150
deps/juce/examples/GUI/WebBrowserDemo.h vendored Normal file
View File

@ -0,0 +1,150 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: WebBrowserDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Displays a web browser.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: WebBrowserDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
/** We'll use a subclass of WebBrowserComponent to demonstrate how to get callbacks
when the browser changes URL. You don't need to do this, you can just also
just use the WebBrowserComponent class directly.
*/
class DemoBrowserComponent : public WebBrowserComponent
{
public:
//==============================================================================
DemoBrowserComponent (TextEditor& addressBox)
: addressTextBox (addressBox)
{}
// This method gets called when the browser is about to go to a new URL..
bool pageAboutToLoad (const String& newURL) override
{
// We'll just update our address box to reflect the new location..
addressTextBox.setText (newURL, false);
// we could return false here to tell the browser not to go ahead with
// loading the page.
return true;
}
// This method gets called when the browser is requested to launch a new window
void newWindowAttemptingToLoad (const String& newURL) override
{
// We'll just load the URL into the main window
goToURL (newURL);
}
private:
TextEditor& addressTextBox;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoBrowserComponent)
};
//==============================================================================
class WebBrowserDemo : public Component
{
public:
WebBrowserDemo()
{
setOpaque (true);
// Create an address box..
addAndMakeVisible (addressTextBox);
addressTextBox.setTextToShowWhenEmpty ("Enter a web address, e.g. https://www.juce.com", Colours::grey);
addressTextBox.onReturnKey = [this] { webView->goToURL (addressTextBox.getText()); };
// create the actual browser component
webView.reset (new DemoBrowserComponent (addressTextBox));
addAndMakeVisible (webView.get());
// add some buttons..
addAndMakeVisible (goButton);
goButton.onClick = [this] { webView->goToURL (addressTextBox.getText()); };
addAndMakeVisible (backButton);
backButton.onClick = [this] { webView->goBack(); };
addAndMakeVisible (forwardButton);
forwardButton.onClick = [this] { webView->goForward(); };
// send the browser to a start page..
webView->goToURL ("https://www.juce.com");
setSize (1000, 1000);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
Colours::grey));
}
void resized() override
{
webView->setBounds (10, 45, getWidth() - 20, getHeight() - 55);
goButton .setBounds (getWidth() - 45, 10, 35, 25);
addressTextBox.setBounds (100, 10, getWidth() - 155, 25);
backButton .setBounds (10, 10, 35, 25);
forwardButton .setBounds (55, 10, 35, 25);
}
private:
std::unique_ptr<DemoBrowserComponent> webView;
TextEditor addressTextBox;
TextButton goButton { "Go", "Go to URL" },
backButton { "<<", "Back" },
forwardButton { ">>", "Forward" };
void lookAndFeelChanged() override
{
addressTextBox.applyFontToAllText (addressTextBox.getFont());
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebBrowserDemo)
};

1576
deps/juce/examples/GUI/WidgetsDemo.h vendored Normal file

File diff suppressed because it is too large Load Diff

365
deps/juce/examples/GUI/WindowsDemo.h vendored Normal file
View File

@ -0,0 +1,365 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: WindowsDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Displays various types of windows.
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: WindowsDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
/** Just a simple window that deletes itself when closed. */
class BasicWindow : public DocumentWindow
{
public:
BasicWindow (const String& name, Colour backgroundColour, int buttonsNeeded)
: DocumentWindow (name, backgroundColour, buttonsNeeded)
{}
void closeButtonPressed()
{
delete this;
}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BasicWindow)
};
//==============================================================================
/** This window contains a ColourSelector which can be used to change the window's colour. */
class ColourSelectorWindow : public DocumentWindow,
private ChangeListener
{
public:
ColourSelectorWindow (const String& name, Colour backgroundColour, int buttonsNeeded)
: DocumentWindow (name, backgroundColour, buttonsNeeded)
{
selector.setCurrentColour (backgroundColour);
selector.setColour (ColourSelector::backgroundColourId, Colours::transparentWhite);
selector.addChangeListener (this);
setContentOwned (&selector, false);
}
~ColourSelectorWindow()
{
selector.removeChangeListener (this);
}
void closeButtonPressed()
{
delete this;
}
private:
ColourSelector selector { ColourSelector::showColourAtTop
| ColourSelector::showSliders
| ColourSelector::showColourspace };
void changeListenerCallback (ChangeBroadcaster* source)
{
if (source == &selector)
setBackgroundColour (selector.getCurrentColour());
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourSelectorWindow)
};
//==============================================================================
class BouncingBallComponent : public Component,
public Timer
{
public:
BouncingBallComponent()
{
setInterceptsMouseClicks (false, false);
Random random;
auto size = 10.0f + (float) random.nextInt (30);
ballBounds.setBounds (random.nextFloat() * 100.0f,
random.nextFloat() * 100.0f,
size, size);
direction.x = random.nextFloat() * 8.0f - 4.0f;
direction.y = random.nextFloat() * 8.0f - 4.0f;
colour = Colour ((juce::uint32) random.nextInt())
.withAlpha (0.5f)
.withBrightness (0.7f);
startTimer (60);
}
void paint (Graphics& g) override
{
g.setColour (colour);
g.fillEllipse (ballBounds - getPosition().toFloat());
}
void timerCallback() override
{
ballBounds += direction;
if (ballBounds.getX() < 0) direction.x = std::abs (direction.x);
if (ballBounds.getY() < 0) direction.y = std::abs (direction.y);
if (ballBounds.getRight() > (float) getParentWidth()) direction.x = -std::abs (direction.x);
if (ballBounds.getBottom() > (float) getParentHeight()) direction.y = -std::abs (direction.y);
setBounds (ballBounds.getSmallestIntegerContainer());
}
private:
Colour colour;
Rectangle<float> ballBounds;
Point<float> direction;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBallComponent)
};
//==============================================================================
class BouncingBallsContainer : public Component
{
public:
BouncingBallsContainer (int numBalls)
{
for (int i = 0; i < numBalls; ++i)
{
auto* newBall = new BouncingBallComponent();
balls.add (newBall);
addAndMakeVisible (newBall);
}
}
void mouseDown (const MouseEvent& e) override
{
dragger.startDraggingComponent (this, e);
}
void mouseDrag (const MouseEvent& e) override
{
// as there's no titlebar we have to manage the dragging ourselves
dragger.dragComponent (this, e, nullptr);
}
void paint (Graphics& g) override
{
if (isOpaque())
g.fillAll (Colours::white);
else
g.fillAll (Colours::blue.withAlpha (0.2f));
g.setFont (16.0f);
g.setColour (Colours::black);
g.drawFittedText ("This window has no titlebar and a transparent background.",
getLocalBounds().reduced (8, 0),
Justification::centred, 5);
g.drawRect (getLocalBounds());
}
private:
ComponentDragger dragger;
OwnedArray<BouncingBallComponent> balls;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBallsContainer)
};
//==============================================================================
class WindowsDemo : public Component
{
public:
enum Windows
{
dialog,
document,
alert,
numWindows
};
WindowsDemo()
{
setOpaque (true);
addAndMakeVisible (showWindowsButton);
showWindowsButton.onClick = [this] { showAllWindows(); };
addAndMakeVisible (closeWindowsButton);
closeWindowsButton.onClick = [this] { closeAllWindows(); };
setSize (250, 250);
}
~WindowsDemo() override
{
if (dialogWindow != nullptr)
{
dialogWindow->exitModalState (0);
// we are shutting down: can't wait for the message manager
// to eventually delete this
delete dialogWindow;
}
closeAllWindows();
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
Colours::grey));
}
void resized() override
{
Rectangle<int> buttonSize (0, 0, 108, 28);
Rectangle<int> area ((getWidth() / 2) - (buttonSize.getWidth() / 2),
(getHeight() / 2) - buttonSize.getHeight(),
buttonSize.getWidth(), buttonSize.getHeight());
showWindowsButton .setBounds (area.reduced (2));
closeWindowsButton.setBounds (area.translated (0, buttonSize.getHeight()).reduced (2));
}
private:
// Because in this demo the windows delete themselves, we'll use the
// Component::SafePointer class to point to them, which automatically becomes
// null when the component that it points to is deleted.
Array<Component::SafePointer<Component>> windows;
SafePointer<DialogWindow> dialogWindow;
TextButton showWindowsButton { "Show Windows" },
closeWindowsButton { "Close Windows" };
void showAllWindows()
{
closeAllWindows();
showDocumentWindow (false);
showDocumentWindow (true);
showTransparentWindow();
showDialogWindow();
}
void closeAllWindows()
{
for (auto& window : windows)
window.deleteAndZero();
windows.clear();
}
void showDialogWindow()
{
String m;
m << "Dialog Windows can be used to quickly show a component, usually blocking mouse input to other windows." << newLine
<< newLine
<< "They can also be quickly closed with the escape key, try it now.";
DialogWindow::LaunchOptions options;
auto* label = new Label();
label->setText (m, dontSendNotification);
label->setColour (Label::textColourId, Colours::whitesmoke);
options.content.setOwned (label);
Rectangle<int> area (0, 0, 300, 200);
options.content->setSize (area.getWidth(), area.getHeight());
options.dialogTitle = "Dialog Window";
options.dialogBackgroundColour = Colour (0xff0e345a);
options.escapeKeyTriggersCloseButton = true;
options.useNativeTitleBar = false;
options.resizable = true;
dialogWindow = options.launchAsync();
if (dialogWindow != nullptr)
dialogWindow->centreWithSize (300, 200);
}
void showDocumentWindow (bool native)
{
auto* dw = new ColourSelectorWindow ("Document Window", getRandomBrightColour(), DocumentWindow::allButtons);
windows.add (dw);
Rectangle<int> area (0, 0, 300, 400);
RectanglePlacement placement ((native ? RectanglePlacement::xLeft
: RectanglePlacement::xRight)
| RectanglePlacement::yTop
| RectanglePlacement::doNotResize);
auto result = placement.appliedTo (area, Desktop::getInstance().getDisplays()
.getPrimaryDisplay()->userArea.reduced (20));
dw->setBounds (result);
dw->setResizable (true, ! native);
dw->setUsingNativeTitleBar (native);
dw->setVisible (true);
}
void showTransparentWindow()
{
auto* balls = new BouncingBallsContainer (3);
balls->addToDesktop (ComponentPeer::windowIsTemporary);
windows.add (balls);
Rectangle<int> area (0, 0, 200, 200);
RectanglePlacement placement (RectanglePlacement::xLeft
| RectanglePlacement::yBottom
| RectanglePlacement::doNotResize);
auto result = placement.appliedTo (area, Desktop::getInstance().getDisplays()
.getPrimaryDisplay()->userArea.reduced (20));
balls->setBounds (result);
balls->setVisible (true);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsDemo)
};