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: TimersAndEventsDemo 27 version: 1.0.0 28 vendor: JUCE 29 website: http://juce.com 30 description: Application using timers and events. 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: TimersAndEventsDemo 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 /** Simple message that holds a Colour. */ 53 struct ColourMessage : public Message 54 { ColourMessageColourMessage55 ColourMessage (Colour col) : colour (col) {} 56 57 /** Returns the colour of a ColourMessage of white if the message is not a ColourMessage. */ getColourColourMessage58 static Colour getColour (const Message& message) 59 { 60 if (auto* cm = dynamic_cast<const ColourMessage*> (&message)) 61 return cm->colour; 62 63 return Colours::white; 64 } 65 66 Colour colour; 67 68 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourMessage) 69 }; 70 71 //============================================================================== 72 /** Simple component that can be triggered to flash. 73 The flash will then fade using a Timer to repaint itself and will send a change 74 message once it is finished. 75 */ 76 class FlashingComponent : public Component, 77 public MessageListener, 78 public ChangeBroadcaster, 79 private Timer 80 { 81 public: FlashingComponent()82 FlashingComponent() {} 83 startFlashing()84 void startFlashing() 85 { 86 flashAlpha = 1.0f; 87 startTimerHz (25); 88 } 89 90 /** Stops this component flashing without sending a change message. */ stopFlashing()91 void stopFlashing() 92 { 93 flashAlpha = 0.0f; 94 stopTimer(); 95 repaint(); 96 } 97 98 /** Sets the colour of the component. */ setFlashColour(const Colour newColour)99 void setFlashColour (const Colour newColour) 100 { 101 colour = newColour; 102 repaint(); 103 } 104 105 /** Draws our component. */ paint(Graphics & g)106 void paint (Graphics& g) override 107 { 108 g.setColour (colour.overlaidWith (Colours::white.withAlpha (flashAlpha))); 109 g.fillEllipse (getLocalBounds().toFloat()); 110 } 111 112 /** Custom mouse handler to trigger a flash. */ mouseDown(const MouseEvent &)113 void mouseDown (const MouseEvent&) override 114 { 115 startFlashing(); 116 } 117 118 /** Message listener callback used to change our colour */ handleMessage(const Message & message)119 void handleMessage (const Message& message) override 120 { 121 setFlashColour (ColourMessage::getColour (message)); 122 } 123 124 private: 125 float flashAlpha = 0.0f; 126 Colour colour { Colours::red }; 127 timerCallback()128 void timerCallback() override 129 { 130 // Reduce the alpha level of the flash slightly so it fades out 131 flashAlpha -= 0.075f; 132 133 if (flashAlpha < 0.05f) 134 { 135 stopFlashing(); 136 sendChangeMessage(); 137 // Once we've finished flashing send a change message to trigger the next component to flash 138 } 139 140 repaint(); 141 } 142 143 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlashingComponent) 144 }; 145 146 //============================================================================== 147 class TimersAndEventsDemo : public Component, 148 private ChangeListener 149 { 150 public: TimersAndEventsDemo()151 TimersAndEventsDemo() 152 { 153 setOpaque (true); 154 155 // Create and add our FlashingComponents with some random colours and sizes 156 for (int i = 0; i < numFlashingComponents; ++i) 157 { 158 auto* newFlasher = new FlashingComponent(); 159 flashingComponents.add (newFlasher); 160 161 newFlasher->setFlashColour (getRandomBrightColour()); 162 newFlasher->addChangeListener (this); 163 164 auto diameter = 25 + random.nextInt (75); 165 newFlasher->setSize (diameter, diameter); 166 167 addAndMakeVisible (newFlasher); 168 } 169 170 addAndMakeVisible (stopButton); 171 stopButton.onClick = [this] { stopButtonClicked(); }; 172 173 addAndMakeVisible (randomColourButton); 174 randomColourButton.onClick = [this] { randomColourButtonClicked(); }; 175 176 // lay out our components in a pseudo random grid 177 Rectangle<int> area (0, 100, 150, 150); 178 179 for (auto* comp : flashingComponents) 180 { 181 auto buttonArea = area.withSize (comp->getWidth(), comp->getHeight()); 182 buttonArea.translate (random.nextInt (area.getWidth() - comp->getWidth()), 183 random.nextInt (area.getHeight() - comp->getHeight())); 184 comp->setBounds (buttonArea); 185 186 area.translate (area.getWidth(), 0); 187 188 // if we go off the right start a new row 189 if (area.getRight() > (800 - area.getWidth())) 190 { 191 area.translate (0, area.getWidth()); 192 area.setX (0); 193 } 194 } 195 196 setSize (600, 600); 197 } 198 ~TimersAndEventsDemo()199 ~TimersAndEventsDemo() override 200 { 201 for (auto* fc : flashingComponents) 202 fc->removeChangeListener (this); 203 } 204 paint(Graphics & g)205 void paint (Graphics& g) override 206 { 207 g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground, 208 Colours::darkgrey)); 209 } 210 paintOverChildren(Graphics & g)211 void paintOverChildren (Graphics& g) override 212 { 213 auto explanationArea = getLocalBounds().removeFromTop (100); 214 215 AttributedString s; 216 s.append ("Click on a circle to make it flash. When it has finished flashing it will send a message which causes the next circle to flash"); 217 s.append (newLine); 218 s.append ("Click the \"Set Random Colour\" button to change the colour of one of the circles."); 219 s.append (newLine); 220 s.setFont (16.0f); 221 s.setColour (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::defaultText, Colours::lightgrey)); 222 s.draw (g, explanationArea.reduced (10).toFloat()); 223 } 224 resized()225 void resized() override 226 { 227 auto area = getLocalBounds().removeFromBottom (40); 228 randomColourButton.setBounds (area.removeFromLeft (166) .reduced (8)); 229 stopButton .setBounds (area.removeFromRight (166).reduced (8)); 230 } 231 232 private: 233 enum { numFlashingComponents = 9 }; 234 235 OwnedArray<FlashingComponent> flashingComponents; 236 TextButton randomColourButton { "Set Random Colour" }, 237 stopButton { "Stop" }; 238 Random random; 239 changeListenerCallback(ChangeBroadcaster * source)240 void changeListenerCallback (ChangeBroadcaster* source) override 241 { 242 for (int i = 0; i < flashingComponents.size(); ++i) 243 if (source == flashingComponents.getUnchecked (i)) 244 flashingComponents.getUnchecked ((i + 1) % flashingComponents.size())->startFlashing(); 245 } 246 randomColourButtonClicked()247 void randomColourButtonClicked() 248 { 249 // Here we post a new ColourMessage with a random colour to a random flashing component. 250 // This will send a message to the component asynchronously and trigger its handleMessage callback 251 flashingComponents.getUnchecked (random.nextInt (flashingComponents.size()))->postMessage (new ColourMessage (getRandomBrightColour())); 252 } 253 stopButtonClicked()254 void stopButtonClicked() 255 { 256 for (auto* fc : flashingComponents) 257 fc->stopFlashing(); 258 } 259 260 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimersAndEventsDemo) 261 }; 262