From d7cea0510409f5b2f9a800083580328a21a74cce Mon Sep 17 00:00:00 2001 From: jules Date: Wed, 29 Aug 2012 11:01:47 +0100 Subject: [PATCH] Added a new struct DialogWindow::LaunchOptions, to provide a better mechanism than the existing static methods for launching dialog boxes. I've left the old static methods in there but will probably deprecate them at some point in the future. Also added a couple of methods to OptionalOwnedPointer. --- .../Source/Application/jucer_JuceUpdater.cpp | 35 ++--- .../Source/Utility/jucer_FileHelpers.cpp | 6 +- .../Source/Utility/jucer_RelativePath.h | 4 +- extras/JuceDemo/Source/demos/WidgetsDemo.cpp | 39 ++---- .../Source/MainHostWindow.cpp | 15 ++- .../the jucer/src/ui/jucer_TestComponent.cpp | 15 ++- .../Standalone/juce_StandaloneFilterWindow.h | 23 ++-- .../memory/juce_OptionalScopedPointer.h | 12 ++ .../windows/juce_DialogWindow.cpp | 120 ++++++++++++------ .../windows/juce_DialogWindow.h | 98 ++++++++++++-- .../misc/juce_PreferencesPanel.cpp | 11 +- 11 files changed, 253 insertions(+), 125 deletions(-) diff --git a/extras/Introjucer/Source/Application/jucer_JuceUpdater.cpp b/extras/Introjucer/Source/Application/jucer_JuceUpdater.cpp index 7e9955c6b5..808f5d55a4 100644 --- a/extras/Introjucer/Source/Application/jucer_JuceUpdater.cpp +++ b/extras/Introjucer/Source/Application/jucer_JuceUpdater.cpp @@ -76,33 +76,18 @@ JuceUpdater::~JuceUpdater() filenameComp.removeListener (this); } -//============================================================================== -class UpdateDialogWindow : public DialogWindow -{ -public: - UpdateDialogWindow (JuceUpdater* updater, Component* componentToCentreAround) - : DialogWindow ("JUCE Module Updater", - Colours::lightgrey, true, true) - { - setUsingNativeTitleBar (true); - setContentOwned (updater, true); - centreAroundComponent (componentToCentreAround, getWidth(), getHeight()); - setResizable (true, true); - } - - void closeButtonPressed() - { - setVisible (false); - } - -private: - JUCE_DECLARE_NON_COPYABLE (UpdateDialogWindow); -}; - void JuceUpdater::show (ModuleList& moduleList, Component* mainWindow, const String& message) { - UpdateDialogWindow w (new JuceUpdater (moduleList, message), mainWindow); - w.runModalLoop(); + DialogWindow::LaunchOptions o; + o.content.setOwned (new JuceUpdater (moduleList, message)); + o.dialogTitle = "JUCE Module Updater"; + o.dialogBackgroundColour = Colours::lightgrey; + o.componentToCentreAround = mainWindow; + o.escapeKeyTriggersCloseButton = true; + o.useNativeTitleBar = true; + o.resizable = true; + + o.runModal(); } void JuceUpdater::resized() diff --git a/extras/Introjucer/Source/Utility/jucer_FileHelpers.cpp b/extras/Introjucer/Source/Utility/jucer_FileHelpers.cpp index 36f960bd2f..f121643d1e 100644 --- a/extras/Introjucer/Source/Utility/jucer_FileHelpers.cpp +++ b/extras/Introjucer/Source/Utility/jucer_FileHelpers.cpp @@ -127,13 +127,13 @@ namespace FileHelpers String appendPath (const String& path, const String& subpath) { if (isAbsolutePath (subpath)) - return subpath.replaceCharacter ('\\', '/'); + return unixStylePath (subpath); - String path1 (path.replaceCharacter ('\\', '/')); + String path1 (unixStylePath (path)); if (! path1.endsWithChar ('/')) path1 << '/'; - return path1 + subpath.replaceCharacter ('\\', '/'); + return path1 + unixStylePath (subpath); } bool shouldPathsBeRelative (String path1, String path2) diff --git a/extras/Introjucer/Source/Utility/jucer_RelativePath.h b/extras/Introjucer/Source/Utility/jucer_RelativePath.h index 305f40fed4..481bac02a0 100644 --- a/extras/Introjucer/Source/Utility/jucer_RelativePath.h +++ b/extras/Introjucer/Source/Utility/jucer_RelativePath.h @@ -48,12 +48,12 @@ public: {} RelativePath (const String& path_, const RootFolder root_) - : path (path_.replaceCharacter ('\\', '/')), root (root_) + : path (FileHelpers::unixStylePath (path_)), root (root_) { } RelativePath (const File& file, const File& rootFolder, const RootFolder root_) - : path (FileHelpers::getRelativePathFrom (file, rootFolder).replaceCharacter ('\\', '/')), root (root_) + : path (FileHelpers::unixStylePath (FileHelpers::getRelativePathFrom (file, rootFolder))), root (root_) { } diff --git a/extras/JuceDemo/Source/demos/WidgetsDemo.cpp b/extras/JuceDemo/Source/demos/WidgetsDemo.cpp index 9ca6b66c61..822dd4cebe 100644 --- a/extras/JuceDemo/Source/demos/WidgetsDemo.cpp +++ b/extras/JuceDemo/Source/demos/WidgetsDemo.cpp @@ -995,28 +995,6 @@ public: } }; -//============================================================================== -/** A DialogWindow containing a ColourSelector component */ -class ColourSelectorDialogWindow : public DialogWindow -{ -public: - ColourSelectorDialogWindow() - : DialogWindow ("Colour selector demo", Colours::lightgrey, true) - { - setContentOwned (new ColourSelector(), false); - centreWithSize (400, 400); - setResizable (true, true); - } - - void closeButtonPressed() - { - // we expect this component to be run within a modal loop, so when the close - // button is clicked, we can make it invisible to cause the loop to exit and the - // calling code will delete this object. - setVisible (false); - } -}; - #if JUCE_MAC //============================================================================== @@ -1287,13 +1265,18 @@ public: } else if (result == 120) { - #if JUCE_MODAL_LOOPS_PERMITTED - ColourSelectorDialogWindow colourDialog; + DialogWindow::LaunchOptions o; - // this will run an event loop until the dialog's closeButtonPressed() - // method causes the loop to exit. - colourDialog.runModalLoop(); - #endif + o.content.setOwned (new ColourSelector()); + o.content->setSize (400, 400); + + o.dialogTitle = "Colour Selector Demo"; + o.dialogBackgroundColour = Colours::grey; + o.escapeKeyTriggersCloseButton = true; + o.useNativeTitleBar = true; + o.resizable = true; + + o.launchAsync(); } else if (result == 140) { diff --git a/extras/audio plugin host/Source/MainHostWindow.cpp b/extras/audio plugin host/Source/MainHostWindow.cpp index 2d8d56ea4f..b6823e0035 100644 --- a/extras/audio plugin host/Source/MainHostWindow.cpp +++ b/extras/audio plugin host/Source/MainHostWindow.cpp @@ -411,11 +411,16 @@ void MainHostWindow::showAudioSettings() audioSettingsComp.setSize (500, 450); - DialogWindow::showModalDialog ("Audio Settings", - &audioSettingsComp, - this, - Colours::azure, - true); + DialogWindow::LaunchOptions o; + o.content.setNonOwned (&audioSettingsComp); + o.dialogTitle = "Audio Settings"; + o.componentToCentreAround = this; + o.dialogBackgroundColour = Colours::azure; + o.escapeKeyTriggersCloseButton = true; + o.useNativeTitleBar = false; + o.resizable = false; + + o.runModal(); ScopedPointer audioState (deviceManager.createStateXml()); diff --git a/extras/the jucer/src/ui/jucer_TestComponent.cpp b/extras/the jucer/src/ui/jucer_TestComponent.cpp index 8ded3fd1d5..fc376bf1fb 100644 --- a/extras/the jucer/src/ui/jucer_TestComponent.cpp +++ b/extras/the jucer/src/ui/jucer_TestComponent.cpp @@ -173,12 +173,13 @@ void TestComponent::resized() //============================================================================== void TestComponent::showInDialogBox (JucerDocument& document) { - TooltipWindow tooltipWindow (0, 400); + DialogWindow::LaunchOptions o; + o.content.setOwned (new TestComponent (nullptr, document.createCopy(), true)); + o.dialogTitle = "Testing: " + document.getClassName(); + o.dialogBackgroundColour = Colours::azure; + o.escapeKeyTriggersCloseButton = true; + o.useNativeTitleBar = false; + o.resizable = true; - TestComponent testComp (0, document.createCopy(), true); - - DialogWindow::showModalDialog ("Testing: " + document.getClassName(), - &testComp, 0, - Colours::azure, - true, true); + o.launchAsync(); } diff --git a/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h b/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h index 895750af2c..4d287e2dfa 100644 --- a/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h +++ b/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h @@ -223,17 +223,22 @@ public: /** Shows the audio properties dialog box modally. */ virtual void showAudioSettingsDialog() { - AudioDeviceSelectorComponent selectorComp (*deviceManager, - filter->getNumInputChannels(), - filter->getNumInputChannels(), - filter->getNumOutputChannels(), - filter->getNumOutputChannels(), - true, false, true, false); + DialogWindow::LaunchOptions o; + o.content.setOwned (new AudioDeviceSelectorComponent (*deviceManager, + filter->getNumInputChannels(), + filter->getNumInputChannels(), + filter->getNumOutputChannels(), + filter->getNumOutputChannels(), + true, false, true, false)); + o.content->setSize (500, 450); - selectorComp.setSize (500, 450); + o.dialogTitle = TRANS("Audio Settings"); + o.dialogBackgroundColour = Colours::lightgrey; + o.escapeKeyTriggersCloseButton = true; + o.useNativeTitleBar = true; + o.resizable = false; - DialogWindow::showModalDialog (TRANS("Audio Settings"), &selectorComp, this, - Colours::lightgrey, true, false, false); + o.launchAsync(); } //============================================================================== diff --git a/modules/juce_core/memory/juce_OptionalScopedPointer.h b/modules/juce_core/memory/juce_OptionalScopedPointer.h index 3a665656a2..55d23470ca 100644 --- a/modules/juce_core/memory/juce_OptionalScopedPointer.h +++ b/modules/juce_core/memory/juce_OptionalScopedPointer.h @@ -148,6 +148,18 @@ public: shouldDelete = takeOwnership; } + /** Makes this OptionalScopedPointer point at a new object, and take ownership of that object. */ + void setOwned (ObjectType* newObject) + { + set (newObject, true); + } + + /** Makes this OptionalScopedPointer point at a new object, but will not take ownership of that object. */ + void setNonOwned (ObjectType* newObject) + { + set (newObject, false); + } + /** Returns true if the target object will be deleted when this pointer object is deleted. */ diff --git a/modules/juce_gui_basics/windows/juce_DialogWindow.cpp b/modules/juce_gui_basics/windows/juce_DialogWindow.cpp index 857fcaf5c6..cfa4d6085f 100644 --- a/modules/juce_gui_basics/windows/juce_DialogWindow.cpp +++ b/modules/juce_gui_basics/windows/juce_DialogWindow.cpp @@ -23,12 +23,10 @@ ============================================================================== */ -DialogWindow::DialogWindow (const String& name, - const Colour& backgroundColour_, - const bool escapeKeyTriggersCloseButton_, - const bool addToDesktop_) - : DocumentWindow (name, backgroundColour_, DocumentWindow::closeButton, addToDesktop_), - escapeKeyTriggersCloseButton (escapeKeyTriggersCloseButton_) +DialogWindow::DialogWindow (const String& name, const Colour& colour, + const bool escapeCloses, const bool onDesktop) + : DocumentWindow (name, colour, DocumentWindow::closeButton, onDesktop), + escapeKeyTriggersCloseButton (escapeCloses) { } @@ -36,40 +34,53 @@ DialogWindow::~DialogWindow() { } -//============================================================================== +bool DialogWindow::keyPressed (const KeyPress& key) +{ + if (escapeKeyTriggersCloseButton && key == KeyPress::escapeKey) + { + setVisible (false); + return true; + } + + return DocumentWindow::keyPressed (key); +} + void DialogWindow::resized() { DocumentWindow::resized(); - const KeyPress esc (KeyPress::escapeKey, 0, 0); - - if (escapeKeyTriggersCloseButton - && getCloseButton() != nullptr - && ! getCloseButton()->isRegisteredForShortcut (esc)) + if (escapeKeyTriggersCloseButton) { - getCloseButton()->addShortcut (esc); + if (Button* const close = getCloseButton()) + { + const KeyPress esc (KeyPress::escapeKey, 0, 0); + + if (! close->isRegisteredForShortcut (esc)) + close->addShortcut (esc); + } } } //============================================================================== -class TempDialogWindow : public DialogWindow +class DefaultDialogWindow : public DialogWindow { public: - TempDialogWindow (const String& title, - Component* contentComponent_, - Component* componentToCentreAround, - const Colour& colour, - const bool escapeKeyTriggersCloseButton_, - const bool shouldBeResizable, - const bool useBottomRightCornerResizer) - : DialogWindow (title, colour, escapeKeyTriggersCloseButton_, true) + DefaultDialogWindow (LaunchOptions& options) + : DialogWindow (options.dialogTitle, options.dialogBackgroundColour, + options.escapeKeyTriggersCloseButton, true) { + setUsingNativeTitleBar (options.useNativeTitleBar); + if (! JUCEApplication::isStandaloneApp()) setAlwaysOnTop (true); // for a plugin, make it always-on-top because the host windows are often top-level - setContentNonOwned (contentComponent_, true); - centreAroundComponent (componentToCentreAround, getWidth(), getHeight()); - setResizable (shouldBeResizable, useBottomRightCornerResizer); + if (options.content.willDeleteObject()) + setContentOwned (options.content.release(), true); + else + setContentNonOwned (options.content.release(), true); + + centreAroundComponent (options.componentToCentreAround, getWidth(), getHeight()); + setResizable (options.resizable, options.useBottomRightCornerResizer); } void closeButtonPressed() @@ -78,9 +89,34 @@ public: } private: - JUCE_DECLARE_NON_COPYABLE (TempDialogWindow); + JUCE_DECLARE_NON_COPYABLE (DefaultDialogWindow); }; +DialogWindow::LaunchOptions::LaunchOptions() noexcept + : dialogBackgroundColour (Colours::lightgrey), + componentToCentreAround (nullptr), + escapeKeyTriggersCloseButton (true), + useNativeTitleBar (true), + resizable (true), + useBottomRightCornerResizer (false) +{ +} + +DialogWindow* DialogWindow::LaunchOptions::launchAsync() +{ + jassert (content != nullptr); // You need to provide some kind of content for the dialog! + + DefaultDialogWindow* const d = new DefaultDialogWindow (*this); + d->enterModalState (true, nullptr, true); + return d; +} + +#if JUCE_MODAL_LOOPS_PERMITTED || DOXYGEN +int DialogWindow::LaunchOptions::runModal() +{ + return launchAsync()->runModalLoop(); +} +#endif //============================================================================== void DialogWindow::showDialog (const String& dialogTitle, @@ -88,14 +124,20 @@ void DialogWindow::showDialog (const String& dialogTitle, Component* const componentToCentreAround, const Colour& backgroundColour, const bool escapeKeyTriggersCloseButton, - const bool shouldBeResizable, + const bool resizable, const bool useBottomRightCornerResizer) { - TempDialogWindow* dw = new TempDialogWindow (dialogTitle, contentComponent, componentToCentreAround, - backgroundColour, escapeKeyTriggersCloseButton, - shouldBeResizable, useBottomRightCornerResizer); + LaunchOptions o; + o.dialogTitle = dialogTitle; + o.content.setNonOwned (contentComponent); + o.componentToCentreAround = componentToCentreAround; + o.dialogBackgroundColour = backgroundColour; + o.escapeKeyTriggersCloseButton = escapeKeyTriggersCloseButton; + o.useNativeTitleBar = false; + o.resizable = resizable; + o.useBottomRightCornerResizer = useBottomRightCornerResizer; - dw->enterModalState (true, 0, true); + o.launchAsync(); } #if JUCE_MODAL_LOOPS_PERMITTED @@ -104,13 +146,19 @@ int DialogWindow::showModalDialog (const String& dialogTitle, Component* const componentToCentreAround, const Colour& backgroundColour, const bool escapeKeyTriggersCloseButton, - const bool shouldBeResizable, + const bool resizable, const bool useBottomRightCornerResizer) { - TempDialogWindow dw (dialogTitle, contentComponent, componentToCentreAround, - backgroundColour, escapeKeyTriggersCloseButton, - shouldBeResizable, useBottomRightCornerResizer); + LaunchOptions o; + o.dialogTitle = dialogTitle; + o.content.setNonOwned (contentComponent); + o.componentToCentreAround = componentToCentreAround; + o.dialogBackgroundColour = backgroundColour; + o.escapeKeyTriggersCloseButton = escapeKeyTriggersCloseButton; + o.useNativeTitleBar = false; + o.resizable = resizable; + o.useBottomRightCornerResizer = useBottomRightCornerResizer; - return dw.runModalLoop(); + return o.runModal(); } #endif diff --git a/modules/juce_gui_basics/windows/juce_DialogWindow.h b/modules/juce_gui_basics/windows/juce_DialogWindow.h index b4c36e8984..47f7f85b3d 100644 --- a/modules/juce_gui_basics/windows/juce_DialogWindow.h +++ b/modules/juce_gui_basics/windows/juce_DialogWindow.h @@ -27,7 +27,6 @@ #define __JUCE_DIALOGWINDOW_JUCEHEADER__ #include "juce_DocumentWindow.h" -#include "../buttons/juce_Button.h" //============================================================================== @@ -40,13 +39,13 @@ Any of the methods available to a DocumentWindow or ResizableWindow are also available to this, so it can be made resizable, have a menu bar, etc. - To add items to the box, see the ResizableWindow::setContentOwned() or - ResizableWindow::setContentNonOwned() methods. Don't add components directly to this - class - always put them in a content component! + You can either override or use an instance of the DialogWindow class directly, + or you can use a DialogWindow::LaunchOptions structure to quickly set up and + launch a box containing a content component. - You'll need to override the DocumentWindow::closeButtonPressed() method to handle - the user clicking the close button - for more info, see the DocumentWindow - help. + If you use the class directly, you'll need to override the + DocumentWindow::closeButtonPressed() method to handle the user clicking the close + button - for more info, see the DocumentWindow help. @see DocumentWindow, ResizableWindow */ @@ -75,9 +74,83 @@ public: */ ~DialogWindow(); + //============================================================================== + /** This class defines a collection of settings to be used to open a DialogWindow. + + The easiest way to open a DialogWindow is to create yourself a LaunchOptions structure, + initialise its fields with the appropriate details, and then call its launchAsync() + method to launch the dialog. + */ + struct LaunchOptions + { + LaunchOptions() noexcept; + + /** The title to give the window. */ + String dialogTitle; + + /** The background colour for the window. */ + Colour dialogBackgroundColour; + + /** The content component to show in the window. This must not be null! + Using an OptionalScopedPointer to hold this pointer lets you indicate whether + you'd like the dialog to automatically delete the component when the dialog + has terminated. + */ + OptionalScopedPointer content; + + /** If this is not a nullptr, it indicates a component that you'd like to position this + dialog box in front of. See the DocumentWindow::centreAroundComponent() method for + more info about this parameter. + */ + Component* componentToCentreAround; + + /** If true, then the escape key will trigger the dialog's close button. */ + bool escapeKeyTriggersCloseButton; + /** If true, the dialog will use a native title bar. See TopLevelWindow::setUsingNativeTitleBar() */ + bool useNativeTitleBar; + /** If true, the window will be resizable. See ResizableWindow::setResizable() */ + bool resizable; + /** Indicates whether to use a border or corner resizer component. See ResizableWindow::setResizable() */ + bool useBottomRightCornerResizer; + + /** Launches a new modal dialog window. + This will create a dialog based on the settings in this structure, + launch it modally, and return immediately. The window that is returned + will be automatically deleted when the modal state is terminated. + + When the dialog's close button is clicked, it'll automatically terminate its + modal state, but you can also do this programatically by calling + exitModalState (returnValue) on the DialogWindow. + + If your content component needs to find the dialog window that it is + contained in, a quick trick is to do this: + @code + Dialogwindow* dw = contentComponent->findParentComponentOfClass(); + + if (dw != nullptr) + dw->exitModalState (1234); + @endcode + */ + DialogWindow* launchAsync(); + + #if JUCE_MODAL_LOOPS_PERMITTED || DOXYGEN + /** Launches and runs the dialog modally, returning the status code that was + used to terminate the modal loop. + + Note that running modal loops inline is a BAD technique. If possible, always + use launchAsync() instead of this method. + */ + int runModal(); + #endif + }; + //============================================================================== /** Easy way of quickly showing a dialog box containing a given component. + Note: this method has been superceded by the DialogWindow::LaunchOptions structure, + which does the same job with some extra flexibility. The showDialog method is here + for backwards compatibility, but please use DialogWindow::LaunchOptions in new code. + This will open and display a DialogWindow containing a given component, making it modal, but returning immediately to allow the dialog to finish in its own time. If you want to block and run a modal loop until the dialog is dismissed, use showModalDialog() @@ -119,8 +192,13 @@ public: bool shouldBeResizable = false, bool useBottomRightCornerResizer = false); + #if JUCE_MODAL_LOOPS_PERMITTED || DOXYGEN /** Easy way of quickly showing a dialog box containing a given component. + Note: this method has been superceded by the DialogWindow::LaunchOptions structure, + which does the same job with some extra flexibility. The showDialog method is here + for backwards compatibility, but please use DialogWindow::LaunchOptions in new code. + This will open and display a DialogWindow containing a given component, returning when the user clicks its close button. @@ -154,7 +232,6 @@ public: @param useBottomRightCornerResizer if shouldBeResizable is true, this indicates whether to use a border or corner resizer component. See ResizableWindow::setResizable() */ - #if JUCE_MODAL_LOOPS_PERMITTED || DOXYGEN static int showModalDialog (const String& dialogTitle, Component* contentComponent, Component* componentToCentreAround, @@ -164,12 +241,15 @@ public: bool useBottomRightCornerResizer = false); #endif + protected: + //============================================================================== /** @internal */ void resized(); + /** @internal */ + bool keyPressed (const KeyPress&); private: - //============================================================================== bool escapeKeyTriggersCloseButton; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DialogWindow); diff --git a/modules/juce_gui_extra/misc/juce_PreferencesPanel.cpp b/modules/juce_gui_extra/misc/juce_PreferencesPanel.cpp index b158c35881..67c7123f1f 100644 --- a/modules/juce_gui_extra/misc/juce_PreferencesPanel.cpp +++ b/modules/juce_gui_extra/misc/juce_PreferencesPanel.cpp @@ -83,7 +83,16 @@ void PreferencesPanel::addSettingsPage (const String& title, const void* imageDa void PreferencesPanel::showInDialogBox (const String& dialogTitle, int dialogWidth, int dialogHeight, const Colour& backgroundColour) { setSize (dialogWidth, dialogHeight); - DialogWindow::showDialog (dialogTitle, this, 0, backgroundColour, false); + + DialogWindow::LaunchOptions o; + o.content.setNonOwned (this); + o.dialogTitle = dialogTitle; + o.dialogBackgroundColour = backgroundColour; + o.escapeKeyTriggersCloseButton = false; + o.useNativeTitleBar = false; + o.resizable = false; + + o.launchAsync(); } //==============================================================================