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(); } //==============================================================================