From a696e907e4fcbafeee3217d260bd03b4981c4161 Mon Sep 17 00:00:00 2001 From: jules Date: Fri, 2 Aug 2013 15:36:49 +0100 Subject: [PATCH] Had a look at the SplashScreen class, and realised it was total crap, so redesigned it to avoid using any modal loop nastiness, and to encourage initialisation on background threads. --- .../juce_gui_extra/misc/juce_SplashScreen.cpp | 77 ++++------ .../juce_gui_extra/misc/juce_SplashScreen.h | 144 ++++++++++-------- 2 files changed, 104 insertions(+), 117 deletions(-) diff --git a/modules/juce_gui_extra/misc/juce_SplashScreen.cpp b/modules/juce_gui_extra/misc/juce_SplashScreen.cpp index ffa0ffacf2..aba0437de3 100644 --- a/modules/juce_gui_extra/misc/juce_SplashScreen.cpp +++ b/modules/juce_gui_extra/misc/juce_SplashScreen.cpp @@ -22,71 +22,50 @@ ============================================================================== */ -SplashScreen::SplashScreen() - : originalClickCounter (0) +SplashScreen::SplashScreen (const String& title, const Image& image, bool useDropShadow) + : Component (title), + backgroundImage (image), + clickCountToDelete (0) { - setOpaque (true); + // You must supply a valid image here! + jassert (backgroundImage.isValid()); + + setOpaque (! backgroundImage.hasAlphaChannel()); + makeVisible (image.getWidth(), image.getHeight(), useDropShadow); } -SplashScreen::~SplashScreen() +SplashScreen::SplashScreen (const String& title, int width, int height, bool useDropShadow) + : Component (title), + clickCountToDelete (0) { + makeVisible (width, height, useDropShadow); } -//============================================================================== -void SplashScreen::show (const String& title, - const Image& backgroundImage_, - const int minimumTimeToDisplayFor, - const bool useDropShadow, - const bool removeOnMouseClick) +void SplashScreen::makeVisible (int w, int h, bool useDropShadow) { - backgroundImage = backgroundImage_; + clickCountToDelete = Desktop::getInstance().getMouseButtonClickCounter(); + creationTime = Time::getCurrentTime(); - jassert (backgroundImage_.isValid()); - - if (backgroundImage_.isValid()) - { - setOpaque (! backgroundImage_.hasAlphaChannel()); - - show (title, - backgroundImage_.getWidth(), - backgroundImage_.getHeight(), - minimumTimeToDisplayFor, - useDropShadow, - removeOnMouseClick); - } -} - -void SplashScreen::show (const String& title, - const int width, - const int height, - const int minimumTimeToDisplayFor, - const bool useDropShadow, - const bool removeOnMouseClick) -{ - setName (title); setAlwaysOnTop (true); setVisible (true); - centreWithSize (width, height); - + centreWithSize (w, h); addToDesktop (useDropShadow ? ComponentPeer::windowHasDropShadow : 0); toFront (false); +} - #if JUCE_MODAL_LOOPS_PERMITTED - MessageManager::getInstance()->runDispatchLoopUntil (300); - #endif +SplashScreen::~SplashScreen() {} - repaint(); +void SplashScreen::deleteAfterDelay (RelativeTime timeout, bool removeOnMouseClick) +{ + // Note that this method must be safe to call from non-GUI threads + if (! removeOnMouseClick) + clickCountToDelete = std::numeric_limits::max(); - originalClickCounter = removeOnMouseClick - ? Desktop::getInstance().getMouseButtonClickCounter() - : std::numeric_limits::max(); - - earliestTimeToDelete = Time::getCurrentTime() + RelativeTime::milliseconds (minimumTimeToDisplayFor); + minimumVisibleTime = timeout; startTimer (50); } -//============================================================================== void SplashScreen::paint (Graphics& g) { g.setOpacity (1.0f); @@ -98,9 +77,7 @@ void SplashScreen::paint (Graphics& g) void SplashScreen::timerCallback() { - if (Time::getCurrentTime() > earliestTimeToDelete - || Desktop::getInstance().getMouseButtonClickCounter() != originalClickCounter) - { + if (Time::getCurrentTime() > creationTime + minimumVisibleTime + || Desktop::getInstance().getMouseButtonClickCounter() > clickCountToDelete) delete this; - } } diff --git a/modules/juce_gui_extra/misc/juce_SplashScreen.h b/modules/juce_gui_extra/misc/juce_SplashScreen.h index 7063836c7e..aa9db7929a 100644 --- a/modules/juce_gui_extra/misc/juce_SplashScreen.h +++ b/modules/juce_gui_extra/misc/juce_SplashScreen.h @@ -29,24 +29,40 @@ //============================================================================== /** A component for showing a splash screen while your app starts up. - This will automatically position itself, and delete itself when the app has - finished initialising (it uses the JUCEApplication::isInitialising() to detect - this). + This will automatically position itself, and can be told to delete itself after + being on-screen for a minimum length of time. To use it, just create one of these in your JUCEApplication::initialise() method, - call its show() method and let the object delete itself later. + and when your initialisation tasks have finished running, call its deleteAfterDelay() + method to make it automatically get rid of itself. + + Note that although you could call deleteAfterDelay() as soon as you create the + SplashScreen object, if you've got a long initialisation procedure, you probably + don't want the splash to time-out and disappear before the initialisation has + finished, which is why it makes sense to not call this method until the end of + your init tasks. E.g. @code void MyApp::initialise (const String& commandLine) { - SplashScreen* splash = new SplashScreen(); + splash = new SplashScreen ("Welcome to my app!", + ImageFileFormat::loadFrom (File ("/foobar/splash.jpg")), + true); - splash->show ("welcome to my app", - ImageCache::getFromFile (File ("/foobar/splash.jpg")), - 4000, false); + // now kick off your initialisation work on some kind of thread or task, and + launchBackgroundInitialisationThread(); + } - .. no need to delete the splash screen - it'll do that itself. + void MyApp::myInitialisationWorkFinished() + { + // ..assuming this is some kind of callback method that is triggered when + // your background initialisation threads have finished, and it's time to open + // your main window, etc.. + + splash->deleteAfterDelay (RelativeTime::seconds (4), false); + + ...etc... } @endcode @@ -59,84 +75,78 @@ public: //============================================================================== /** Creates a SplashScreen object. - After creating one of these (or your subclass of it), call one of the show() - methods to display it. - */ - SplashScreen(); + When called, the constructor will position the SplashScreen in the centre of the + display, and after the time specified, it will automatically delete itself. - /** Destructor. */ - ~SplashScreen(); + Bear in mind that if you call this during your JUCEApplication::initialise() + method and then block the message thread by performing some kind of task, then + obviously neither your splash screen or any other GUI won't appear until you + allow the message thread to resume and do its work. So if you have time-consuming + tasks to do during startup, use a background thread for them. - //============================================================================== - /** Creates a SplashScreen object that will display an image. + After creating one of these (or your subclass of it), you should do your app's + initialisation work, and then call the deleteAfterDelay() method to tell this object + to delete itself after the user has had chance to get a good look at it. - As soon as this is called, the SplashScreen will be displayed in the centre of the - screen. This method will also dispatch any pending messages to make sure that when - it returns, the splash screen has been completely drawn, and your initialisation - code can carry on. + If you're writing a custom splash screen class, there's another protected constructor + that your subclass can call, which doesn't take an image. @param title the name to give the component @param backgroundImage an image to draw on the component. The component's size will be set to the size of this image, and if the image is - semi-transparent, the component will be made semi-transparent - too. This image will be deleted (or released from the ImageCache - if that's how it was created) by the splash screen object when - it is itself deleted. - @param minimumTimeToDisplayFor how long (in milliseconds) the splash screen - should stay visible for. If the initialisation takes longer than - this time, the splash screen will wait for it to finish before - disappearing, but if initialisation is very quick, this lets - you make sure that people get a good look at your splash. + semi-transparent, the component will be made non-opaque @param useDropShadow if true, the window will have a drop shadow - @param removeOnMouseClick if true, the window will go away as soon as the user clicks + + */ + SplashScreen (const String& title, + const Image& backgroundImage, + bool useDropShadow); + + /** Destructor. */ + ~SplashScreen(); + + /** Tells the component to auto-delete itself after a timeout period, or when the + mouse is clicked. + + You should call this after finishing your app's initialisation work. + + Note that although you could call deleteAfterDelay() as soon as you create the + SplashScreen object, if you've got a long initialisation procedure, you probably + don't want the splash to time-out and disappear before your initialisation has + finished, which is why it makes sense to not call this method and start the + self-delete timer until you're ready. + + It's safe to call this method from a non-GUI thread as long as there's no danger that + the object may be being deleted at the same time. + + @param minimumTotalTimeToDisplayFor how long the splash screen should stay visible for. + Note that this time is measured from the construction-time of this + object, not from the time that the deleteAfterDelay() method is + called, so if you call this method after a long initialisation + period, it may be deleted without any further delay. + @param removeOnMouseClick if true, the window will be deleted as soon as the user clicks the mouse (anywhere) */ - void show (const String& title, - const Image& backgroundImage, - int minimumTimeToDisplayFor, - bool useDropShadow, - bool removeOnMouseClick = true); - - /** Creates a SplashScreen object with a specified size. - - For a custom splash screen, you can use this method to display it at a certain size - and then override the paint() method yourself to do whatever's necessary. - - As soon as this is called, the SplashScreen will be displayed in the centre of the - screen. This method will also dispatch any pending messages to make sure that when - it returns, the splash screen has been completely drawn, and your initialisation - code can carry on. - - @param title the name to give the component - @param width the width to use - @param height the height to use - @param minimumTimeToDisplayFor how long (in milliseconds) the splash screen - should stay visible for. If the initialisation takes longer than - this time, the splash screen will wait for it to finish before - disappearing, but if initialisation is very quick, this lets - you make sure that people get a good look at your splash. - @param useDropShadow if true, the window will have a drop shadow - @param removeOnMouseClick if true, the window will go away as soon as the user clicks - the mouse (anywhere) - */ - void show (const String& title, - int width, - int height, - int minimumTimeToDisplayFor, - bool useDropShadow, - bool removeOnMouseClick = true); + void deleteAfterDelay (RelativeTime minimumTotalTimeToDisplayFor, + bool removeOnMouseClick); +protected: //============================================================================== + /** This constructor is for use by custom sub-classes that don't want to provide an image. */ + SplashScreen (const String& title, int width, int height, bool useDropShadow); + /** @internal */ void paint (Graphics&) override; private: //============================================================================== Image backgroundImage; - Time earliestTimeToDelete; - int originalClickCounter; + Time creationTime; + RelativeTime minimumVisibleTime; + int clickCountToDelete; void timerCallback() override; + void makeVisible (int w, int h, bool shadow); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SplashScreen) };