The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

737 lines
26KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE examples.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. The code included in this file is provided under the terms of the ISC license
  6. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  7. To use, copy, modify, and/or distribute this software for any purpose with or
  8. without fee is hereby granted provided that the above copyright notice and
  9. this permission notice appear in all copies.
  10. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
  11. WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
  12. PURPOSE, ARE DISCLAIMED.
  13. ==============================================================================
  14. */
  15. /*******************************************************************************
  16. The block below describes the properties of this PIP. A PIP is a short snippet
  17. of code that can be read by the Projucer and used to generate a JUCE project.
  18. BEGIN_JUCE_PIP_METADATA
  19. name: GraphicsDemo
  20. version: 1.0.0
  21. vendor: JUCE
  22. website: http://juce.com
  23. description: Showcases various graphics features.
  24. dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
  25. juce_gui_basics
  26. exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
  27. moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
  28. type: Component
  29. mainClass: GraphicsDemo
  30. useLocalCopy: 1
  31. END_JUCE_PIP_METADATA
  32. *******************************************************************************/
  33. #pragma once
  34. #include "../Assets/DemoUtilities.h"
  35. //==============================================================================
  36. /** Holds the various toggle buttons for the animation modes. */
  37. class ControllersComponent final : public Component
  38. {
  39. public:
  40. ControllersComponent()
  41. {
  42. setOpaque (true);
  43. initialiseToggle (animatePosition, "Animate Position", true);
  44. initialiseToggle (animateRotation, "Animate Rotation", true);
  45. initialiseToggle (animateSize, "Animate Size", false);
  46. initialiseToggle (animateShear, "Animate Shearing", false);
  47. initialiseToggle (animateAlpha, "Animate Alpha", false);
  48. initialiseToggle (clipToRectangle, "Clip to Rectangle", false);
  49. initialiseToggle (clipToPath, "Clip to Path", false);
  50. initialiseToggle (clipToImage, "Clip to Image", false);
  51. initialiseToggle (quality, "Higher quality image interpolation", false);
  52. }
  53. void paint (Graphics& g) override
  54. {
  55. g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
  56. }
  57. void resized() override
  58. {
  59. auto r = getLocalBounds().reduced (4);
  60. int buttonHeight = 22;
  61. auto columns = r.removeFromTop (buttonHeight * 4);
  62. auto col = columns.removeFromLeft (200);
  63. animatePosition.setBounds (col.removeFromTop (buttonHeight));
  64. animateRotation.setBounds (col.removeFromTop (buttonHeight));
  65. animateSize .setBounds (col.removeFromTop (buttonHeight));
  66. animateShear .setBounds (col.removeFromTop (buttonHeight));
  67. columns.removeFromLeft (20);
  68. col = columns.removeFromLeft (200);
  69. animateAlpha .setBounds (col.removeFromTop (buttonHeight));
  70. clipToRectangle.setBounds (col.removeFromTop (buttonHeight));
  71. clipToPath .setBounds (col.removeFromTop (buttonHeight));
  72. clipToImage .setBounds (col.removeFromTop (buttonHeight));
  73. r.removeFromBottom (6);
  74. quality.setBounds (r.removeFromTop (buttonHeight));
  75. }
  76. void initialiseToggle (ToggleButton& b, const char* name, bool on)
  77. {
  78. addAndMakeVisible (b);
  79. b.setButtonText (name);
  80. b.setToggleState (on, dontSendNotification);
  81. }
  82. ToggleButton animateRotation, animatePosition, animateAlpha, animateSize, animateShear;
  83. ToggleButton clipToRectangle, clipToPath, clipToImage, quality;
  84. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ControllersComponent)
  85. };
  86. //==============================================================================
  87. class GraphicsDemoBase : public Component
  88. {
  89. public:
  90. GraphicsDemoBase (ControllersComponent& cc, const String& name)
  91. : Component (name),
  92. controls (cc)
  93. {
  94. displayFont = Font (Font::getDefaultMonospacedFontName(), 12.0f, Font::bold);
  95. }
  96. AffineTransform getTransform()
  97. {
  98. auto hw = 0.5f * (float) getWidth();
  99. auto hh = 0.5f * (float) getHeight();
  100. AffineTransform t;
  101. if (controls.animateRotation.getToggleState())
  102. t = t.rotated (rotation.getValue() * MathConstants<float>::twoPi);
  103. if (controls.animateSize.getToggleState())
  104. t = t.scaled (0.3f + size.getValue() * 2.0f);
  105. if (controls.animatePosition.getToggleState())
  106. t = t.translated (hw + hw * (offsetX.getValue() - 0.5f),
  107. hh + hh * (offsetY.getValue() - 0.5f));
  108. else
  109. t = t.translated (hw, hh);
  110. if (controls.animateShear.getToggleState())
  111. t = t.sheared (shear.getValue() * 2.0f - 1.0f, 0.0f);
  112. return t;
  113. }
  114. float getAlpha() const
  115. {
  116. if (controls.animateAlpha.getToggleState())
  117. return alpha.getValue();
  118. return 1.0f;
  119. }
  120. void paint (Graphics& g) override
  121. {
  122. auto startTime = 0.0;
  123. {
  124. // A ScopedSaveState will return the Graphics context to the state it was at the time of
  125. // construction when it goes out of scope. We use it here to avoid clipping the fps text
  126. const Graphics::ScopedSaveState state (g);
  127. if (controls.clipToRectangle.getToggleState()) clipToRectangle (g);
  128. if (controls.clipToPath .getToggleState()) clipToPath (g);
  129. if (controls.clipToImage .getToggleState()) clipToImage (g);
  130. g.setImageResamplingQuality (controls.quality.getToggleState() ? Graphics::highResamplingQuality
  131. : Graphics::mediumResamplingQuality);
  132. // take a note of the time before the render
  133. startTime = Time::getMillisecondCounterHiRes();
  134. // then let the demo draw itself..
  135. drawDemo (g);
  136. }
  137. auto now = Time::getMillisecondCounterHiRes();
  138. auto filtering = 0.08;
  139. auto elapsedMs = now - startTime;
  140. averageTimeMs += (elapsedMs - averageTimeMs) * filtering;
  141. auto sinceLastRender = now - lastRenderStartTime;
  142. lastRenderStartTime = now;
  143. auto effectiveFPS = 1000.0 / averageTimeMs;
  144. auto actualFPS = sinceLastRender > 0 ? (1000.0 / sinceLastRender) : 0;
  145. averageActualFPS += (actualFPS - averageActualFPS) * filtering;
  146. GlyphArrangement ga;
  147. ga.addFittedText (displayFont,
  148. "Time: " + String (averageTimeMs, 2)
  149. + " ms\nEffective FPS: " + String (effectiveFPS, 1)
  150. + "\nActual FPS: " + String (averageActualFPS, 1),
  151. 0, 10.0f, (float) getWidth() - 10.0f, (float) getHeight(), Justification::topRight, 3);
  152. g.setColour (Colours::white.withAlpha (0.5f));
  153. g.fillRect (ga.getBoundingBox (0, ga.getNumGlyphs(), true).getSmallestIntegerContainer().expanded (4));
  154. g.setColour (Colours::black);
  155. ga.draw (g);
  156. }
  157. virtual void drawDemo (Graphics&) = 0;
  158. void clipToRectangle (Graphics& g)
  159. {
  160. auto w = getWidth() / 2;
  161. auto h = getHeight() / 2;
  162. auto x = (int) ((float) w * clipRectX.getValue());
  163. auto y = (int) ((float) h * clipRectY.getValue());
  164. g.reduceClipRegion (x, y, w, h);
  165. }
  166. void clipToPath (Graphics& g)
  167. {
  168. auto pathSize = (float) jmin (getWidth(), getHeight());
  169. Path p;
  170. p.addStar (Point<float> (clipPathX.getValue(),
  171. clipPathY.getValue()) * pathSize,
  172. 7,
  173. pathSize * (0.5f + clipPathDepth.getValue()),
  174. pathSize * 0.5f,
  175. clipPathAngle.getValue());
  176. g.reduceClipRegion (p, AffineTransform());
  177. }
  178. void clipToImage (Graphics& g)
  179. {
  180. if (! clipImage.isValid())
  181. createClipImage();
  182. AffineTransform transform (AffineTransform::translation ((float) clipImage.getWidth() / -2.0f,
  183. (float) clipImage.getHeight() / -2.0f)
  184. .rotated (clipImageAngle.getValue() * MathConstants<float>::twoPi)
  185. .scaled (2.0f + clipImageSize.getValue() * 3.0f)
  186. .translated ((float) getWidth() * 0.5f,
  187. (float) getHeight() * 0.5f));
  188. g.reduceClipRegion (clipImage, transform);
  189. }
  190. void createClipImage()
  191. {
  192. clipImage = Image (Image::ARGB, 300, 300, true);
  193. Graphics g (clipImage);
  194. g.setGradientFill (ColourGradient (Colours::transparentBlack, 0, 0,
  195. Colours::black, 0, 300, false));
  196. for (int i = 0; i < 20; ++i)
  197. g.fillRect (Random::getSystemRandom().nextInt (200),
  198. Random::getSystemRandom().nextInt (200),
  199. Random::getSystemRandom().nextInt (100),
  200. Random::getSystemRandom().nextInt (100));
  201. }
  202. //==============================================================================
  203. ControllersComponent& controls;
  204. SlowerBouncingNumber offsetX, offsetY, rotation, size, shear, alpha, clipRectX,
  205. clipRectY, clipPathX, clipPathY, clipPathDepth, clipPathAngle,
  206. clipImageX, clipImageY, clipImageAngle, clipImageSize;
  207. double lastRenderStartTime = 0.0, averageTimeMs = 0.0, averageActualFPS = 0.0;
  208. Image clipImage;
  209. Font displayFont;
  210. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GraphicsDemoBase)
  211. };
  212. //==============================================================================
  213. class RectangleFillTypesDemo final : public GraphicsDemoBase
  214. {
  215. public:
  216. RectangleFillTypesDemo (ControllersComponent& cc)
  217. : GraphicsDemoBase (cc, "Fill Types: Rectangles")
  218. {}
  219. void drawDemo (Graphics& g) override
  220. {
  221. g.addTransform (getTransform());
  222. const int rectSize = jmin (getWidth(), getHeight()) / 2 - 20;
  223. g.setColour (colour1.withAlpha (getAlpha()));
  224. g.fillRect (-rectSize, -rectSize, rectSize, rectSize);
  225. g.setGradientFill (ColourGradient (colour1, 10.0f, (float) -rectSize,
  226. colour2, 10.0f + (float) rectSize, 0.0f, false));
  227. g.setOpacity (getAlpha());
  228. g.fillRect (10, -rectSize, rectSize, rectSize);
  229. g.setGradientFill (ColourGradient (colour1, (float) rectSize * -0.5f, 10.0f + (float) rectSize * 0.5f,
  230. colour2, 0, 10.0f + (float) rectSize, true));
  231. g.setOpacity (getAlpha());
  232. g.fillRect (-rectSize, 10, rectSize, rectSize);
  233. g.setGradientFill (ColourGradient (colour1, 10.0f, 10.0f,
  234. colour2, 10.0f + (float) rectSize, 10.0f + (float) rectSize, false));
  235. g.setOpacity (getAlpha());
  236. g.drawRect (10, 10, rectSize, rectSize, 5);
  237. }
  238. Colour colour1 { Colours::red }, colour2 { Colours::green };
  239. };
  240. //==============================================================================
  241. class PathsDemo final : public GraphicsDemoBase
  242. {
  243. public:
  244. PathsDemo (ControllersComponent& cc, bool linear, bool radial)
  245. : GraphicsDemoBase (cc, String ("Paths") + (radial ? ": Radial Gradients"
  246. : (linear ? ": Linear Gradients"
  247. : ": Solid"))),
  248. useLinearGradient (linear), useRadialGradient (radial)
  249. {
  250. logoPath = getJUCELogoPath();
  251. // rescale the logo path so that it's centred about the origin and has the right size.
  252. logoPath.applyTransform (RectanglePlacement (RectanglePlacement::centred)
  253. .getTransformToFit (logoPath.getBounds(),
  254. Rectangle<float> (-120.0f, -120.0f, 240.0f, 240.0f)));
  255. // Surround it with some other shapes..
  256. logoPath.addStar ({ -300.0f, -50.0f }, 7, 30.0f, 70.0f, 0.1f);
  257. logoPath.addStar ({ 300.0f, 50.0f }, 6, 40.0f, 70.0f, 0.1f);
  258. logoPath.addEllipse (-100.0f, 150.0f, 200.0f, 140.0f);
  259. logoPath.addRectangle (-100.0f, -280.0f, 200.0f, 140.0f);
  260. }
  261. void drawDemo (Graphics& g) override
  262. {
  263. auto& p = logoPath;
  264. if (useLinearGradient || useRadialGradient)
  265. {
  266. Colour c1 (gradientColours[0].getValue(), gradientColours[1].getValue(), gradientColours[2].getValue(), 1.0f);
  267. Colour c2 (gradientColours[3].getValue(), gradientColours[4].getValue(), gradientColours[5].getValue(), 1.0f);
  268. Colour c3 (gradientColours[6].getValue(), gradientColours[7].getValue(), gradientColours[8].getValue(), 1.0f);
  269. auto x1 = gradientPositions[0].getValue() * (float) getWidth() * 0.25f;
  270. auto y1 = gradientPositions[1].getValue() * (float) getHeight() * 0.25f;
  271. auto x2 = gradientPositions[2].getValue() * (float) getWidth() * 0.75f;
  272. auto y2 = gradientPositions[3].getValue() * (float) getHeight() * 0.75f;
  273. ColourGradient gradient (c1, x1, y1,
  274. c2, x2, y2,
  275. useRadialGradient);
  276. gradient.addColour (gradientIntermediate.getValue(), c3);
  277. g.setGradientFill (gradient);
  278. }
  279. else
  280. {
  281. g.setColour (Colours::blue);
  282. }
  283. g.setOpacity (getAlpha());
  284. g.fillPath (p, getTransform());
  285. }
  286. Path logoPath;
  287. bool useLinearGradient, useRadialGradient;
  288. SlowerBouncingNumber gradientColours[9], gradientPositions[4], gradientIntermediate;
  289. };
  290. //==============================================================================
  291. class StrokesDemo final : public GraphicsDemoBase
  292. {
  293. public:
  294. StrokesDemo (ControllersComponent& cc)
  295. : GraphicsDemoBase (cc, "Paths: Stroked")
  296. {}
  297. void drawDemo (Graphics& g) override
  298. {
  299. auto w = (float) getWidth();
  300. auto h = (float) getHeight();
  301. Path p;
  302. p.startNewSubPath (points[0].getValue() * w,
  303. points[1].getValue() * h);
  304. for (int i = 2; i < numElementsInArray (points); i += 4)
  305. p.quadraticTo (points[i] .getValue() * w,
  306. points[i + 1].getValue() * h,
  307. points[i + 2].getValue() * w,
  308. points[i + 3].getValue() * h);
  309. p.closeSubPath();
  310. PathStrokeType stroke (0.5f + 10.0f * thickness.getValue());
  311. g.setColour (Colours::purple.withAlpha (getAlpha()));
  312. g.strokePath (p, stroke, AffineTransform());
  313. }
  314. SlowerBouncingNumber points[2 + 4 * 8], thickness;
  315. };
  316. //==============================================================================
  317. class ImagesRenderingDemo final : public GraphicsDemoBase
  318. {
  319. public:
  320. ImagesRenderingDemo (ControllersComponent& cc, bool argb, bool tiled)
  321. : GraphicsDemoBase (cc, String ("Images") + (argb ? ": ARGB" : ": RGB") + (tiled ? " Tiled" : String() )),
  322. isArgb (argb), isTiled (tiled)
  323. {
  324. argbImage = getImageFromAssets ("juce_icon.png");
  325. rgbImage = getImageFromAssets ("portmeirion.jpg");
  326. }
  327. void drawDemo (Graphics& g) override
  328. {
  329. auto image = isArgb ? argbImage : rgbImage;
  330. AffineTransform transform (AffineTransform::translation ((float) (image.getWidth() / -2),
  331. (float) (image.getHeight() / -2))
  332. .followedBy (getTransform()));
  333. if (isTiled)
  334. {
  335. FillType fill (image, transform);
  336. fill.setOpacity (getAlpha());
  337. g.setFillType (fill);
  338. g.fillAll();
  339. }
  340. else
  341. {
  342. g.setOpacity (getAlpha());
  343. g.drawImageTransformed (image, transform, false);
  344. }
  345. }
  346. bool isArgb, isTiled;
  347. Image rgbImage, argbImage;
  348. };
  349. //==============================================================================
  350. class GlyphsDemo final : public GraphicsDemoBase
  351. {
  352. public:
  353. GlyphsDemo (ControllersComponent& cc)
  354. : GraphicsDemoBase (cc, "Glyphs")
  355. {
  356. glyphs.addFittedText ({ 20.0f }, "The Quick Brown Fox Jumped Over The Lazy Dog",
  357. -120, -50, 240, 100, Justification::centred, 2, 1.0f);
  358. }
  359. void drawDemo (Graphics& g) override
  360. {
  361. g.setColour (Colours::black.withAlpha (getAlpha()));
  362. glyphs.draw (g, getTransform());
  363. }
  364. GlyphArrangement glyphs;
  365. };
  366. //==============================================================================
  367. class SVGDemo final : public GraphicsDemoBase
  368. {
  369. public:
  370. SVGDemo (ControllersComponent& cc)
  371. : GraphicsDemoBase (cc, "SVG")
  372. {
  373. createSVGDrawable();
  374. }
  375. void drawDemo (Graphics& g) override
  376. {
  377. if (Time::getCurrentTime().toMilliseconds() > lastSVGLoadTime.toMilliseconds() + 2000)
  378. createSVGDrawable();
  379. svgDrawable->draw (g, getAlpha(), getTransform());
  380. }
  381. void createSVGDrawable()
  382. {
  383. lastSVGLoadTime = Time::getCurrentTime();
  384. ZipFile icons (createAssetInputStream ("icons.zip").release(), true);
  385. // Load a random SVG file from our embedded icons.zip file.
  386. const std::unique_ptr<InputStream> svgFileStream (icons.createStreamForEntry (Random::getSystemRandom().nextInt (icons.getNumEntries())));
  387. if (svgFileStream.get() != nullptr)
  388. {
  389. svgDrawable = Drawable::createFromImageDataStream (*svgFileStream);
  390. if (svgDrawable != nullptr)
  391. {
  392. // to make our icon the right size, we'll set its bounding box to the size and position that we want.
  393. if (auto comp = dynamic_cast<DrawableComposite*> (svgDrawable.get()))
  394. comp->setBoundingBox ({ -100.0f, -100.0f, 200.0f, 200.0f });
  395. }
  396. }
  397. }
  398. Time lastSVGLoadTime;
  399. std::unique_ptr<Drawable> svgDrawable;
  400. };
  401. //==============================================================================
  402. class LinesDemo final : public GraphicsDemoBase
  403. {
  404. public:
  405. LinesDemo (ControllersComponent& cc)
  406. : GraphicsDemoBase (cc, "Lines")
  407. {}
  408. void drawDemo (Graphics& g) override
  409. {
  410. {
  411. RectangleList<float> verticalLines;
  412. verticalLines.ensureStorageAllocated (getWidth());
  413. auto pos = offset.getValue();
  414. for (int x = 0; x < getWidth(); ++x)
  415. {
  416. auto y = (float) getHeight() * 0.3f;
  417. auto length = y * std::abs (std::sin ((float) x / 100.0f + 2.0f * pos));
  418. verticalLines.addWithoutMerging (Rectangle<float> ((float) x, y - length * 0.5f, 1.0f, length));
  419. }
  420. g.setColour (Colours::blue.withAlpha (getAlpha()));
  421. g.fillRectList (verticalLines);
  422. }
  423. {
  424. RectangleList<float> horizontalLines;
  425. horizontalLines.ensureStorageAllocated (getHeight());
  426. auto pos = offset.getValue();
  427. for (int y = 0; y < getHeight(); ++y)
  428. {
  429. auto x = (float) getWidth() * 0.3f;
  430. auto length = x * std::abs (std::sin ((float) y / 100.0f + 2.0f * pos));
  431. horizontalLines.addWithoutMerging (Rectangle<float> (x - length * 0.5f, (float) y, length, 1.0f));
  432. }
  433. g.setColour (Colours::green.withAlpha (getAlpha()));
  434. g.fillRectList (horizontalLines);
  435. }
  436. g.setColour (Colours::red.withAlpha (getAlpha()));
  437. auto w = (float) getWidth();
  438. auto h = (float) getHeight();
  439. g.drawLine (positions[0].getValue() * w,
  440. positions[1].getValue() * h,
  441. positions[2].getValue() * w,
  442. positions[3].getValue() * h);
  443. g.drawLine (positions[4].getValue() * w,
  444. positions[5].getValue() * h,
  445. positions[6].getValue() * w,
  446. positions[7].getValue() * h);
  447. }
  448. SlowerBouncingNumber offset, positions[8];
  449. };
  450. //==============================================================================
  451. class DemoHolderComponent final : public Component,
  452. private Timer
  453. {
  454. public:
  455. DemoHolderComponent()
  456. {
  457. setOpaque (true);
  458. }
  459. void paint (Graphics& g) override
  460. {
  461. g.fillCheckerBoard (getLocalBounds().toFloat(), 48.0f, 48.0f,
  462. Colours::lightgrey, Colours::white);
  463. }
  464. void timerCallback() override
  465. {
  466. if (currentDemo != nullptr)
  467. currentDemo->repaint();
  468. }
  469. void setDemo (GraphicsDemoBase* newDemo)
  470. {
  471. if (currentDemo != nullptr)
  472. removeChildComponent (currentDemo);
  473. currentDemo = newDemo;
  474. if (currentDemo != nullptr)
  475. {
  476. addAndMakeVisible (currentDemo);
  477. startTimerHz (60);
  478. resized();
  479. }
  480. }
  481. void resized() override
  482. {
  483. if (currentDemo != nullptr)
  484. currentDemo->setBounds (getLocalBounds());
  485. }
  486. private:
  487. GraphicsDemoBase* currentDemo = nullptr;
  488. };
  489. //==============================================================================
  490. class TestListComponent final : public Component,
  491. private ListBoxModel
  492. {
  493. public:
  494. TestListComponent (DemoHolderComponent& holder, ControllersComponent& controls)
  495. : demoHolder (holder)
  496. {
  497. demos.add (new PathsDemo (controls, false, true));
  498. demos.add (new PathsDemo (controls, true, false));
  499. demos.add (new PathsDemo (controls, false, false));
  500. demos.add (new RectangleFillTypesDemo (controls));
  501. demos.add (new StrokesDemo (controls));
  502. demos.add (new ImagesRenderingDemo (controls, false, false));
  503. demos.add (new ImagesRenderingDemo (controls, false, true));
  504. demos.add (new ImagesRenderingDemo (controls, true, false));
  505. demos.add (new ImagesRenderingDemo (controls, true, true));
  506. demos.add (new GlyphsDemo (controls));
  507. demos.add (new SVGDemo (controls));
  508. demos.add (new LinesDemo (controls));
  509. addAndMakeVisible (listBox);
  510. listBox.setTitle ("Test List");
  511. listBox.setModel (this);
  512. listBox.selectRow (0);
  513. }
  514. void resized() override
  515. {
  516. listBox.setBounds (getLocalBounds());
  517. }
  518. int getNumRows() override
  519. {
  520. return demos.size();
  521. }
  522. void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override
  523. {
  524. if (demos[rowNumber] == nullptr)
  525. return;
  526. if (rowIsSelected)
  527. g.fillAll (Colour::contrasting (findColour (ListBox::textColourId),
  528. findColour (ListBox::backgroundColourId)));
  529. g.setColour (findColour (ListBox::textColourId));
  530. g.setFont (14.0f);
  531. g.drawFittedText (getNameForRow (rowNumber), 8, 0, width - 10, height, Justification::centredLeft, 2);
  532. }
  533. String getNameForRow (int rowNumber) override
  534. {
  535. if (auto* demo = demos[rowNumber])
  536. return demo->getName();
  537. return {};
  538. }
  539. void selectedRowsChanged (int lastRowSelected) override
  540. {
  541. demoHolder.setDemo (demos [lastRowSelected]);
  542. }
  543. private:
  544. DemoHolderComponent& demoHolder;
  545. ListBox listBox;
  546. OwnedArray<GraphicsDemoBase> demos;
  547. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestListComponent)
  548. };
  549. //==============================================================================
  550. class GraphicsDemo final : public Component
  551. {
  552. public:
  553. GraphicsDemo()
  554. : testList (demoHolder, controllersComponent)
  555. {
  556. setOpaque (true);
  557. addAndMakeVisible (demoHolder);
  558. addAndMakeVisible (controllersComponent);
  559. addAndMakeVisible (performanceDisplay);
  560. addAndMakeVisible (testList);
  561. setSize (750, 750);
  562. }
  563. void paint (Graphics& g) override
  564. {
  565. g.fillAll (Colours::grey);
  566. }
  567. void resized() override
  568. {
  569. auto area = getLocalBounds();
  570. controllersComponent.setBounds (area.removeFromBottom (150));
  571. testList .setBounds (area.removeFromRight (150));
  572. demoHolder .setBounds (area);
  573. performanceDisplay .setBounds (area.removeFromTop (20).removeFromRight (100));
  574. }
  575. private:
  576. ControllersComponent controllersComponent;
  577. DemoHolderComponent demoHolder;
  578. Label performanceDisplay;
  579. TestListComponent testList;
  580. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GraphicsDemo)
  581. };