From d6fb474c3900fc7a3090979948e6306c02033dd2 Mon Sep 17 00:00:00 2001 From: ed Date: Fri, 29 Mar 2019 16:03:58 +0000 Subject: [PATCH] Restructure OpenGLDemo and UnitTestsDemo and remove unused macro from DemoRunner --- .../DemoRunner/Source/Demos/DemoPIPs1.cpp | 72 +- .../DemoRunner/Source/Demos/DemoPIPs2.cpp | 52 +- examples/DemoRunner/Source/Demos/JUCEDemos.h | 1 - examples/GUI/OpenGLDemo.h | 1636 ++++++++--------- examples/Utilities/UnitTestsDemo.h | 231 ++- 5 files changed, 990 insertions(+), 1002 deletions(-) diff --git a/examples/DemoRunner/Source/Demos/DemoPIPs1.cpp b/examples/DemoRunner/Source/Demos/DemoPIPs1.cpp index dc4577d411..9fe417f9ea 100644 --- a/examples/DemoRunner/Source/Demos/DemoPIPs1.cpp +++ b/examples/DemoRunner/Source/Demos/DemoPIPs1.cpp @@ -80,50 +80,50 @@ void registerDemos_One() noexcept { - REGISTER_DEMO (AudioAppDemo, Audio, false) - REGISTER_DEMO (AudioLatencyDemo, Audio, false) - REGISTER_DEMO (AudioPlaybackDemo, Audio, false) - REGISTER_DEMO (AudioRecordingDemo, Audio, false) - REGISTER_DEMO (AudioSettingsDemo, Audio, false) - REGISTER_DEMO (AudioSynthesiserDemo, Audio, false) - REGISTER_DEMO (MidiDemo, Audio, false) - REGISTER_DEMO (MPEDemo, Audio, false) - REGISTER_DEMO (PluckedStringsDemo, Audio, false) + REGISTER_DEMO (AudioAppDemo, Audio, false) + REGISTER_DEMO (AudioLatencyDemo, Audio, false) + REGISTER_DEMO (AudioPlaybackDemo, Audio, false) + REGISTER_DEMO (AudioRecordingDemo, Audio, false) + REGISTER_DEMO (AudioSettingsDemo, Audio, false) + REGISTER_DEMO (AudioSynthesiserDemo, Audio, false) + REGISTER_DEMO (MidiDemo, Audio, false) + REGISTER_DEMO (MPEDemo, Audio, false) + REGISTER_DEMO (PluckedStringsDemo, Audio, false) #if JUCE_HAS_CONSTEXPR - REGISTER_DEMO (SimpleFFTDemo, Audio, false) - REGISTER_DEMO (BlocksDrawingDemo, BLOCKS, false) - REGISTER_DEMO (BlocksMonitorDemo, BLOCKS, false) - REGISTER_DEMO (BlocksSynthDemo, BLOCKS, false) - - REGISTER_DEMO (ConvolutionDemo, DSP, false) - REGISTER_DEMO (FIRFilterDemo, DSP, false) - REGISTER_DEMO (GainDemo, DSP, false) - REGISTER_DEMO (IIRFilterDemo, DSP, false) - REGISTER_DEMO (OscillatorDemo, DSP, false) - REGISTER_DEMO (OverdriveDemo, DSP, false) + REGISTER_DEMO (SimpleFFTDemo, Audio, false) + REGISTER_DEMO (BlocksDrawingDemo, BLOCKS, false) + REGISTER_DEMO (BlocksMonitorDemo, BLOCKS, false) + REGISTER_DEMO (BlocksSynthDemo, BLOCKS, false) + + REGISTER_DEMO (ConvolutionDemo, DSP, false) + REGISTER_DEMO (FIRFilterDemo, DSP, false) + REGISTER_DEMO (GainDemo, DSP, false) + REGISTER_DEMO (IIRFilterDemo, DSP, false) + REGISTER_DEMO (OscillatorDemo, DSP, false) + REGISTER_DEMO (OverdriveDemo, DSP, false) #if JUCE_USE_SIMD - REGISTER_DEMO (SIMDRegisterDemo, DSP, false) + REGISTER_DEMO (SIMDRegisterDemo, DSP, false) #endif - REGISTER_DEMO (StateVariableFilterDemo, DSP, false) - REGISTER_DEMO (WaveShaperTanhDemo, DSP, false) + REGISTER_DEMO (StateVariableFilterDemo, DSP, false) + REGISTER_DEMO (WaveShaperTanhDemo, DSP, false) #endif - REGISTER_DEMO (Box2DDemo, Utilities, false) + REGISTER_DEMO (Box2DDemo, Utilities, false) #if JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX - REGISTER_DEMO (ChildProcessDemo, Utilities, false) + REGISTER_DEMO (ChildProcessDemo, Utilities, false) #endif - REGISTER_DEMO (CryptographyDemo, Utilities, false) - REGISTER_DEMO (JavaScriptDemo, Utilities, false) - REGISTER_DEMO (LiveConstantDemo, Utilities, false) - REGISTER_DEMO (MultithreadingDemo, Utilities, false) - REGISTER_DEMO (NetworkingDemo, Utilities, false) - REGISTER_DEMO (OSCDemo, Utilities, false) - REGISTER_DEMO (SystemInfoDemo, Utilities, false) - REGISTER_DEMO (TimersAndEventsDemo, Utilities, false) - REGISTER_DEMO_WITH_FILENAME (UnitTestClasses::UnitTestsDemo, Utilities, UnitTestsDemo, false) - REGISTER_DEMO (ValueTreesDemo, Utilities, false) - REGISTER_DEMO (XMLandJSONDemo, Utilities, false) + REGISTER_DEMO (CryptographyDemo, Utilities, false) + REGISTER_DEMO (JavaScriptDemo, Utilities, false) + REGISTER_DEMO (LiveConstantDemo, Utilities, false) + REGISTER_DEMO (MultithreadingDemo, Utilities, false) + REGISTER_DEMO (NetworkingDemo, Utilities, false) + REGISTER_DEMO (OSCDemo, Utilities, false) + REGISTER_DEMO (SystemInfoDemo, Utilities, false) + REGISTER_DEMO (TimersAndEventsDemo, Utilities, false) + REGISTER_DEMO (UnitTestsDemo, Utilities, false) + REGISTER_DEMO (ValueTreesDemo, Utilities, false) + REGISTER_DEMO (XMLandJSONDemo, Utilities, false) } Component* createIntroDemo() diff --git a/examples/DemoRunner/Source/Demos/DemoPIPs2.cpp b/examples/DemoRunner/Source/Demos/DemoPIPs2.cpp index 51074e1301..b7d95de83a 100644 --- a/examples/DemoRunner/Source/Demos/DemoPIPs2.cpp +++ b/examples/DemoRunner/Source/Demos/DemoPIPs2.cpp @@ -70,42 +70,42 @@ void registerDemos_Two() noexcept { - REGISTER_DEMO (AnimationAppDemo, GUI, false) - REGISTER_DEMO (AnimationDemo, GUI, false) - REGISTER_DEMO (BouncingBallWavetableDemo, GUI, false) + REGISTER_DEMO (AnimationAppDemo, GUI, false) + REGISTER_DEMO (AnimationDemo, GUI, false) + REGISTER_DEMO (BouncingBallWavetableDemo, GUI, false) #if JUCE_USE_CAMERA && ! JUCE_LINUX - REGISTER_DEMO (CameraDemo, GUI, true) + REGISTER_DEMO (CameraDemo, GUI, true) #endif #if ! JUCE_ANDROID - REGISTER_DEMO (CodeEditorDemo, GUI, false) + REGISTER_DEMO (CodeEditorDemo, GUI, false) #endif - REGISTER_DEMO (ComponentDemo, GUI, false) - REGISTER_DEMO (ComponentTransformsDemo, GUI, false) - REGISTER_DEMO (DialogsDemo, GUI, false) - REGISTER_DEMO (FlexBoxDemo, GUI, false) - REGISTER_DEMO (FontsDemo, GUI, false) - REGISTER_DEMO (GraphicsDemo, GUI, false) + REGISTER_DEMO (ComponentDemo, GUI, false) + REGISTER_DEMO (ComponentTransformsDemo, GUI, false) + REGISTER_DEMO (DialogsDemo, GUI, false) + REGISTER_DEMO (FlexBoxDemo, GUI, false) + REGISTER_DEMO (FontsDemo, GUI, false) + REGISTER_DEMO (GraphicsDemo, GUI, false) #if JUCE_HAS_CONSTEXPR - REGISTER_DEMO (GridDemo, GUI, false) + REGISTER_DEMO (GridDemo, GUI, false) #endif - REGISTER_DEMO (ImagesDemo, GUI, false) - REGISTER_DEMO (KeyMappingsDemo, GUI, false) - REGISTER_DEMO (LookAndFeelDemo, GUI, false) - REGISTER_DEMO (MDIDemo, GUI, false) - REGISTER_DEMO (MenusDemo, GUI, false) - REGISTER_DEMO (MultiTouchDemo, GUI, false) + REGISTER_DEMO (ImagesDemo, GUI, false) + REGISTER_DEMO (KeyMappingsDemo, GUI, false) + REGISTER_DEMO (LookAndFeelDemo, GUI, false) + REGISTER_DEMO (MDIDemo, GUI, false) + REGISTER_DEMO (MenusDemo, GUI, false) + REGISTER_DEMO (MultiTouchDemo, GUI, false) #if JUCE_OPENGL - REGISTER_DEMO (OpenGLAppDemo, GUI, true) - REGISTER_DEMO (OpenGLDemo2D, GUI, true) - REGISTER_DEMO_WITH_FILENAME (OpenGLDemoClasses::OpenGLDemo, GUI, OpenGLDemo, true) + REGISTER_DEMO (OpenGLAppDemo, GUI, true) + REGISTER_DEMO (OpenGLDemo2D, GUI, true) + REGISTER_DEMO (OpenGLDemo, GUI, true) #endif - REGISTER_DEMO (PropertiesDemo, GUI, false) + REGISTER_DEMO (PropertiesDemo, GUI, false) #if ! JUCE_LINUX - REGISTER_DEMO (VideoDemo, GUI, true) + REGISTER_DEMO (VideoDemo, GUI, true) #endif - REGISTER_DEMO (WebBrowserDemo, GUI, true) - REGISTER_DEMO (WidgetsDemo, GUI, false) - REGISTER_DEMO (WindowsDemo, GUI, false) + REGISTER_DEMO (WebBrowserDemo, GUI, true) + REGISTER_DEMO (WidgetsDemo, GUI, false) + REGISTER_DEMO (WindowsDemo, GUI, false) } CodeEditorComponent::ColourScheme getDarkColourScheme() diff --git a/examples/DemoRunner/Source/Demos/JUCEDemos.h b/examples/DemoRunner/Source/Demos/JUCEDemos.h index 53db349e2f..a7bc07c187 100644 --- a/examples/DemoRunner/Source/Demos/JUCEDemos.h +++ b/examples/DemoRunner/Source/Demos/JUCEDemos.h @@ -34,7 +34,6 @@ #define CREATE_FILEPATH(DemoName, category) JUCE_STRINGIFY(EXPAND(category)/EXPAND(DemoName)EXPAND(FILE_EXT)) #define REGISTER_DEMO(DemoName, category, heavyweight) JUCEDemos::registerDemo ([] { return new DemoName(); }, CREATE_FILEPATH(DemoName, category), JUCE_STRINGIFY (category), heavyweight); -#define REGISTER_DEMO_WITH_FILENAME(DemoName, category, fileName, heavyweight) JUCEDemos::registerDemo ([] { return new DemoName(); }, CREATE_FILEPATH(fileName, category), JUCE_STRINGIFY (category), heavyweight); //============================================================================== struct JUCEDemos diff --git a/examples/GUI/OpenGLDemo.h b/examples/GUI/OpenGLDemo.h index d8f3bf5855..04f9f9c79d 100644 --- a/examples/GUI/OpenGLDemo.h +++ b/examples/GUI/OpenGLDemo.h @@ -36,7 +36,7 @@ moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1 type: Component - mainClass: OpenGLDemoClasses::OpenGLDemo + mainClass: OpenGLDemo useLocalCopy: 1 @@ -49,9 +49,9 @@ #include "../Assets/DemoUtilities.h" #include "../Assets/WavefrontObjParser.h" -//============================================================================== -struct OpenGLDemoClasses +struct OpenGLUtils { + //============================================================================== /** Vertex data to be passed to the shaders. For the purposes of this demo, each vertex will have a 3D position, a colour and a 2D texture co-ordinate. Of course you can ignore these or manipulate them in the @@ -247,1011 +247,1009 @@ struct OpenGLDemoClasses }; //============================================================================== - // These classes are used to load textures from the various sources that the demo uses.. - struct DemoTexture + struct ShaderPreset { - virtual ~DemoTexture() {} - virtual bool applyTo (OpenGLTexture&) = 0; - - String name; + const char* name; + const char* vertexShader; + const char* fragmentShader; }; - struct DynamicTexture : public DemoTexture + static Array getPresets() { - DynamicTexture() { name = "Dynamically-generated texture"; } - - Image image; - BouncingNumber x, y; + #define SHADER_DEMO_HEADER \ + "/* This is a live OpenGL Shader demo.\n" \ + " Edit the shader program below and it will be \n" \ + " compiled and applied to the model above!\n" \ + "*/\n\n" - bool applyTo (OpenGLTexture& texture) override + ShaderPreset presets[] = { - int size = 128; - - if (! image.isValid()) - image = Image (Image::ARGB, size, size, true); - { - Graphics g (image); - g.fillAll (Colours::lightcyan); - - g.setColour (Colours::darkred); - g.drawRect (0, 0, size, size, 2); - - g.setColour (Colours::green); - g.fillEllipse (x.getValue() * size * 0.9f, y.getValue() * size * 0.9f, size * 0.1f, size * 0.1f); + "Texture + Lighting", - g.setColour (Colours::black); - g.setFont (40); + SHADER_DEMO_HEADER + "attribute vec4 position;\n" + "attribute vec4 normal;\n" + "attribute vec4 sourceColour;\n" + "attribute vec2 textureCoordIn;\n" + "\n" + "uniform mat4 projectionMatrix;\n" + "uniform mat4 viewMatrix;\n" + "uniform vec4 lightPosition;\n" + "\n" + "varying vec4 destinationColour;\n" + "varying vec2 textureCoordOut;\n" + "varying float lightIntensity;\n" + "\n" + "void main()\n" + "{\n" + " destinationColour = sourceColour;\n" + " textureCoordOut = textureCoordIn;\n" + "\n" + " vec4 light = viewMatrix * lightPosition;\n" + " lightIntensity = dot (light, normal);\n" + "\n" + " gl_Position = projectionMatrix * viewMatrix * position;\n" + "}\n", - const MessageManagerLock mml (ThreadPoolJob::getCurrentThreadPoolJob()); - if (! mml.lockWasGained()) - return false; + SHADER_DEMO_HEADER + #if JUCE_OPENGL_ES + "varying lowp vec4 destinationColour;\n" + "varying lowp vec2 textureCoordOut;\n" + "varying highp float lightIntensity;\n" + #else + "varying vec4 destinationColour;\n" + "varying vec2 textureCoordOut;\n" + "varying float lightIntensity;\n" + #endif + "\n" + "uniform sampler2D demoTexture;\n" + "\n" + "void main()\n" + "{\n" + #if JUCE_OPENGL_ES + " highp float l = max (0.3, lightIntensity * 0.3);\n" + " highp vec4 colour = vec4 (l, l, l, 1.0);\n" + #else + " float l = max (0.3, lightIntensity * 0.3);\n" + " vec4 colour = vec4 (l, l, l, 1.0);\n" + #endif + " gl_FragColor = colour * texture2D (demoTexture, textureCoordOut);\n" + "}\n" + }, - g.drawFittedText (String (Time::getCurrentTime().getMilliseconds()), image.getBounds(), Justification::centred, 1); - } + { + "Textured", - texture.loadImage (image); - return true; - } - }; + SHADER_DEMO_HEADER + "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", - struct BuiltInTexture : public DemoTexture - { - BuiltInTexture (const char* nm, const void* imageData, size_t imageSize) - : image (resizeImageToPowerOfTwo (ImageFileFormat::loadFrom (imageData, imageSize))) - { - name = nm; - } + SHADER_DEMO_HEADER + #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" + "uniform sampler2D demoTexture;\n" + "\n" + "void main()\n" + "{\n" + " gl_FragColor = texture2D (demoTexture, textureCoordOut);\n" + "}\n" + }, - Image image; + { + "Flat Colour", - bool applyTo (OpenGLTexture& texture) override - { - texture.loadImage (image); - return false; - } - }; + SHADER_DEMO_HEADER + "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", - struct TextureFromFile : public DemoTexture - { - TextureFromFile (const File& file) - { - name = file.getFileName(); - image = resizeImageToPowerOfTwo (ImageFileFormat::loadFrom (file)); - } + SHADER_DEMO_HEADER + #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" + " gl_FragColor = destinationColour;\n" + "}\n" + }, - Image image; + { + "Rainbow", - bool applyTo (OpenGLTexture& texture) override - { - texture.loadImage (image); - return false; - } - }; + SHADER_DEMO_HEADER + "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" + "varying float xPos;\n" + "varying float yPos;\n" + "varying float zPos;\n" + "\n" + "void main()\n" + "{\n" + " vec4 v = vec4 (position);\n" + " xPos = clamp (v.x, 0.0, 1.0);\n" + " yPos = clamp (v.y, 0.0, 1.0);\n" + " zPos = clamp (v.z, 0.0, 1.0);\n" + " gl_Position = projectionMatrix * viewMatrix * position;\n" + "}", - struct TextureFromAsset : public DemoTexture - { - TextureFromAsset (const char* assetName) - { - name = assetName; - image = resizeImageToPowerOfTwo (getImageFromAssets (assetName)); - } + SHADER_DEMO_HEADER + #if JUCE_OPENGL_ES + "varying lowp vec4 destinationColour;\n" + "varying lowp vec2 textureCoordOut;\n" + "varying lowp float xPos;\n" + "varying lowp float yPos;\n" + "varying lowp float zPos;\n" + #else + "varying vec4 destinationColour;\n" + "varying vec2 textureCoordOut;\n" + "varying float xPos;\n" + "varying float yPos;\n" + "varying float zPos;\n" + #endif + "\n" + "void main()\n" + "{\n" + " gl_FragColor = vec4 (xPos, yPos, zPos, 1.0);\n" + "}" + }, - Image image; + { + "Changing Colour", - bool applyTo (OpenGLTexture& texture) override - { - texture.loadImage (image); - return false; - } - }; + SHADER_DEMO_HEADER + "attribute vec4 position;\n" + "attribute vec2 textureCoordIn;\n" + "\n" + "uniform mat4 projectionMatrix;\n" + "uniform mat4 viewMatrix;\n" + "\n" + "varying vec2 textureCoordOut;\n" + "\n" + "void main()\n" + "{\n" + " textureCoordOut = textureCoordIn;\n" + " gl_Position = projectionMatrix * viewMatrix * position;\n" + "}\n", - static Image resizeImageToPowerOfTwo (Image image) - { - if (! (isPowerOfTwo (image.getWidth()) && isPowerOfTwo (image.getHeight()))) - return image.rescaled (jmin (1024, nextPowerOfTwo (image.getWidth())), - jmin (1024, nextPowerOfTwo (image.getHeight()))); + SHADER_DEMO_HEADER + "#define PI 3.1415926535897932384626433832795\n" + "\n" + #if JUCE_OPENGL_ES + "precision mediump float;\n" + "varying lowp vec2 textureCoordOut;\n" + #else + "varying vec2 textureCoordOut;\n" + #endif + "uniform float bouncingNumber;\n" + "\n" + "void main()\n" + "{\n" + " float b = bouncingNumber;\n" + " float n = b * PI * 2.0;\n" + " float sn = (sin (n * textureCoordOut.x) * 0.5) + 0.5;\n" + " float cn = (sin (n * textureCoordOut.y) * 0.5) + 0.5;\n" + "\n" + " vec4 col = vec4 (b, sn, cn, 1.0);\n" + " gl_FragColor = col;\n" + "}\n" + }, - return image; - } + { + "Simple Light", - class OpenGLDemo; + SHADER_DEMO_HEADER + "attribute vec4 position;\n" + "attribute vec4 normal;\n" + "\n" + "uniform mat4 projectionMatrix;\n" + "uniform mat4 viewMatrix;\n" + "uniform vec4 lightPosition;\n" + "\n" + "varying float lightIntensity;\n" + "\n" + "void main()\n" + "{\n" + " vec4 light = viewMatrix * lightPosition;\n" + " lightIntensity = dot (light, normal);\n" + "\n" + " gl_Position = projectionMatrix * viewMatrix * position;\n" + "}\n", - //============================================================================== - /** - This component sits on top of the main GL demo, and contains all the sliders - and widgets that control things. - */ - class DemoControlsOverlay : public Component, - private CodeDocument::Listener, - private Slider::Listener, - private Timer - { - public: - DemoControlsOverlay (OpenGLDemo& d) - : demo (d) - { - addAndMakeVisible (statusLabel); - statusLabel.setJustificationType (Justification::topLeft); - statusLabel.setFont (Font (14.0f)); + SHADER_DEMO_HEADER + #if JUCE_OPENGL_ES + "varying highp float lightIntensity;\n" + #else + "varying float lightIntensity;\n" + #endif + "\n" + "void main()\n" + "{\n" + #if JUCE_OPENGL_ES + " highp float l = lightIntensity * 0.25;\n" + " highp vec4 colour = vec4 (l, l, l, 1.0);\n" + #else + " float l = lightIntensity * 0.25;\n" + " vec4 colour = vec4 (l, l, l, 1.0);\n" + #endif + "\n" + " gl_FragColor = colour;\n" + "}\n" + }, - addAndMakeVisible (sizeSlider); - sizeSlider.setRange (0.0, 1.0, 0.001); - sizeSlider.addListener (this); + { + "Flattened", - addAndMakeVisible (zoomLabel); - zoomLabel.attachToComponent (&sizeSlider, true); + SHADER_DEMO_HEADER + "attribute vec4 position;\n" + "attribute vec4 normal;\n" + "\n" + "uniform mat4 projectionMatrix;\n" + "uniform mat4 viewMatrix;\n" + "uniform vec4 lightPosition;\n" + "\n" + "varying float lightIntensity;\n" + "\n" + "void main()\n" + "{\n" + " vec4 light = viewMatrix * lightPosition;\n" + " lightIntensity = dot (light, normal);\n" + "\n" + " vec4 v = vec4 (position);\n" + " v.z = v.z * 0.1;\n" + "\n" + " gl_Position = projectionMatrix * viewMatrix * v;\n" + "}\n", - addAndMakeVisible (speedSlider); - speedSlider.setRange (0.0, 0.5, 0.001); - speedSlider.addListener (this); - speedSlider.setSkewFactor (0.5f); + SHADER_DEMO_HEADER + #if JUCE_OPENGL_ES + "varying highp float lightIntensity;\n" + #else + "varying float lightIntensity;\n" + #endif + "\n" + "void main()\n" + "{\n" + #if JUCE_OPENGL_ES + " highp float l = lightIntensity * 0.25;\n" + " highp vec4 colour = vec4 (l, l, l, 1.0);\n" + #else + " float l = lightIntensity * 0.25;\n" + " vec4 colour = vec4 (l, l, l, 1.0);\n" + #endif + "\n" + " gl_FragColor = colour;\n" + "}\n" + }, - addAndMakeVisible (speedLabel); - speedLabel.attachToComponent (&speedSlider, true); + { + "Toon Shader", - addAndMakeVisible (showBackgroundToggle); - showBackgroundToggle.onClick = [this] { demo.doBackgroundDrawing = showBackgroundToggle.getToggleState(); }; + SHADER_DEMO_HEADER + "attribute vec4 position;\n" + "attribute vec4 normal;\n" + "\n" + "uniform mat4 projectionMatrix;\n" + "uniform mat4 viewMatrix;\n" + "uniform vec4 lightPosition;\n" + "\n" + "varying float lightIntensity;\n" + "\n" + "void main()\n" + "{\n" + " vec4 light = viewMatrix * lightPosition;\n" + " lightIntensity = dot (light, normal);\n" + "\n" + " gl_Position = projectionMatrix * viewMatrix * position;\n" + "}\n", - addAndMakeVisible (tabbedComp); - tabbedComp.setTabBarDepth (25); - tabbedComp.setColour (TabbedButtonBar::tabTextColourId, Colours::grey); - tabbedComp.addTab ("Vertex", Colours::transparentBlack, &vertexEditorComp, false); - tabbedComp.addTab ("Fragment", Colours::transparentBlack, &fragmentEditorComp, false); + SHADER_DEMO_HEADER + #if JUCE_OPENGL_ES + "varying highp float lightIntensity;\n" + #else + "varying float lightIntensity;\n" + #endif + "\n" + "void main()\n" + "{\n" + #if JUCE_OPENGL_ES + " highp float intensity = lightIntensity * 0.5;\n" + " highp vec4 colour;\n" + #else + " float intensity = lightIntensity * 0.5;\n" + " vec4 colour;\n" + #endif + "\n" + " if (intensity > 0.95)\n" + " colour = vec4 (1.0, 0.5, 0.5, 1.0);\n" + " else if (intensity > 0.5)\n" + " colour = vec4 (0.6, 0.3, 0.3, 1.0);\n" + " else if (intensity > 0.25)\n" + " colour = vec4 (0.4, 0.2, 0.2, 1.0);\n" + " else\n" + " colour = vec4 (0.2, 0.1, 0.1, 1.0);\n" + "\n" + " gl_FragColor = colour;\n" + "}\n" + } + }; - vertexDocument.addListener (this); - fragmentDocument.addListener (this); + return Array (presets, numElementsInArray (presets)); + } - textures.add (new TextureFromAsset ("portmeirion.jpg")); - textures.add (new TextureFromAsset ("tile_background.png")); - textures.add (new TextureFromAsset ("juce_icon.png")); - textures.add (new DynamicTexture()); + //============================================================================== + // These classes are used to load textures from the various sources that the demo uses.. + struct DemoTexture + { + virtual ~DemoTexture() {} + virtual bool applyTo (OpenGLTexture&) = 0; - addAndMakeVisible (textureBox); - textureBox.onChange = [this] { selectTexture (textureBox.getSelectedId()); }; - updateTexturesList(); + String name; + }; - addAndMakeVisible (presetBox); - presetBox.onChange = [this] { selectPreset (presetBox.getSelectedItemIndex()); }; + struct DynamicTexture : public DemoTexture + { + DynamicTexture() { name = "Dynamically-generated texture"; } - auto presets = getPresets(); + Image image; + BouncingNumber x, y; - for (int i = 0; i < presets.size(); ++i) - presetBox.addItem (presets[i].name, i + 1); + bool applyTo (OpenGLTexture& texture) override + { + int size = 128; - addAndMakeVisible (presetLabel); - presetLabel.attachToComponent (&presetBox, true); + if (! image.isValid()) + image = Image (Image::ARGB, size, size, true); - addAndMakeVisible (textureLabel); - textureLabel.attachToComponent (&textureBox, true); + { + Graphics g (image); + g.fillAll (Colours::lightcyan); - lookAndFeelChanged(); - } + g.setColour (Colours::darkred); + g.drawRect (0, 0, size, size, 2); - void initialise() - { - showBackgroundToggle.setToggleState (false, sendNotification); - textureBox.setSelectedItemIndex (0); - presetBox .setSelectedItemIndex (0); - speedSlider.setValue (0.01); - sizeSlider .setValue (0.5); - } + g.setColour (Colours::green); + g.fillEllipse (x.getValue() * size * 0.9f, y.getValue() * size * 0.9f, size * 0.1f, size * 0.1f); - void resized() override - { - auto area = getLocalBounds().reduced (4); + g.setColour (Colours::black); + g.setFont (40); - auto top = area.removeFromTop (75); + const MessageManagerLock mml (ThreadPoolJob::getCurrentThreadPoolJob()); + if (! mml.lockWasGained()) + return false; - auto sliders = top.removeFromRight (area.getWidth() / 2); - showBackgroundToggle.setBounds (sliders.removeFromBottom (25)); - speedSlider .setBounds (sliders.removeFromBottom (25)); - sizeSlider .setBounds (sliders.removeFromBottom (25)); + g.drawFittedText (String (Time::getCurrentTime().getMilliseconds()), image.getBounds(), Justification::centred, 1); + } - top.removeFromRight (70); - statusLabel.setBounds (top); + texture.loadImage (image); + return true; + } + }; - auto shaderArea = area.removeFromBottom (area.getHeight() / 2); + static Image resizeImageToPowerOfTwo (Image image) + { + if (! (isPowerOfTwo (image.getWidth()) && isPowerOfTwo (image.getHeight()))) + return image.rescaled (jmin (1024, nextPowerOfTwo (image.getWidth())), + jmin (1024, nextPowerOfTwo (image.getHeight()))); - auto presets = shaderArea.removeFromTop (25); - presets.removeFromLeft (100); - presetBox.setBounds (presets.removeFromLeft (150)); - presets.removeFromLeft (100); - textureBox.setBounds (presets); + return image; + } - shaderArea.removeFromTop (4); - tabbedComp.setBounds (shaderArea); + struct BuiltInTexture : public DemoTexture + { + BuiltInTexture (const char* nm, const void* imageData, size_t imageSize) + : image (resizeImageToPowerOfTwo (ImageFileFormat::loadFrom (imageData, imageSize))) + { + name = nm; } - void mouseDown (const MouseEvent& e) override + Image image; + + bool applyTo (OpenGLTexture& texture) override { - demo.draggableOrientation.mouseDown (e.getPosition()); + texture.loadImage (image); + return false; } + }; - void mouseDrag (const MouseEvent& e) override + struct TextureFromFile : public DemoTexture + { + TextureFromFile (const File& file) { - demo.draggableOrientation.mouseDrag (e.getPosition()); + name = file.getFileName(); + image = resizeImageToPowerOfTwo (ImageFileFormat::loadFrom (file)); } - void mouseWheelMove (const MouseEvent&, const MouseWheelDetails& d) override + Image image; + + bool applyTo (OpenGLTexture& texture) override { - sizeSlider.setValue (sizeSlider.getValue() + d.deltaY); + texture.loadImage (image); + return false; } + }; - void mouseMagnify (const MouseEvent&, float magnifyAmmount) override + struct TextureFromAsset : public DemoTexture + { + TextureFromAsset (const char* assetName) { - sizeSlider.setValue (sizeSlider.getValue() + magnifyAmmount - 1.0f); + name = assetName; + image = resizeImageToPowerOfTwo (getImageFromAssets (assetName)); } - void selectPreset (int preset) + Image image; + + bool applyTo (OpenGLTexture& texture) override { - const auto& p = getPresets()[preset]; + texture.loadImage (image); + return false; + } + }; +}; - vertexDocument .replaceAllContent (p.vertexShader); - fragmentDocument.replaceAllContent (p.fragmentShader); +//============================================================================== +/** This is the main demo component - the GL context gets attached to it, and + it implements the OpenGLRenderer callback so that it can do real GL work. +*/ +class OpenGLDemo : public Component, + private OpenGLRenderer, + private AsyncUpdater +{ +public: + OpenGLDemo() + { + if (auto* peer = getPeer()) + peer->setCurrentRenderingEngine (0); - startTimer (1); - } + setOpaque (true); + controlsOverlay.reset (new DemoControlsOverlay (*this)); + addAndMakeVisible (controlsOverlay.get()); - void selectTexture (int itemID) - { - #if JUCE_MODAL_LOOPS_PERMITTED - if (itemID == 1000) - { - auto lastLocation = File::getSpecialLocation (File::userPicturesDirectory); + openGLContext.setRenderer (this); + openGLContext.attachTo (*this); + openGLContext.setContinuousRepainting (true); - FileChooser fc ("Choose an image to open...", lastLocation, "*.jpg;*.jpeg;*.png;*.gif"); + controlsOverlay->initialise(); - if (fc.browseForFileToOpen()) - { - lastLocation = fc.getResult(); + setSize (500, 500); + } - textures.add (new TextureFromFile (fc.getResult())); - updateTexturesList(); + ~OpenGLDemo() + { + openGLContext.detach(); + } - textureBox.setSelectedId (textures.size()); - } - } - else - #endif - { - if (auto* t = textures[itemID - 1]) - demo.setTexture (t); - } - } + void newOpenGLContextCreated() override + { + // nothing to do in this case - we'll initialise our shaders + textures + // on demand, during the render callback. + freeAllContextObjects(); - void updateTexturesList() - { - textureBox.clear(); + if (controlsOverlay.get() != nullptr) + controlsOverlay->updateShader(); + } - for (int i = 0; i < textures.size(); ++i) - textureBox.addItem (textures.getUnchecked (i)->name, i + 1); + void openGLContextClosing() override + { + // When the context is about to close, you must use this callback to delete + // any GPU resources while the context is still current. + freeAllContextObjects(); - #if JUCE_MODAL_LOOPS_PERMITTED - textureBox.addSeparator(); - textureBox.addItem ("Load from a file...", 1000); - #endif - } + if (lastTexture != nullptr) + setTexture (lastTexture); + } - void updateShader() - { - startTimer (10); - } + void freeAllContextObjects() + { + shape .reset(); + shader .reset(); + attributes.reset(); + uniforms .reset(); + texture .release(); + } - Label statusLabel; + // This is a virtual method in OpenGLRenderer, and is called when it's time + // to do your GL rendering. + void renderOpenGL() override + { + jassert (OpenGLHelpers::isContextActive()); - private: - void sliderValueChanged (Slider*) override - { - demo.scale = (float) sizeSlider .getValue(); - demo.rotationSpeed = (float) speedSlider.getValue(); - } + auto desktopScale = (float) openGLContext.getRenderingScale(); - enum { shaderLinkDelay = 500 }; + OpenGLHelpers::clear (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground, + Colours::lightblue)); - void codeDocumentTextInserted (const String& /*newText*/, int /*insertIndex*/) override - { - startTimer (shaderLinkDelay); - } + if (textureToUse != nullptr) + if (! textureToUse->applyTo (texture)) + textureToUse = nullptr; - void codeDocumentTextDeleted (int /*startIndex*/, int /*endIndex*/) override - { - startTimer (shaderLinkDelay); - } + // First draw our background graphics to demonstrate the OpenGLGraphicsContext class + if (doBackgroundDrawing) + drawBackground2DStuff (desktopScale); - void timerCallback() override - { - stopTimer(); - demo.setShaderProgram (vertexDocument .getAllContent(), - fragmentDocument.getAllContent()); - } + updateShader(); // Check whether we need to compile a new shader - void lookAndFeelChanged() override - { - auto editorBackground = getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground, - Colours::white); + if (shader.get() == nullptr) + return; - for (int i = tabbedComp.getNumTabs(); i >= 0; --i) - tabbedComp.setTabBackgroundColour (i, editorBackground); + // Having used the juce 2D renderer, it will have messed-up a whole load of GL state, so + // we need to initialise some important settings before doing our normal GL 3D drawing.. + glEnable (GL_DEPTH_TEST); + glDepthFunc (GL_LESS); + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + openGLContext.extensions.glActiveTexture (GL_TEXTURE0); + glEnable (GL_TEXTURE_2D); - vertexEditorComp .setColour (CodeEditorComponent::backgroundColourId, editorBackground); - fragmentEditorComp.setColour (CodeEditorComponent::backgroundColourId, editorBackground); - } + glViewport (0, 0, roundToInt (desktopScale * getWidth()), roundToInt (desktopScale * getHeight())); - OpenGLDemo& demo; + texture.bind(); - Label speedLabel { {}, "Speed:" }, - zoomLabel { {}, "Zoom:" }; + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - CodeDocument vertexDocument, fragmentDocument; - CodeEditorComponent vertexEditorComp { vertexDocument, nullptr }, - fragmentEditorComp { fragmentDocument, nullptr }; + shader->use(); - TabbedComponent tabbedComp { TabbedButtonBar::TabsAtLeft }; + if (uniforms->projectionMatrix.get() != nullptr) + uniforms->projectionMatrix->setMatrix4 (getProjectionMatrix().mat, 1, false); - ComboBox presetBox, textureBox; + if (uniforms->viewMatrix.get() != nullptr) + uniforms->viewMatrix->setMatrix4 (getViewMatrix().mat, 1, false); - Label presetLabel { {}, "Shader Preset:" }, - textureLabel { {}, "Texture:" }; + if (uniforms->texture.get() != nullptr) + uniforms->texture->set ((GLint) 0); - Slider speedSlider, sizeSlider; + if (uniforms->lightPosition.get() != nullptr) + uniforms->lightPosition->set (-15.0f, 10.0f, 15.0f, 0.0f); - ToggleButton showBackgroundToggle { "Draw 2D graphics in background" }; + if (uniforms->bouncingNumber.get() != nullptr) + uniforms->bouncingNumber->set (bouncingNumber.getValue()); - OwnedArray textures; + shape->draw (openGLContext, *attributes); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoControlsOverlay) - }; + // Reset the element buffers so child Components draw correctly + openGLContext.extensions.glBindBuffer (GL_ARRAY_BUFFER, 0); + openGLContext.extensions.glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0); - //============================================================================== - /** This is the main demo component - the GL context gets attached to it, and - it implements the OpenGLRenderer callback so that it can do real GL work. - */ - class OpenGLDemo : public Component, - private OpenGLRenderer, - private AsyncUpdater + if (! controlsOverlay->isMouseButtonDown()) + rotation += (float) rotationSpeed; + } + + Matrix3D getProjectionMatrix() const { - public: - OpenGLDemo() - { - if (auto* peer = getPeer()) - peer->setCurrentRenderingEngine (0); + auto w = 1.0f / (scale + 0.1f); + auto h = w * getLocalBounds().toFloat().getAspectRatio (false); - setOpaque (true); - controlsOverlay.reset (new DemoControlsOverlay (*this)); - addAndMakeVisible (controlsOverlay.get()); + return Matrix3D::fromFrustum (-w, w, -h, h, 4.0f, 30.0f); + } - openGLContext.setRenderer (this); - openGLContext.attachTo (*this); - openGLContext.setContinuousRepainting (true); + Matrix3D getViewMatrix() const + { + auto viewMatrix = draggableOrientation.getRotationMatrix() + * Vector3D (0.0f, 1.0f, -10.0f); - controlsOverlay->initialise(); + auto rotationMatrix = Matrix3D::rotation ({ rotation, rotation, -0.3f }); - setSize (500, 500); - } + return rotationMatrix * viewMatrix; + } - ~OpenGLDemo() - { - openGLContext.detach(); - } + void setTexture (OpenGLUtils::DemoTexture* t) + { + lastTexture = textureToUse = t; + } - void newOpenGLContextCreated() override - { - // nothing to do in this case - we'll initialise our shaders + textures - // on demand, during the render callback. - freeAllContextObjects(); + void setShaderProgram (const String& vertexShader, const String& fragmentShader) + { + newVertexShader = vertexShader; + newFragmentShader = fragmentShader; + } - if (controlsOverlay.get() != nullptr) - controlsOverlay->updateShader(); - } + void paint (Graphics&) override {} - void openGLContextClosing() override - { - // When the context is about to close, you must use this callback to delete - // any GPU resources while the context is still current. - freeAllContextObjects(); + void resized() override + { + controlsOverlay->setBounds (getLocalBounds()); + draggableOrientation.setViewport (getLocalBounds()); + } - if (lastTexture != nullptr) - setTexture (lastTexture); - } + Draggable3DOrientation draggableOrientation; + bool doBackgroundDrawing = false; + float scale = 0.5f, rotationSpeed = 0.0f; + BouncingNumber bouncingNumber; + +private: + void handleAsyncUpdate() override + { + controlsOverlay->statusLabel.setText (statusText, dontSendNotification); + } + + void drawBackground2DStuff (float desktopScale) + { + // Create an OpenGLGraphicsContext that will draw into this GL window.. + std::unique_ptr glRenderer (createOpenGLGraphicsContext (openGLContext, + roundToInt (desktopScale * getWidth()), + roundToInt (desktopScale * getHeight()))); - void freeAllContextObjects() + if (glRenderer.get() != nullptr) { - shape .reset(); - shader .reset(); - attributes.reset(); - uniforms .reset(); - texture .release(); + Graphics g (*glRenderer); + g.addTransform (AffineTransform::scale (desktopScale)); + + for (auto s : stars) + { + auto size = 0.25f; + + // This stuff just creates a spinning star shape and fills it.. + Path p; + p.addStar ({ getWidth() * s.x.getValue(), + getHeight() * s.y.getValue() }, + 7, + getHeight() * size * 0.5f, + getHeight() * size, + s.angle.getValue()); + + auto hue = s.hue.getValue(); + + g.setGradientFill (ColourGradient (Colours::green.withRotatedHue (hue).withAlpha (0.8f), + 0, 0, + Colours::red.withRotatedHue (hue).withAlpha (0.5f), + 0, (float) getHeight(), false)); + g.fillPath (p); + } } + } + + OpenGLContext openGLContext; - // This is a virtual method in OpenGLRenderer, and is called when it's time - // to do your GL rendering. - void renderOpenGL() override + //============================================================================== + /** + This component sits on top of the main GL demo, and contains all the sliders + and widgets that control things. + */ + class DemoControlsOverlay : public Component, + private CodeDocument::Listener, + private Slider::Listener, + private Timer + { + public: + DemoControlsOverlay (OpenGLDemo& d) + : demo (d) { - jassert (OpenGLHelpers::isContextActive()); + addAndMakeVisible (statusLabel); + statusLabel.setJustificationType (Justification::topLeft); + statusLabel.setFont (Font (14.0f)); + + addAndMakeVisible (sizeSlider); + sizeSlider.setRange (0.0, 1.0, 0.001); + sizeSlider.addListener (this); + + addAndMakeVisible (zoomLabel); + zoomLabel.attachToComponent (&sizeSlider, true); + + addAndMakeVisible (speedSlider); + speedSlider.setRange (0.0, 0.5, 0.001); + speedSlider.addListener (this); + speedSlider.setSkewFactor (0.5f); - auto desktopScale = (float) openGLContext.getRenderingScale(); + addAndMakeVisible (speedLabel); + speedLabel.attachToComponent (&speedSlider, true); + + addAndMakeVisible (showBackgroundToggle); + showBackgroundToggle.onClick = [this] { demo.doBackgroundDrawing = showBackgroundToggle.getToggleState(); }; - OpenGLHelpers::clear (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground, - Colours::lightblue)); + addAndMakeVisible (tabbedComp); + tabbedComp.setTabBarDepth (25); + tabbedComp.setColour (TabbedButtonBar::tabTextColourId, Colours::grey); + tabbedComp.addTab ("Vertex", Colours::transparentBlack, &vertexEditorComp, false); + tabbedComp.addTab ("Fragment", Colours::transparentBlack, &fragmentEditorComp, false); - if (textureToUse != nullptr) - if (! textureToUse->applyTo (texture)) - textureToUse = nullptr; + vertexDocument.addListener (this); + fragmentDocument.addListener (this); - // First draw our background graphics to demonstrate the OpenGLGraphicsContext class - if (doBackgroundDrawing) - drawBackground2DStuff (desktopScale); + textures.add (new OpenGLUtils::TextureFromAsset ("portmeirion.jpg")); + textures.add (new OpenGLUtils::TextureFromAsset ("tile_background.png")); + textures.add (new OpenGLUtils::TextureFromAsset ("juce_icon.png")); + textures.add (new OpenGLUtils::DynamicTexture()); - updateShader(); // Check whether we need to compile a new shader + addAndMakeVisible (textureBox); + textureBox.onChange = [this] { selectTexture (textureBox.getSelectedId()); }; + updateTexturesList(); - if (shader.get() == nullptr) - return; + addAndMakeVisible (presetBox); + presetBox.onChange = [this] { selectPreset (presetBox.getSelectedItemIndex()); }; - // Having used the juce 2D renderer, it will have messed-up a whole load of GL state, so - // we need to initialise some important settings before doing our normal GL 3D drawing.. - glEnable (GL_DEPTH_TEST); - glDepthFunc (GL_LESS); - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - openGLContext.extensions.glActiveTexture (GL_TEXTURE0); - glEnable (GL_TEXTURE_2D); + auto presets = OpenGLUtils::getPresets(); - glViewport (0, 0, roundToInt (desktopScale * getWidth()), roundToInt (desktopScale * getHeight())); + for (int i = 0; i < presets.size(); ++i) + presetBox.addItem (presets[i].name, i + 1); - texture.bind(); + addAndMakeVisible (presetLabel); + presetLabel.attachToComponent (&presetBox, true); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + addAndMakeVisible (textureLabel); + textureLabel.attachToComponent (&textureBox, true); - shader->use(); + lookAndFeelChanged(); + } - if (uniforms->projectionMatrix.get() != nullptr) - uniforms->projectionMatrix->setMatrix4 (getProjectionMatrix().mat, 1, false); + void initialise() + { + showBackgroundToggle.setToggleState (false, sendNotification); + textureBox.setSelectedItemIndex (0); + presetBox .setSelectedItemIndex (0); + speedSlider.setValue (0.01); + sizeSlider .setValue (0.5); + } - if (uniforms->viewMatrix.get() != nullptr) - uniforms->viewMatrix->setMatrix4 (getViewMatrix().mat, 1, false); + void resized() override + { + auto area = getLocalBounds().reduced (4); - if (uniforms->texture.get() != nullptr) - uniforms->texture->set ((GLint) 0); + auto top = area.removeFromTop (75); - if (uniforms->lightPosition.get() != nullptr) - uniforms->lightPosition->set (-15.0f, 10.0f, 15.0f, 0.0f); + auto sliders = top.removeFromRight (area.getWidth() / 2); + showBackgroundToggle.setBounds (sliders.removeFromBottom (25)); + speedSlider .setBounds (sliders.removeFromBottom (25)); + sizeSlider .setBounds (sliders.removeFromBottom (25)); - if (uniforms->bouncingNumber.get() != nullptr) - uniforms->bouncingNumber->set (bouncingNumber.getValue()); + top.removeFromRight (70); + statusLabel.setBounds (top); - shape->draw (openGLContext, *attributes); + auto shaderArea = area.removeFromBottom (area.getHeight() / 2); - // Reset the element buffers so child Components draw correctly - openGLContext.extensions.glBindBuffer (GL_ARRAY_BUFFER, 0); - openGLContext.extensions.glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0); + auto presets = shaderArea.removeFromTop (25); + presets.removeFromLeft (100); + presetBox.setBounds (presets.removeFromLeft (150)); + presets.removeFromLeft (100); + textureBox.setBounds (presets); - if (! controlsOverlay->isMouseButtonDown()) - rotation += (float) rotationSpeed; + shaderArea.removeFromTop (4); + tabbedComp.setBounds (shaderArea); } - Matrix3D getProjectionMatrix() const + void mouseDown (const MouseEvent& e) override { - auto w = 1.0f / (scale + 0.1f); - auto h = w * getLocalBounds().toFloat().getAspectRatio (false); - - return Matrix3D::fromFrustum (-w, w, -h, h, 4.0f, 30.0f); + demo.draggableOrientation.mouseDown (e.getPosition()); } - Matrix3D getViewMatrix() const + void mouseDrag (const MouseEvent& e) override { - auto viewMatrix = draggableOrientation.getRotationMatrix() - * Vector3D (0.0f, 1.0f, -10.0f); - - auto rotationMatrix = Matrix3D::rotation ({ rotation, rotation, -0.3f }); - - return rotationMatrix * viewMatrix; + demo.draggableOrientation.mouseDrag (e.getPosition()); } - void setTexture (DemoTexture* t) + void mouseWheelMove (const MouseEvent&, const MouseWheelDetails& d) override { - lastTexture = textureToUse = t; + sizeSlider.setValue (sizeSlider.getValue() + d.deltaY); } - void setShaderProgram (const String& vertexShader, const String& fragmentShader) + void mouseMagnify (const MouseEvent&, float magnifyAmmount) override { - newVertexShader = vertexShader; - newFragmentShader = fragmentShader; + sizeSlider.setValue (sizeSlider.getValue() + magnifyAmmount - 1.0f); } - void paint (Graphics&) override {} - - void resized() override + void selectPreset (int preset) { - controlsOverlay->setBounds (getLocalBounds()); - draggableOrientation.setViewport (getLocalBounds()); - } + const auto& p = OpenGLUtils::getPresets()[preset]; - Draggable3DOrientation draggableOrientation; - bool doBackgroundDrawing = false; - float scale = 0.5f, rotationSpeed = 0.0f; - BouncingNumber bouncingNumber; + vertexDocument .replaceAllContent (p.vertexShader); + fragmentDocument.replaceAllContent (p.fragmentShader); - private: - void handleAsyncUpdate() override - { - controlsOverlay->statusLabel.setText (statusText, dontSendNotification); + startTimer (1); } - void drawBackground2DStuff (float desktopScale) + void selectTexture (int itemID) { - // Create an OpenGLGraphicsContext that will draw into this GL window.. - std::unique_ptr glRenderer (createOpenGLGraphicsContext (openGLContext, - roundToInt (desktopScale * getWidth()), - roundToInt (desktopScale * getHeight()))); - - if (glRenderer.get() != nullptr) + #if JUCE_MODAL_LOOPS_PERMITTED + if (itemID == 1000) { - Graphics g (*glRenderer); - g.addTransform (AffineTransform::scale (desktopScale)); + auto lastLocation = File::getSpecialLocation (File::userPicturesDirectory); - for (auto s : stars) + FileChooser fc ("Choose an image to open...", lastLocation, "*.jpg;*.jpeg;*.png;*.gif"); + + if (fc.browseForFileToOpen()) { - auto size = 0.25f; - - // This stuff just creates a spinning star shape and fills it.. - Path p; - p.addStar ({ getWidth() * s.x.getValue(), - getHeight() * s.y.getValue() }, - 7, - getHeight() * size * 0.5f, - getHeight() * size, - s.angle.getValue()); - - auto hue = s.hue.getValue(); - - g.setGradientFill (ColourGradient (Colours::green.withRotatedHue (hue).withAlpha (0.8f), - 0, 0, - Colours::red.withRotatedHue (hue).withAlpha (0.5f), - 0, (float) getHeight(), false)); - g.fillPath (p); + lastLocation = fc.getResult(); + + textures.add (new OpenGLUtils::TextureFromFile (fc.getResult())); + updateTexturesList(); + + textureBox.setSelectedId (textures.size()); } } + else + #endif + { + if (auto* t = textures[itemID - 1]) + demo.setTexture (t); + } } - OpenGLContext openGLContext; - - std::unique_ptr controlsOverlay; - - float rotation = 0.0f; - - std::unique_ptr shader; - std::unique_ptr shape; - std::unique_ptr attributes; - std::unique_ptr uniforms; - - OpenGLTexture texture; - DemoTexture* textureToUse = nullptr; - DemoTexture* lastTexture = nullptr; - - String newVertexShader, newFragmentShader, statusText; - - struct BackgroundStar + void updateTexturesList() { - SlowerBouncingNumber x, y, hue, angle; - }; + textureBox.clear(); + + for (int i = 0; i < textures.size(); ++i) + textureBox.addItem (textures.getUnchecked (i)->name, i + 1); - BackgroundStar stars[3]; + #if JUCE_MODAL_LOOPS_PERMITTED + textureBox.addSeparator(); + textureBox.addItem ("Load from a file...", 1000); + #endif + } - //============================================================================== void updateShader() { - if (newVertexShader.isNotEmpty() || newFragmentShader.isNotEmpty()) - { - std::unique_ptr newShader (new OpenGLShaderProgram (openGLContext)); - - if (newShader->addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (newVertexShader)) - && newShader->addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (newFragmentShader)) - && newShader->link()) - { - shape .reset(); - attributes.reset(); - uniforms .reset(); - - shader.reset (newShader.release()); - shader->use(); - - shape .reset (new Shape (openGLContext)); - attributes.reset (new Attributes (openGLContext, *shader)); - uniforms .reset (new Uniforms (openGLContext, *shader)); - - statusText = "GLSL: v" + String (OpenGLShaderProgram::getLanguageVersion(), 2); - } - else - { - statusText = newShader->getLastError(); - } - - triggerAsyncUpdate(); - - newVertexShader = {}; - newFragmentShader = {}; - } + startTimer (10); } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLDemo) - }; + Label statusLabel; - //============================================================================== - struct ShaderPreset - { - const char* name; - const char* vertexShader; - const char* fragmentShader; - }; + private: + void sliderValueChanged (Slider*) override + { + demo.scale = (float) sizeSlider .getValue(); + demo.rotationSpeed = (float) speedSlider.getValue(); + } - static Array getPresets() - { - #define SHADER_DEMO_HEADER \ - "/* This is a live OpenGL Shader demo.\n" \ - " Edit the shader program below and it will be \n" \ - " compiled and applied to the model above!\n" \ - "*/\n\n" + enum { shaderLinkDelay = 500 }; - ShaderPreset presets[] = + void codeDocumentTextInserted (const String& /*newText*/, int /*insertIndex*/) override { - { - "Texture + Lighting", - - SHADER_DEMO_HEADER - "attribute vec4 position;\n" - "attribute vec4 normal;\n" - "attribute vec4 sourceColour;\n" - "attribute vec2 textureCoordIn;\n" - "\n" - "uniform mat4 projectionMatrix;\n" - "uniform mat4 viewMatrix;\n" - "uniform vec4 lightPosition;\n" - "\n" - "varying vec4 destinationColour;\n" - "varying vec2 textureCoordOut;\n" - "varying float lightIntensity;\n" - "\n" - "void main()\n" - "{\n" - " destinationColour = sourceColour;\n" - " textureCoordOut = textureCoordIn;\n" - "\n" - " vec4 light = viewMatrix * lightPosition;\n" - " lightIntensity = dot (light, normal);\n" - "\n" - " gl_Position = projectionMatrix * viewMatrix * position;\n" - "}\n", + startTimer (shaderLinkDelay); + } - SHADER_DEMO_HEADER - #if JUCE_OPENGL_ES - "varying lowp vec4 destinationColour;\n" - "varying lowp vec2 textureCoordOut;\n" - "varying highp float lightIntensity;\n" - #else - "varying vec4 destinationColour;\n" - "varying vec2 textureCoordOut;\n" - "varying float lightIntensity;\n" - #endif - "\n" - "uniform sampler2D demoTexture;\n" - "\n" - "void main()\n" - "{\n" - #if JUCE_OPENGL_ES - " highp float l = max (0.3, lightIntensity * 0.3);\n" - " highp vec4 colour = vec4 (l, l, l, 1.0);\n" - #else - " float l = max (0.3, lightIntensity * 0.3);\n" - " vec4 colour = vec4 (l, l, l, 1.0);\n" - #endif - " gl_FragColor = colour * texture2D (demoTexture, textureCoordOut);\n" - "}\n" - }, + void codeDocumentTextDeleted (int /*startIndex*/, int /*endIndex*/) override + { + startTimer (shaderLinkDelay); + } - { - "Textured", + void timerCallback() override + { + stopTimer(); + demo.setShaderProgram (vertexDocument .getAllContent(), + fragmentDocument.getAllContent()); + } - SHADER_DEMO_HEADER - "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", + void lookAndFeelChanged() override + { + auto editorBackground = getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground, + Colours::white); - SHADER_DEMO_HEADER - #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" - "uniform sampler2D demoTexture;\n" - "\n" - "void main()\n" - "{\n" - " gl_FragColor = texture2D (demoTexture, textureCoordOut);\n" - "}\n" - }, + for (int i = tabbedComp.getNumTabs(); i >= 0; --i) + tabbedComp.setTabBackgroundColour (i, editorBackground); - { - "Flat Colour", + vertexEditorComp .setColour (CodeEditorComponent::backgroundColourId, editorBackground); + fragmentEditorComp.setColour (CodeEditorComponent::backgroundColourId, editorBackground); + } - SHADER_DEMO_HEADER - "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", + OpenGLDemo& demo; - SHADER_DEMO_HEADER - #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" - " gl_FragColor = destinationColour;\n" - "}\n" - }, + Label speedLabel { {}, "Speed:" }, + zoomLabel { {}, "Zoom:" }; - { - "Rainbow", + CodeDocument vertexDocument, fragmentDocument; + CodeEditorComponent vertexEditorComp { vertexDocument, nullptr }, + fragmentEditorComp { fragmentDocument, nullptr }; - SHADER_DEMO_HEADER - "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" - "varying float xPos;\n" - "varying float yPos;\n" - "varying float zPos;\n" - "\n" - "void main()\n" - "{\n" - " vec4 v = vec4 (position);\n" - " xPos = clamp (v.x, 0.0, 1.0);\n" - " yPos = clamp (v.y, 0.0, 1.0);\n" - " zPos = clamp (v.z, 0.0, 1.0);\n" - " gl_Position = projectionMatrix * viewMatrix * position;\n" - "}", + TabbedComponent tabbedComp { TabbedButtonBar::TabsAtLeft }; - SHADER_DEMO_HEADER - #if JUCE_OPENGL_ES - "varying lowp vec4 destinationColour;\n" - "varying lowp vec2 textureCoordOut;\n" - "varying lowp float xPos;\n" - "varying lowp float yPos;\n" - "varying lowp float zPos;\n" - #else - "varying vec4 destinationColour;\n" - "varying vec2 textureCoordOut;\n" - "varying float xPos;\n" - "varying float yPos;\n" - "varying float zPos;\n" - #endif - "\n" - "void main()\n" - "{\n" - " gl_FragColor = vec4 (xPos, yPos, zPos, 1.0);\n" - "}" - }, + ComboBox presetBox, textureBox; - { - "Changing Colour", + Label presetLabel { {}, "Shader Preset:" }, + textureLabel { {}, "Texture:" }; - SHADER_DEMO_HEADER - "attribute vec4 position;\n" - "attribute vec2 textureCoordIn;\n" - "\n" - "uniform mat4 projectionMatrix;\n" - "uniform mat4 viewMatrix;\n" - "\n" - "varying vec2 textureCoordOut;\n" - "\n" - "void main()\n" - "{\n" - " textureCoordOut = textureCoordIn;\n" - " gl_Position = projectionMatrix * viewMatrix * position;\n" - "}\n", + Slider speedSlider, sizeSlider; - SHADER_DEMO_HEADER - "#define PI 3.1415926535897932384626433832795\n" - "\n" - #if JUCE_OPENGL_ES - "precision mediump float;\n" - "varying lowp vec2 textureCoordOut;\n" - #else - "varying vec2 textureCoordOut;\n" - #endif - "uniform float bouncingNumber;\n" - "\n" - "void main()\n" - "{\n" - " float b = bouncingNumber;\n" - " float n = b * PI * 2.0;\n" - " float sn = (sin (n * textureCoordOut.x) * 0.5) + 0.5;\n" - " float cn = (sin (n * textureCoordOut.y) * 0.5) + 0.5;\n" - "\n" - " vec4 col = vec4 (b, sn, cn, 1.0);\n" - " gl_FragColor = col;\n" - "}\n" - }, + ToggleButton showBackgroundToggle { "Draw 2D graphics in background" }; - { - "Simple Light", + OwnedArray textures; - SHADER_DEMO_HEADER - "attribute vec4 position;\n" - "attribute vec4 normal;\n" - "\n" - "uniform mat4 projectionMatrix;\n" - "uniform mat4 viewMatrix;\n" - "uniform vec4 lightPosition;\n" - "\n" - "varying float lightIntensity;\n" - "\n" - "void main()\n" - "{\n" - " vec4 light = viewMatrix * lightPosition;\n" - " lightIntensity = dot (light, normal);\n" - "\n" - " gl_Position = projectionMatrix * viewMatrix * position;\n" - "}\n", + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoControlsOverlay) + }; - SHADER_DEMO_HEADER - #if JUCE_OPENGL_ES - "varying highp float lightIntensity;\n" - #else - "varying float lightIntensity;\n" - #endif - "\n" - "void main()\n" - "{\n" - #if JUCE_OPENGL_ES - " highp float l = lightIntensity * 0.25;\n" - " highp vec4 colour = vec4 (l, l, l, 1.0);\n" - #else - " float l = lightIntensity * 0.25;\n" - " vec4 colour = vec4 (l, l, l, 1.0);\n" - #endif - "\n" - " gl_FragColor = colour;\n" - "}\n" - }, + std::unique_ptr controlsOverlay; - { - "Flattened", + float rotation = 0.0f; - SHADER_DEMO_HEADER - "attribute vec4 position;\n" - "attribute vec4 normal;\n" - "\n" - "uniform mat4 projectionMatrix;\n" - "uniform mat4 viewMatrix;\n" - "uniform vec4 lightPosition;\n" - "\n" - "varying float lightIntensity;\n" - "\n" - "void main()\n" - "{\n" - " vec4 light = viewMatrix * lightPosition;\n" - " lightIntensity = dot (light, normal);\n" - "\n" - " vec4 v = vec4 (position);\n" - " v.z = v.z * 0.1;\n" - "\n" - " gl_Position = projectionMatrix * viewMatrix * v;\n" - "}\n", + std::unique_ptr shader; + std::unique_ptr shape; + std::unique_ptr attributes; + std::unique_ptr uniforms; - SHADER_DEMO_HEADER - #if JUCE_OPENGL_ES - "varying highp float lightIntensity;\n" - #else - "varying float lightIntensity;\n" - #endif - "\n" - "void main()\n" - "{\n" - #if JUCE_OPENGL_ES - " highp float l = lightIntensity * 0.25;\n" - " highp vec4 colour = vec4 (l, l, l, 1.0);\n" - #else - " float l = lightIntensity * 0.25;\n" - " vec4 colour = vec4 (l, l, l, 1.0);\n" - #endif - "\n" - " gl_FragColor = colour;\n" - "}\n" - }, + OpenGLTexture texture; + OpenGLUtils::DemoTexture* textureToUse = nullptr; + OpenGLUtils::DemoTexture* lastTexture = nullptr; + String newVertexShader, newFragmentShader, statusText; + + struct BackgroundStar + { + SlowerBouncingNumber x, y, hue, angle; + }; + + BackgroundStar stars[3]; + + //============================================================================== + void updateShader() + { + if (newVertexShader.isNotEmpty() || newFragmentShader.isNotEmpty()) + { + std::unique_ptr newShader (new OpenGLShaderProgram (openGLContext)); + + if (newShader->addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (newVertexShader)) + && newShader->addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (newFragmentShader)) + && newShader->link()) { - "Toon Shader", + shape .reset(); + attributes.reset(); + uniforms .reset(); - SHADER_DEMO_HEADER - "attribute vec4 position;\n" - "attribute vec4 normal;\n" - "\n" - "uniform mat4 projectionMatrix;\n" - "uniform mat4 viewMatrix;\n" - "uniform vec4 lightPosition;\n" - "\n" - "varying float lightIntensity;\n" - "\n" - "void main()\n" - "{\n" - " vec4 light = viewMatrix * lightPosition;\n" - " lightIntensity = dot (light, normal);\n" - "\n" - " gl_Position = projectionMatrix * viewMatrix * position;\n" - "}\n", + shader.reset (newShader.release()); + shader->use(); - SHADER_DEMO_HEADER - #if JUCE_OPENGL_ES - "varying highp float lightIntensity;\n" - #else - "varying float lightIntensity;\n" - #endif - "\n" - "void main()\n" - "{\n" - #if JUCE_OPENGL_ES - " highp float intensity = lightIntensity * 0.5;\n" - " highp vec4 colour;\n" - #else - " float intensity = lightIntensity * 0.5;\n" - " vec4 colour;\n" - #endif - "\n" - " if (intensity > 0.95)\n" - " colour = vec4 (1.0, 0.5, 0.5, 1.0);\n" - " else if (intensity > 0.5)\n" - " colour = vec4 (0.6, 0.3, 0.3, 1.0);\n" - " else if (intensity > 0.25)\n" - " colour = vec4 (0.4, 0.2, 0.2, 1.0);\n" - " else\n" - " colour = vec4 (0.2, 0.1, 0.1, 1.0);\n" - "\n" - " gl_FragColor = colour;\n" - "}\n" + shape .reset (new OpenGLUtils::Shape (openGLContext)); + attributes.reset (new OpenGLUtils::Attributes (openGLContext, *shader)); + uniforms .reset (new OpenGLUtils::Uniforms (openGLContext, *shader)); + + statusText = "GLSL: v" + String (OpenGLShaderProgram::getLanguageVersion(), 2); + } + else + { + statusText = newShader->getLastError(); } - }; - return Array (presets, numElementsInArray (presets)); + triggerAsyncUpdate(); + + newVertexShader = {}; + newFragmentShader = {}; + } } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLDemo) }; diff --git a/examples/Utilities/UnitTestsDemo.h b/examples/Utilities/UnitTestsDemo.h index 4c2f9c3de4..26b4dbc961 100644 --- a/examples/Utilities/UnitTestsDemo.h +++ b/examples/Utilities/UnitTestsDemo.h @@ -40,7 +40,7 @@ defines: JUCE_UNIT_TESTS=1 type: Component - mainClass: UnitTestClasses::UnitTestsDemo + mainClass: UnitTestsDemo useLocalCopy: 1 @@ -53,35 +53,98 @@ #include "../Assets/DemoUtilities.h" //============================================================================== -struct UnitTestClasses +class UnitTestsDemo : public Component { - class UnitTestsDemo; - class TestRunnerThread; +public: + UnitTestsDemo() + { + setOpaque (true); + + addAndMakeVisible (startTestButton); + startTestButton.onClick = [this] { start(); }; + + addAndMakeVisible (testResultsBox); + testResultsBox.setMultiLine (true); + testResultsBox.setFont (Font (Font::getDefaultMonospacedFontName(), 12.0f, Font::plain)); + + addAndMakeVisible (categoriesBox); + categoriesBox.addItem ("All Tests", 1); + + auto categories = UnitTest::getAllCategories(); + categories.sort (true); + + categoriesBox.addItemList (categories, 2); + categoriesBox.setSelectedId (1); + + logMessage ("This panel runs the built-in JUCE unit-tests from the selected category.\n"); + logMessage ("To add your own unit-tests, see the JUCE_UNIT_TESTS macro."); + + setSize (500, 500); + } + + ~UnitTestsDemo() + { + stopTest(); + } //============================================================================== - // This subclass of UnitTestRunner is used to redirect the test output to our - // TextBox, and to interrupt the running tests when our thread is asked to stop.. - class CustomTestRunner : public UnitTestRunner + void paint (Graphics& g) override { - public: - CustomTestRunner (TestRunnerThread& trt) : owner (trt) {} + g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground, + Colours::grey)); + } - void logMessage (const String& message) override - { - owner.logMessage (message); - } + void resized() override + { + auto bounds = getLocalBounds().reduced (6); + + auto topSlice = bounds.removeFromTop (25); + startTestButton.setBounds (topSlice.removeFromLeft (200)); + topSlice.removeFromLeft (10); + categoriesBox .setBounds (topSlice.removeFromLeft (250)); + + bounds.removeFromTop (5); + testResultsBox.setBounds (bounds); + } + + void start() + { + startTest (categoriesBox.getText()); + } + + void startTest (const String& category) + { + testResultsBox.clear(); + startTestButton.setEnabled (false); + + currentTestThread.reset (new TestRunnerThread (*this, category)); + currentTestThread->startThread(); + } - bool shouldAbortTests() override + void stopTest() + { + if (currentTestThread.get() != nullptr) { - return owner.threadShouldExit(); + currentTestThread->stopThread (15000); + currentTestThread.reset(); } + } - private: - TestRunnerThread& owner; + void logMessage (const String& message) + { + testResultsBox.moveCaretToEnd(); + testResultsBox.insertTextAtCaret (message + newLine); + testResultsBox.moveCaretToEnd(); + } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomTestRunner) - }; + void testFinished() + { + stopTest(); + startTestButton.setEnabled (true); + logMessage (newLine + "*** Tests finished ***"); + } +private: //============================================================================== class TestRunnerThread : public Thread, private Timer @@ -121,117 +184,45 @@ struct UnitTestClasses } private: - UnitTestsDemo& owner; - const String category; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestRunnerThread) - }; - - - //============================================================================== - class UnitTestsDemo : public Component - { - public: - UnitTestsDemo() - { - setOpaque (true); - - addAndMakeVisible (startTestButton); - startTestButton.onClick = [this] { start(); }; - - addAndMakeVisible (testResultsBox); - testResultsBox.setMultiLine (true); - testResultsBox.setFont (Font (Font::getDefaultMonospacedFontName(), 12.0f, Font::plain)); - - addAndMakeVisible (categoriesBox); - categoriesBox.addItem ("All Tests", 1); - - auto categories = UnitTest::getAllCategories(); - categories.sort (true); - - categoriesBox.addItemList (categories, 2); - categoriesBox.setSelectedId (1); - - logMessage ("This panel runs the built-in JUCE unit-tests from the selected category.\n"); - logMessage ("To add your own unit-tests, see the JUCE_UNIT_TESTS macro."); - - setSize (500, 500); - } - - ~UnitTestsDemo() - { - stopTest(); - } - //============================================================================== - void paint (Graphics& g) override - { - g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground, - Colours::grey)); - } - - void resized() override - { - auto bounds = getLocalBounds().reduced (6); - - auto topSlice = bounds.removeFromTop (25); - startTestButton.setBounds (topSlice.removeFromLeft (200)); - topSlice.removeFromLeft (10); - categoriesBox .setBounds (topSlice.removeFromLeft (250)); - - bounds.removeFromTop (5); - testResultsBox.setBounds (bounds); - } - - void start() + // This subclass of UnitTestRunner is used to redirect the test output to our + // TextBox, and to interrupt the running tests when our thread is asked to stop.. + class CustomTestRunner : public UnitTestRunner { - startTest (categoriesBox.getText()); - } + public: + CustomTestRunner (TestRunnerThread& trt) : owner (trt) {} - void startTest (const String& category) - { - testResultsBox.clear(); - startTestButton.setEnabled (false); - - currentTestThread.reset (new TestRunnerThread (*this, category)); - currentTestThread->startThread(); - } + void logMessage (const String& message) override + { + owner.logMessage (message); + } - void stopTest() - { - if (currentTestThread.get() != nullptr) + bool shouldAbortTests() override { - currentTestThread->stopThread (15000); - currentTestThread.reset(); + return owner.threadShouldExit(); } - } - void logMessage (const String& message) - { - testResultsBox.moveCaretToEnd(); - testResultsBox.insertTextAtCaret (message + newLine); - testResultsBox.moveCaretToEnd(); - } + private: + TestRunnerThread& owner; - void testFinished() - { - stopTest(); - startTestButton.setEnabled (true); - logMessage (newLine + "*** Tests finished ***"); - } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomTestRunner) + }; - private: - std::unique_ptr currentTestThread; + UnitTestsDemo& owner; + const String category; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestRunnerThread) + }; + std::unique_ptr currentTestThread; - TextButton startTestButton { "Run Unit Tests..." }; - ComboBox categoriesBox; - TextEditor testResultsBox; + TextButton startTestButton { "Run Unit Tests..." }; + ComboBox categoriesBox; + TextEditor testResultsBox; - void lookAndFeelChanged() override - { - testResultsBox.applyFontToAllText (testResultsBox.getFont()); - } + void lookAndFeelChanged() override + { + testResultsBox.applyFontToAllText (testResultsBox.getFont()); + } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnitTestsDemo) - }; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnitTestsDemo) };