migrating to the latest JUCE version
This commit is contained in:
2959
deps/juce/examples/GUI/AccessibilityDemo.h
vendored
2959
deps/juce/examples/GUI/AccessibilityDemo.h
vendored
File diff suppressed because it is too large
Load Diff
224
deps/juce/examples/GUI/AnimationAppDemo.h
vendored
224
deps/juce/examples/GUI/AnimationAppDemo.h
vendored
@ -1,112 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
602
deps/juce/examples/GUI/AnimationDemo.h
vendored
602
deps/juce/examples/GUI/AnimationDemo.h
vendored
@ -1,301 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
548
deps/juce/examples/GUI/BouncingBallWavetableDemo.h
vendored
548
deps/juce/examples/GUI/BouncingBallWavetableDemo.h
vendored
@ -1,274 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
8
deps/juce/examples/GUI/CMakeLists.txt
vendored
8
deps/juce/examples/GUI/CMakeLists.txt
vendored
@ -1,15 +1,15 @@
|
||||
# ==============================================================================
|
||||
#
|
||||
# This file is part of the JUCE library.
|
||||
# Copyright (c) 2020 - Raw Material Software Limited
|
||||
# Copyright (c) 2022 - Raw Material Software Limited
|
||||
#
|
||||
# JUCE is an open source library subject to commercial or open-source
|
||||
# licensing.
|
||||
#
|
||||
# By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
# Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
# By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
# Agreement and JUCE Privacy Policy.
|
||||
#
|
||||
# End User License Agreement: www.juce.com/juce-6-licence
|
||||
# End User License Agreement: www.juce.com/juce-7-licence
|
||||
# Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
#
|
||||
# Or: You may also use this code under the terms of the GPL v3 (see
|
||||
|
782
deps/juce/examples/GUI/CameraDemo.h
vendored
782
deps/juce/examples/GUI/CameraDemo.h
vendored
@ -1,391 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
402
deps/juce/examples/GUI/CodeEditorDemo.h
vendored
402
deps/juce/examples/GUI/CodeEditorDemo.h
vendored
@ -1,201 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
324
deps/juce/examples/GUI/ComponentDemo.h
vendored
324
deps/juce/examples/GUI/ComponentDemo.h
vendored
@ -1,162 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022
|
||||
|
||||
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)
|
||||
};
|
||||
|
322
deps/juce/examples/GUI/ComponentTransformsDemo.h
vendored
322
deps/juce/examples/GUI/ComponentTransformsDemo.h
vendored
@ -1,161 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
1038
deps/juce/examples/GUI/DialogsDemo.h
vendored
1038
deps/juce/examples/GUI/DialogsDemo.h
vendored
File diff suppressed because it is too large
Load Diff
640
deps/juce/examples/GUI/FlexBoxDemo.h
vendored
640
deps/juce/examples/GUI/FlexBoxDemo.h
vendored
@ -1,320 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
704
deps/juce/examples/GUI/FontsDemo.h
vendored
704
deps/juce/examples/GUI/FontsDemo.h
vendored
@ -1,352 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
1472
deps/juce/examples/GUI/GraphicsDemo.h
vendored
1472
deps/juce/examples/GUI/GraphicsDemo.h
vendored
File diff suppressed because it is too large
Load Diff
282
deps/juce/examples/GUI/GridDemo.h
vendored
282
deps/juce/examples/GUI/GridDemo.h
vendored
@ -1,141 +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;
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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;
|
||||
};
|
||||
|
216
deps/juce/examples/GUI/HelloWorldDemo.h
vendored
216
deps/juce/examples/GUI/HelloWorldDemo.h
vendored
@ -1,108 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
278
deps/juce/examples/GUI/ImagesDemo.h
vendored
278
deps/juce/examples/GUI/ImagesDemo.h
vendored
@ -1,139 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
542
deps/juce/examples/GUI/KeyMappingsDemo.h
vendored
542
deps/juce/examples/GUI/KeyMappingsDemo.h
vendored
@ -1,271 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
1262
deps/juce/examples/GUI/LookAndFeelDemo.h
vendored
1262
deps/juce/examples/GUI/LookAndFeelDemo.h
vendored
File diff suppressed because it is too large
Load Diff
573
deps/juce/examples/GUI/MDIDemo.h
vendored
573
deps/juce/examples/GUI/MDIDemo.h
vendored
@ -1,271 +1,302 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void activeDocumentChanged() override
|
||||
{
|
||||
if (auto* activeDoc = getActiveDocument())
|
||||
Logger::outputDebugString ("activeDocumentChanged() to " + activeDoc->getName());
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
oneDocShouldBeFullscreenButton.onClick = [this]
|
||||
{
|
||||
multiDocumentPanel.useFullscreenWhenOneDocument (oneDocShouldBeFullscreenButton.getToggleState());
|
||||
};
|
||||
addAndMakeVisible (oneDocShouldBeFullscreenButton);
|
||||
oneDocShouldBeFullscreenButton.setToggleState (false, juce::sendNotification);
|
||||
|
||||
addNoteButton.onClick = [this]
|
||||
{
|
||||
addNote ("Note " + String (noteCounter), "Hello World! " + String (noteCounter));
|
||||
++noteCounter;
|
||||
};
|
||||
addAndMakeVisible (addNoteButton);
|
||||
|
||||
closeActiveDocumentButton.onClick = [this]
|
||||
{
|
||||
multiDocumentPanel.closeDocumentAsync (multiDocumentPanel.getActiveDocument(), false, [] (auto) {});
|
||||
};
|
||||
addAndMakeVisible (closeActiveDocumentButton);
|
||||
|
||||
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 (650, 500);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto area = getLocalBounds();
|
||||
|
||||
auto topButtonRow = area.removeFromTop (28).reduced (2);
|
||||
|
||||
showInTabsButton .setBounds (topButtonRow.removeFromLeft (150));
|
||||
|
||||
closeApplicationButton .setBounds (topButtonRow.removeFromRight (150));
|
||||
addNoteButton .setBounds (topButtonRow.removeFromRight (150));
|
||||
closeActiveDocumentButton .setBounds (topButtonRow.removeFromRight (150));
|
||||
|
||||
oneDocShouldBeFullscreenButton.setBounds (area.removeFromTop (28).reduced (2).removeFromLeft (240));
|
||||
|
||||
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" };
|
||||
ToggleButton oneDocShouldBeFullscreenButton { "Fill screen when only one note is open" };
|
||||
TextButton addNoteButton { "Create a new note" },
|
||||
closeApplicationButton { "Close app" },
|
||||
closeActiveDocumentButton { "Close active document" };
|
||||
|
||||
DemoMultiDocumentPanel multiDocumentPanel;
|
||||
int noteCounter = 1;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MDIDemo)
|
||||
};
|
||||
|
1046
deps/juce/examples/GUI/MenusDemo.h
vendored
1046
deps/juce/examples/GUI/MenusDemo.h
vendored
File diff suppressed because it is too large
Load Diff
364
deps/juce/examples/GUI/MultiTouchDemo.h
vendored
364
deps/juce/examples/GUI/MultiTouchDemo.h
vendored
@ -1,182 +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;
|
||||
}
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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;
|
||||
}
|
||||
};
|
||||
|
874
deps/juce/examples/GUI/OpenGLAppDemo.h
vendored
874
deps/juce/examples/GUI/OpenGLAppDemo.h
vendored
@ -1,437 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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
|
||||
{
|
||||
auto viewMatrix = Matrix3D<float>::fromTranslation ({ 0.0f, 0.0f, -10.0f });
|
||||
auto rotationMatrix = viewMatrix.rotation ({ -0.3f, 5.0f * std::sin ((float) getFrameCounter() * 0.01f), 0.0f });
|
||||
|
||||
return viewMatrix * rotationMatrix;
|
||||
}
|
||||
|
||||
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)
|
||||
};
|
||||
|
2595
deps/juce/examples/GUI/OpenGLDemo.h
vendored
2595
deps/juce/examples/GUI/OpenGLDemo.h
vendored
File diff suppressed because it is too large
Load Diff
522
deps/juce/examples/GUI/OpenGLDemo2D.h
vendored
522
deps/juce/examples/GUI/OpenGLDemo2D.h
vendored
@ -1,261 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
456
deps/juce/examples/GUI/PropertiesDemo.h
vendored
456
deps/juce/examples/GUI/PropertiesDemo.h
vendored
@ -1,228 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
1416
deps/juce/examples/GUI/VideoDemo.h
vendored
1416
deps/juce/examples/GUI/VideoDemo.h
vendored
File diff suppressed because it is too large
Load Diff
300
deps/juce/examples/GUI/WebBrowserDemo.h
vendored
300
deps/juce/examples/GUI/WebBrowserDemo.h
vendored
@ -1,150 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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)
|
||||
};
|
||||
|
3155
deps/juce/examples/GUI/WidgetsDemo.h
vendored
3155
deps/juce/examples/GUI/WidgetsDemo.h
vendored
File diff suppressed because it is too large
Load Diff
730
deps/juce/examples/GUI/WindowsDemo.h
vendored
730
deps/juce/examples/GUI/WindowsDemo.h
vendored
@ -1,365 +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)
|
||||
};
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2022 - 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, vs2022, 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