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:
1477
deps/juce/examples/GUI/AccessibilityDemo.h
vendored
Normal file
1477
deps/juce/examples/GUI/AccessibilityDemo.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
112
deps/juce/examples/GUI/AnimationAppDemo.h
vendored
Normal file
112
deps/juce/examples/GUI/AnimationAppDemo.h
vendored
Normal 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
301
deps/juce/examples/GUI/AnimationDemo.h
vendored
Normal 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)
|
||||
};
|
274
deps/juce/examples/GUI/BouncingBallWavetableDemo.h
vendored
Normal file
274
deps/juce/examples/GUI/BouncingBallWavetableDemo.h
vendored
Normal 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
24
deps/juce/examples/GUI/CMakeLists.txt
vendored
Normal 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
391
deps/juce/examples/GUI/CameraDemo.h
vendored
Normal 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
201
deps/juce/examples/GUI/CodeEditorDemo.h
vendored
Normal 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
162
deps/juce/examples/GUI/ComponentDemo.h
vendored
Normal 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)
|
||||
};
|
161
deps/juce/examples/GUI/ComponentTransformsDemo.h
vendored
Normal file
161
deps/juce/examples/GUI/ComponentTransformsDemo.h
vendored
Normal 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
519
deps/juce/examples/GUI/DialogsDemo.h
vendored
Normal 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
320
deps/juce/examples/GUI/FlexBoxDemo.h
vendored
Normal 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
352
deps/juce/examples/GUI/FontsDemo.h
vendored
Normal 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
736
deps/juce/examples/GUI/GraphicsDemo.h
vendored
Normal 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
141
deps/juce/examples/GUI/GridDemo.h
vendored
Normal 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
108
deps/juce/examples/GUI/HelloWorldDemo.h
vendored
Normal 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
139
deps/juce/examples/GUI/ImagesDemo.h
vendored
Normal 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
271
deps/juce/examples/GUI/KeyMappingsDemo.h
vendored
Normal 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
631
deps/juce/examples/GUI/LookAndFeelDemo.h
vendored
Normal 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
271
deps/juce/examples/GUI/MDIDemo.h
vendored
Normal 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
523
deps/juce/examples/GUI/MenusDemo.h
vendored
Normal 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
182
deps/juce/examples/GUI/MultiTouchDemo.h
vendored
Normal 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
437
deps/juce/examples/GUI/OpenGLAppDemo.h
vendored
Normal 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
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
261
deps/juce/examples/GUI/OpenGLDemo2D.h
vendored
Normal 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
228
deps/juce/examples/GUI/PropertiesDemo.h
vendored
Normal 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
708
deps/juce/examples/GUI/VideoDemo.h
vendored
Normal 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
150
deps/juce/examples/GUI/WebBrowserDemo.h
vendored
Normal 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
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
365
deps/juce/examples/GUI/WindowsDemo.h
vendored
Normal 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)
|
||||
};
|
Reference in New Issue
Block a user