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