diff --git a/examples/Assets/DSPDemos_Common.h b/examples/Assets/DSPDemos_Common.h index 25647e14f1..841a7c7665 100644 --- a/examples/Assets/DSPDemos_Common.h +++ b/examples/Assets/DSPDemos_Common.h @@ -612,7 +612,11 @@ private: auto u = fc.getURLResult(); if (! audioFileReader.loadURL (u)) - NativeMessageBox::showOkCancelBox (AlertWindow::WarningIcon, "Error loading file", "Unable to load audio file", nullptr, nullptr); + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::WarningIcon) + .withTitle ("Error loading file") + .withMessage ("Unable to load audio file"), + nullptr); else thumbnailComp.setCurrentURL (u); } diff --git a/examples/Audio/AudioPlaybackDemo.h b/examples/Audio/AudioPlaybackDemo.h index ea1b6b7bdf..deeb4673a6 100644 --- a/examples/Audio/AudioPlaybackDemo.h +++ b/examples/Audio/AudioPlaybackDemo.h @@ -529,10 +529,13 @@ private: } else { - NativeMessageBox::showMessageBoxAsync (AlertWindow::WarningIcon, "Enable Code Signing", - "You need to enable code-signing for your iOS project and enable \"iCloud Documents\" " - "permissions to be able to open audio files on your iDevice. See: " - "https://forum.juce.com/t/native-ios-android-file-choosers"); + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::WarningIcon) + .withTitle ("Enable Code Signing") + .withMessage ("You need to enable code-signing for your iOS project and enable \"iCloud Documents\" " + "permissions to be able to open audio files on your iDevice. See: " + "https://forum.juce.com/t/native-ios-android-file-choosers"), + nullptr); } } } diff --git a/examples/Audio/AudioRecordingDemo.h b/examples/Audio/AudioRecordingDemo.h index b6265c74f6..e04db4b3c8 100644 --- a/examples/Audio/AudioRecordingDemo.h +++ b/examples/Audio/AudioRecordingDemo.h @@ -359,11 +359,11 @@ private: fileToShare.deleteFile(); if (! success && error.isNotEmpty()) - { - NativeMessageBox::showMessageBoxAsync (AlertWindow::WarningIcon, - "Sharing Error", - error); - } + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::WarningIcon) + .withTitle ("Sharing Error") + .withMessage (error), + nullptr); }); #endif diff --git a/examples/Audio/MPEDemo.h b/examples/Audio/MPEDemo.h index 1962e56b0e..f8f5c702d8 100644 --- a/examples/Audio/MPEDemo.h +++ b/examples/Audio/MPEDemo.h @@ -524,7 +524,7 @@ private: void handleInvalidLegacyModeParameters() const { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Invalid legacy mode channel layout", "Cannot set legacy mode start/end channel:\n" "The end channel must not be less than the start channel!", diff --git a/examples/GUI/AccessibilityDemo.h b/examples/GUI/AccessibilityDemo.h index e64cc6fc15..59063cdb18 100644 --- a/examples/GUI/AccessibilityDemo.h +++ b/examples/GUI/AccessibilityDemo.h @@ -193,7 +193,7 @@ private: addAndMakeVisible (textButton); shapeButton.setShape (getJUCELogoPath(), false, true, false); - shapeButton.onClick = [] { AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, "Alert", "This is an AlertWindow"); }; + shapeButton.onClick = [] { AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, "Alert", "This is an AlertWindow"); }; addAndMakeVisible (shapeButton); } diff --git a/examples/GUI/CameraDemo.h b/examples/GUI/CameraDemo.h index 0891fadccd..92198043ad 100644 --- a/examples/GUI/CameraDemo.h +++ b/examples/GUI/CameraDemo.h @@ -252,7 +252,7 @@ private: } else { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Camera open failed", + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Camera open failed", "Camera open failed, reason: " + error); } @@ -365,7 +365,7 @@ private: void errorOccurred (const String& error) { - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, "Camera Device Error", "An error has occurred: " + error + " Camera will be closed."); @@ -378,7 +378,7 @@ private: void sharingFinished (bool success, bool isCapture) { - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, isCapture ? "Image sharing result" : "Video sharing result", success ? "Success!" : "Failed!"); diff --git a/examples/GUI/DialogsDemo.h b/examples/GUI/DialogsDemo.h index 0cac5293de..77b692d43a 100644 --- a/examples/GUI/DialogsDemo.h +++ b/examples/GUI/DialogsDemo.h @@ -89,19 +89,14 @@ public: // This method gets called on the message thread once our thread has finished.. void threadComplete (bool userPressedCancel) override { - if (userPressedCancel) - { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, - "Progress window", - "You pressed cancel!"); - } - else - { - // thread finished normally.. - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, - "Progress window", - "Thread finished ok!"); - } + const String messageString (userPressedCancel ? "You pressed cancel!" : "Thread finished ok!"); + + AlertWindow::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Progress window") + .withMessage (messageString) + .withButton ("OK"), + nullptr); // ..and clean up by deleting our thread object.. delete this; @@ -177,12 +172,13 @@ public: [] (bool granted) { if (! granted) - { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, - "Permissions warning", - "External storage access permission not granted, some files" - " may be inaccessible."); - } + AlertWindow::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::WarningIcon) + .withTitle ("Permissions warning") + .withMessage ("External storage access permission not granted, some files" + " may be inaccessible.") + .withButton ("OK"), + nullptr); }); } @@ -222,9 +218,12 @@ private: { void operator() (int result) const noexcept { - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, - "Alert Box", - "Result code: " + String (result)); + AlertWindow::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Alert Box") + .withMessage ("Result code: " + String (result)) + .withButton ("OK"), + nullptr); } }; @@ -246,10 +245,14 @@ private: auto optionIndexChosen = aw.getComboBoxComponent ("option")->getSelectedItemIndex(); auto text = aw.getTextEditorContents ("text"); - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, "Alert Box", - "Result code: " + String (result) + newLine - + "Option index chosen: " + String (optionIndexChosen) + newLine - + "Text: " + text); + AlertWindow::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Alert Box") + .withMessage ("Result code: " + String (result) + newLine + + "Option index chosen: " + String (optionIndexChosen) + newLine + + "Text: " + text) + .withButton ("OK"), + nullptr); } DialogsDemo& demo; @@ -259,11 +262,11 @@ private: { if (type >= plainAlertWindow && type <= questionAlertWindow) { - AlertWindow::AlertIconType icon = AlertWindow::NoIcon; + MessageBoxIconType icon = MessageBoxIconType::NoIcon; - if (type == warningAlertWindow) icon = AlertWindow::WarningIcon; - if (type == infoAlertWindow) icon = AlertWindow::InfoIcon; - if (type == questionAlertWindow) icon = AlertWindow::QuestionIcon; + if (type == warningAlertWindow) icon = MessageBoxIconType::WarningIcon; + if (type == infoAlertWindow) icon = MessageBoxIconType::InfoIcon; + if (type == questionAlertWindow) icon = MessageBoxIconType::QuestionIcon; AlertWindow::showMessageBoxAsync (icon, "This is an AlertWindow", "And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah.", @@ -271,7 +274,7 @@ private: } else if (type == okCancelAlertWindow) { - AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon, "This is an ok/cancel AlertWindow", + AlertWindow::showOkCancelBox (MessageBoxIconType::QuestionIcon, "This is an ok/cancel AlertWindow", "And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah.", {}, {}, {}, ModalCallbackFunction::create (AlertBoxResultChosen{})); @@ -291,7 +294,7 @@ private: { asyncAlertWindow = std::make_unique ("AlertWindow demo..", "This AlertWindow has a couple of extra components added to show how to add drop-down lists and text entry boxes.", - AlertWindow::QuestionIcon); + MessageBoxIconType::QuestionIcon); asyncAlertWindow->addTextEditor ("text", "enter some text here", "text field:"); asyncAlertWindow->addComboBox ("option", { "option 1", "option 2", "option 3", "option 4" }, "some options"); @@ -326,9 +329,12 @@ private: chosen << (result.isLocalFile() ? result.getLocalFile().getFullPathName() : result.toString (false)) << "\n"; - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, - "File Chooser...", - "You picked: " + chosen); + AlertWindow::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("File Chooser...") + .withMessage ("You picked: " + chosen) + .withButton ("OK"), + nullptr); }); } else if (type == loadWithPreviewChooser) @@ -349,9 +355,12 @@ private: chosen << (result.isLocalFile() ? result.getLocalFile().getFullPathName() : result.toString (false)) << "\n"; - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, - "File Chooser...", - "You picked: " + chosen); + AlertWindow::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("File Chooser...") + .withMessage ("You picked: " + chosen) + .withButton ("OK"), + nullptr); }, &imagePreview); } @@ -401,9 +410,12 @@ private: } #endif - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, - "File Chooser...", - "You picked: " + name); + AlertWindow::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("File Chooser...") + .withMessage ("You picked: " + name) + .withButton ("OK"), + nullptr); }); } else if (type == directoryChooser) @@ -420,9 +432,12 @@ private: auto name = result.isLocalFile() ? result.getLocalFile().getFullPathName() : result.toString (true); - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, - "File Chooser...", - "You picked: " + name); + AlertWindow::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("File Chooser...") + .withMessage ("You picked: " + name) + .withButton ("OK"), + nullptr); }); } } @@ -433,8 +448,12 @@ private: { auto resultString = success ? String ("success") : ("failure\n (error: " + error + ")"); - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, "Sharing Text Result", - "Sharing text finished\nwith " + resultString); + AlertWindow::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Sharing Text Result") + .withMessage ("Sharing text finished\nwith " + resultString) + .withButton ("OK"), + nullptr); }); } else if (type == shareFile) @@ -454,9 +473,12 @@ private: { auto resultString = success ? String ("success") : ("failure\n (error: " + error + ")"); - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, - "Sharing Files Result", - "Sharing files finished\nwith " + resultString); + AlertWindow::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Sharing Files Result") + .withMessage ("Sharing files finished\nwith " + resultString) + .withButton ("OK"), + nullptr); }); } @@ -480,8 +502,12 @@ private: String resultString = success ? String ("success") : ("failure\n (error: " + error + ")"); - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, "Sharing Images Result", - "Sharing images finished\nwith " + resultString); + AlertWindow::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Sharing Images Result") + .withMessage ("Sharing images finished\nwith " + resultString) + .withButton ("OK"), + nullptr); }); } } diff --git a/examples/GUI/PropertiesDemo.h b/examples/GUI/PropertiesDemo.h index 71c0992511..0229c654e3 100644 --- a/examples/GUI/PropertiesDemo.h +++ b/examples/GUI/PropertiesDemo.h @@ -61,7 +61,7 @@ public: void buttonClicked() override { ++counter; - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, "Action Button Pressed", + AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, "Action Button Pressed", "Pressing this type of property component can trigger an action such as showing an alert window!"); refresh(); } diff --git a/examples/GUI/VideoDemo.h b/examples/GUI/VideoDemo.h index 3ae280f7d3..137852764f 100644 --- a/examples/GUI/VideoDemo.h +++ b/examples/GUI/VideoDemo.h @@ -134,7 +134,7 @@ private: } else { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Couldn't load the file!", result.getErrorMessage()); } @@ -354,7 +354,7 @@ public: { if (! granted) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Permissions warning", "External storage access permission not granted, some files" " may be inaccessible."); @@ -503,7 +503,7 @@ private: void askIfUseNativeControls (const URL& url) { - auto* aw = new AlertWindow ("Choose viewer type", {}, AlertWindow::NoIcon); + auto* aw = new AlertWindow ("Choose viewer type", {}, MessageBoxIconType::NoIcon); aw->addButton ("Yes", 1, KeyPress (KeyPress::returnKey)); aw->addButton ("No", 0, KeyPress (KeyPress::escapeKey)); @@ -559,7 +559,7 @@ private: void showVideoUrlPrompt() { - auto* aw = new AlertWindow ("Enter URL for video to load", {}, AlertWindow::NoIcon); + auto* aw = new AlertWindow ("Enter URL for video to load", {}, MessageBoxIconType::NoIcon); aw->addButton ("OK", 1, KeyPress (KeyPress::returnKey)); aw->addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey)); @@ -596,7 +596,7 @@ private: } else { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Couldn't load the file!", result.getErrorMessage()); } @@ -677,7 +677,7 @@ private: void errorOccurred (const String& errorMessage) { - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, "An error has occurred", errorMessage + ", video will be unloaded."); diff --git a/examples/Utilities/InAppPurchasesDemo.h b/examples/Utilities/InAppPurchasesDemo.h index 53fa863fe7..af0491be2b 100644 --- a/examples/Utilities/InAppPurchasesDemo.h +++ b/examples/Utilities/InAppPurchasesDemo.h @@ -156,7 +156,7 @@ private: voiceProduct.purchasePrice = "In-App purchases unavailable"; } - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "In-app purchase is unavailable!", "In-App purchases are not available. This either means you are trying " "to use IAP on a platform that does not support IAP or you haven't setup " @@ -178,7 +178,7 @@ private: } } - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Your credit card will be charged!", "You are running the sample code for JUCE In-App purchases. " "Although this is only sample code, it will still CHARGE YOUR CREDIT CARD!", diff --git a/examples/Utilities/OSCDemo.h b/examples/Utilities/OSCDemo.h index 1618f51693..0109cc8d19 100644 --- a/examples/Utilities/OSCDemo.h +++ b/examples/Utilities/OSCDemo.h @@ -222,7 +222,7 @@ private: //============================================================================== void showConnectionErrorMessage (const String& messageText) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Connection error", messageText, "OK"); @@ -273,7 +273,7 @@ private: void showConnectionErrorMessage (const String& messageText) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Connection error", messageText, "OK"); @@ -403,7 +403,7 @@ private: //============================================================================== void handleConnectError (int failedPort) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "OSC Connection error", "Error: could not connect to port " + String (failedPort), "OK"); @@ -412,7 +412,7 @@ private: //============================================================================== void handleDisconnectError() { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Unknown error", "An unknown error occurred while trying to disconnect from UDP port.", "OK"); @@ -421,7 +421,7 @@ private: //============================================================================== void handleInvalidPortNumberEntered() { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Invalid port number", "Error: you have entered an invalid UDP port number.", "OK"); diff --git a/examples/Utilities/PushNotificationsDemo.h b/examples/Utilities/PushNotificationsDemo.h index e5a51e85ed..70711949cd 100644 --- a/examples/Utilities/PushNotificationsDemo.h +++ b/examples/Utilities/PushNotificationsDemo.h @@ -194,7 +194,11 @@ public: if (token.isEmpty()) showRemoteInstructions(); else - NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, "Device token", token); + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Device token") + .withMessage (token), + nullptr); }; #if JUCE_ANDROID @@ -308,11 +312,11 @@ private: String requiredFields = "all required fields"; #endif - NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, - "Incorrect notifications setup", - "Please make sure that " - + requiredFields + " are set."); - + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Incorrect notifications setup") + .withMessage ("Please make sure that " + requiredFields + " are set."), + nullptr); return; } @@ -559,11 +563,13 @@ private: { ignoreUnused (isLocalNotification); - NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, - "Received notification", - "ID: " + n.identifier - + ", title: " + n.title - + ", body: " + n.body); + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Received notification") + .withMessage ("ID: " + n.identifier + + ", title: " + n.title + + ", body: " + n.body), + nullptr); } void handleNotificationAction (bool isLocalNotification, @@ -573,24 +579,28 @@ private: { ignoreUnused (isLocalNotification); - NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, - "Received notification action", - "ID: " + n.identifier - + ", title: " + n.title - + ", body: " + n.body - + ", action: " + actionIdentifier - + ", optionalResponse: " + optionalResponse); + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Received notification action") + .withMessage ("ID: " + n.identifier + + ", title: " + n.title + + ", body: " + n.body + + ", action: " + actionIdentifier + + ", optionalResponse: " + optionalResponse), + nullptr); PushNotifications::getInstance()->removeDeliveredNotification (n.identifier); } void localNotificationDismissedByUser (const PushNotifications::Notification& n) override { - NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, - "Notification dismissed by a user", - "ID: " + n.identifier - + ", title: " + n.title - + ", body: " + n.body); + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Notification dismissed by a user") + .withMessage ("ID: " + n.identifier + + ", title: " + n.title + + ", body: " + n.body), + nullptr); } void deliveredNotificationsListReceived (const Array& notifs) override @@ -600,7 +610,11 @@ private: for (auto& n : notifs) text << "(" << n.identifier << ", " << n.title << ", " << n.body << "), "; - NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, "Received notification list", text); + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Received notification list") + .withMessage (text), + nullptr); } void pendingLocalNotificationsListReceived (const Array& notifs) override @@ -610,37 +624,49 @@ private: for (auto& n : notifs) text << "(" << n.identifier << ", " << n.title << ", " << n.body << "), "; - NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, "Pending notification list", text); + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Pending notification list") + .withMessage (text), + nullptr); } void deviceTokenRefreshed (const String& token) override { - NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, - "Device token refreshed", - token); + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Device token refreshed") + .withMessage (token), + nullptr); } #if JUCE_ANDROID void remoteNotificationsDeleted() override { - NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, - "Remote notifications deleted", - "Some of the pending messages were removed!"); + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Remote notifications deleted") + .withMessage ("Some of the pending messages were removed!"), + nullptr); } void upstreamMessageSent (const String& messageId) override { - NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, - "Upstream message sent", - "Message id: " + messageId); + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Upstream message sent") + .withMessage ("Message id: " + messageId), + nullptr); } void upstreamMessageSendingError (const String& messageId, const String& error) override { - NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, - "Upstream message sending error", - "Message id: " + messageId - + "\nerror: " + error); + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Upstream message sending error") + .withMessage ("Message id: " + messageId + + "\nerror: " + error), + nullptr); } static Array getAndroidChannels() @@ -1191,12 +1217,14 @@ private: static void showRemoteInstructions() { #if JUCE_IOS || JUCE_MAC - NativeMessageBox::showMessageBoxAsync (AlertWindow::InfoIcon, - "Remote Notifications instructions", - "In order to be able to test remote notifications " - "ensure that the app is signed and that you register " - "the bundle ID for remote notifications in " - "Apple Developer Center."); + NativeMessageBox::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Remote Notifications instructions") + .withMessage ("In order to be able to test remote notifications " + "ensure that the app is signed and that you register " + "the bundle ID for remote notifications in " + "Apple Developer Center."), + nullptr); #endif } diff --git a/extras/AudioPluginHost/Source/Plugins/PluginGraph.cpp b/extras/AudioPluginHost/Source/Plugins/PluginGraph.cpp index 2e7d0cc9d3..0951c63d80 100644 --- a/extras/AudioPluginHost/Source/Plugins/PluginGraph.cpp +++ b/extras/AudioPluginHost/Source/Plugins/PluginGraph.cpp @@ -98,7 +98,7 @@ void PluginGraph::addPluginCallback (std::unique_ptr instan { if (instance == nullptr) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS("Couldn't create plugin"), error); } diff --git a/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.cpp b/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.cpp index 49cfa72dcd..97fa67be91 100644 --- a/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.cpp +++ b/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.cpp @@ -222,7 +222,7 @@ File NewProjectWizard::getLastWizardFolder() static void displayFailedFilesMessage (const StringArray& failedFiles) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS("Errors in Creating Project!"), TRANS("The following files couldn't be written:") + "\n\n" @@ -244,7 +244,7 @@ static void prepareDirectory (const File& targetFolder, Callback&& callback) } else if (FileHelpers::containsAnyNonHiddenFiles (targetFolder)) { - AlertWindow::showOkCancelBox (AlertWindow::InfoIcon, + AlertWindow::showOkCancelBox (MessageBoxIconType::InfoIcon, TRANS("New JUCE Project"), TRANS("You chose the folder:\n\nXFLDRX\n\n").replace ("XFLDRX", targetFolder.getFullPathName()) + TRANS("This folder isn't empty - are you sure you want to create the project there?") diff --git a/extras/Projucer/Source/Application/Windows/jucer_TranslationToolWindowComponent.h b/extras/Projucer/Source/Application/Windows/jucer_TranslationToolWindowComponent.h index f681ad5bcd..271bb83ca8 100644 --- a/extras/Projucer/Source/Application/Windows/jucer_TranslationToolWindowComponent.h +++ b/extras/Projucer/Source/Application/Windows/jucer_TranslationToolWindowComponent.h @@ -122,7 +122,7 @@ private: if (postStrings.size() != preStrings.size()) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS("Error"), TRANS("The pre- and post-translation text doesn't match!\n\n" "Perhaps it got mangled by the translator?")); @@ -138,7 +138,7 @@ private: if (Project* project = ProjucerApplication::getApp().mainWindowList.getFrontmostProject()) setPreTranslationText (TranslationHelpers::getPreTranslationText (*project)); else - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Translation Tool", + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Translation Tool", "This will only work when you have a project open!"); } diff --git a/extras/Projucer/Source/Application/jucer_Application.cpp b/extras/Projucer/Source/Application/jucer_Application.cpp index fe00ec4407..a84df13b5c 100644 --- a/extras/Projucer/Source/Application/jucer_Application.cpp +++ b/extras/Projucer/Source/Application/jucer_Application.cpp @@ -835,7 +835,7 @@ void ProjucerApplication::launchDemoRunner() "Couldn't find a compiled version of the Demo Runner." " Please compile the Demo Runner project in the JUCE examples directory.", "OK", {}, {}, - AlertWindow::WarningIcon, 1, + MessageBoxIconType::WarningIcon, 1, mainWindowList.getFrontmostWindow (false))); demoRunnerAlert->enterModalState (true, ModalCallbackFunction::create ([this] (int) { @@ -847,7 +847,7 @@ void ProjucerApplication::launchDemoRunner() "Couldn't find a compiled version of the Demo Runner." " Do you want to open the project?", "Open project", "Cancel", {}, - AlertWindow::QuestionIcon, 2, + MessageBoxIconType::QuestionIcon, 2, mainWindowList.getFrontmostWindow (false))); demoRunnerAlert->enterModalState (true, ModalCallbackFunction::create ([this, demoRunnerFile] (int retVal) { @@ -1102,7 +1102,7 @@ void ProjucerApplication::createNewProjectFromClipboard() { if (errorString.isNotEmpty()) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Error", errorString); + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Error", errorString); tempFile.deleteFile(); } }; diff --git a/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp b/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp index ba3dfe173c..6c087dd483 100644 --- a/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp +++ b/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp @@ -56,7 +56,7 @@ void LatestVersionCheckerAndUpdater::run() if (info == nullptr) { if (! backgroundCheck) - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Update Server Communication Error", "Failed to communicate with the JUCE update server.\n" "Please try again in a few minutes.\n\n" @@ -68,7 +68,7 @@ void LatestVersionCheckerAndUpdater::run() if (! info->isNewerVersionThanCurrent()) { if (! backgroundCheck) - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, "No New Version Available", "Your JUCE version is up to date."); return; @@ -109,7 +109,7 @@ void LatestVersionCheckerAndUpdater::run() } if (! backgroundCheck) - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Failed to find any new downloads", "Please try again in a few minutes."); } @@ -275,13 +275,13 @@ void LatestVersionCheckerAndUpdater::askUserForLocationToDownload (const Version { if (targetFolder.getChildFile (".git").isDirectory()) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Downloading New JUCE Version", + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Downloading New JUCE Version", targetFolderPath + "\n\nis a GIT repository!\n\nYou should use a \"git pull\" to update it to the latest version."); return; } - AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, "Overwrite Existing JUCE Folder?", "Do you want to replace the folder\n\n" + targetFolderPath + "\n\nwith the latest version from juce.com?\n\n" "This will move the existing folder to " + targetFolderPath + "_old.\n\n" @@ -295,7 +295,7 @@ void LatestVersionCheckerAndUpdater::askUserForLocationToDownload (const Version if (targetFolder.exists()) { - AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, "Existing File Or Directory", "Do you want to move\n\n" + targetFolderPath + "\n\nto\n\n" + targetFolderPath + "_old?", {}, @@ -388,7 +388,7 @@ private: result = install (zipData); if (result.failed()) - MessageManager::callAsync ([result] { AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + MessageManager::callAsync ([result] { AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Installation Failed", result.getErrorMessage()); }); else diff --git a/extras/Projucer/Source/Application/jucer_MainWindow.cpp b/extras/Projucer/Source/Application/jucer_MainWindow.cpp index 7e066d5d2c..fde35f5afa 100644 --- a/extras/Projucer/Source/Application/jucer_MainWindow.cpp +++ b/extras/Projucer/Source/Application/jucer_MainWindow.cpp @@ -452,7 +452,7 @@ void MainWindow::openPIP (const File& pipFile, std::function callba if (generatorResult != Result::ok()) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "PIP Error.", generatorResult.getErrorMessage()); @@ -464,7 +464,7 @@ void MainWindow::openPIP (const File& pipFile, std::function callba if (! generator->createMainCpp()) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "PIP Error.", "Failed to create Main.cpp."); @@ -481,7 +481,7 @@ void MainWindow::openPIP (const File& pipFile, std::function callba if (! openedSuccessfully) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "PIP Error.", "Failed to open .jucer file."); diff --git a/extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.cpp b/extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.cpp index acab822956..0ba7adfb50 100644 --- a/extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.cpp +++ b/extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.cpp @@ -176,7 +176,7 @@ void OpenDocumentManager::saveIfNeededAndUserAgrees (OpenDocumentManager::Docume return; } - AlertWindow::showYesNoCancelBox (AlertWindow::QuestionIcon, + AlertWindow::showYesNoCancelBox (MessageBoxIconType::QuestionIcon, TRANS("Closing document..."), TRANS("Do you want to save the changes to \"") + doc->getName() + "\"?", diff --git a/extras/Projucer/Source/CodeEditor/jucer_SourceCodeEditor.cpp b/extras/Projucer/Source/CodeEditor/jucer_SourceCodeEditor.cpp index 1d0c0952a2..1b845d3890 100644 --- a/extras/Projucer/Source/CodeEditor/jucer_SourceCodeEditor.cpp +++ b/extras/Projucer/Source/CodeEditor/jucer_SourceCodeEditor.cpp @@ -658,7 +658,7 @@ void CppCodeEditorComponent::insertComponentClass() { asyncAlertWindow = std::make_unique (TRANS ("Insert a new Component class"), TRANS ("Please enter a name for the new class"), - AlertWindow::NoIcon, + MessageBoxIconType::NoIcon, nullptr); const String classNameField { "Class Name" }; diff --git a/extras/Projucer/Source/ComponentEditor/UI/jucer_ResourceEditorPanel.cpp b/extras/Projucer/Source/ComponentEditor/UI/jucer_ResourceEditorPanel.cpp index 7885bdc589..20d96d15e5 100644 --- a/extras/Projucer/Source/ComponentEditor/UI/jucer_ResourceEditorPanel.cpp +++ b/extras/Projucer/Source/ComponentEditor/UI/jucer_ResourceEditorPanel.cpp @@ -265,7 +265,7 @@ void ResourceEditorPanel::reloadAll() failed.add (document.getResources().getResourceNames() [i]); if (failed.size() > 0) - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS("Reloading resources"), TRANS("The following resources couldn't be reloaded from their original files:\n\n") + failed.joinIntoString (", ")); diff --git a/extras/Projucer/Source/ComponentEditor/jucer_BinaryResources.cpp b/extras/Projucer/Source/ComponentEditor/jucer_BinaryResources.cpp index 03beab082c..485284d629 100644 --- a/extras/Projucer/Source/ComponentEditor/jucer_BinaryResources.cpp +++ b/extras/Projucer/Source/ComponentEditor/jucer_BinaryResources.cpp @@ -143,7 +143,7 @@ void BinaryResources::browseForResource (const String& title, if (! add (name, fc.getResult())) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS("Adding Resource"), TRANS("Failed to load the file!")); diff --git a/extras/Projucer/Source/Project/Modules/jucer_Modules.cpp b/extras/Projucer/Source/Project/Modules/jucer_Modules.cpp index e29d0b328d..fca72e17d0 100644 --- a/extras/Projucer/Source/Project/Modules/jucer_Modules.cpp +++ b/extras/Projucer/Source/Project/Modules/jucer_Modules.cpp @@ -682,14 +682,14 @@ void EnabledModulesList::addModuleOfferingToCopy (const File& f, bool isFromUser if (! m.isValid()) { - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, "Add Module", "This wasn't a valid module folder!"); return; } if (isModuleEnabled (m.getID())) { - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, "Add Module", "The project already contains this module!"); return; } diff --git a/extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h b/extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h index b19c6bdc96..0ddb45a3f8 100644 --- a/extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h +++ b/extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h @@ -90,7 +90,7 @@ public: safeThis->project.getUndoManagerFor (parent)); }; - AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, "Delete Exporter", "Are you sure you want to delete this export target?", "", @@ -244,7 +244,7 @@ public: void deleteItem() override { - AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, "Delete Configuration", "Are you sure you want to delete this configuration?", "", diff --git a/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h b/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h index ddea173612..a7d1ad73b2 100644 --- a/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h +++ b/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h @@ -138,7 +138,7 @@ public: if (filesToTrash.size() > maxFilesToList) fileList << "\n...plus " << (filesToTrash.size() - maxFilesToList) << " more files..."; - AlertWindow::showYesNoCancelBox (AlertWindow::NoIcon, + AlertWindow::showYesNoCancelBox (MessageBoxIconType::NoIcon, "Delete Project Items", "As well as removing the selected item(s) from the project, do you also want to move their files to the trash:\n\n" + fileList, @@ -521,7 +521,7 @@ public: { if (newName != File::createLegalFileName (newName)) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "File Rename", "That filename contained some illegal characters!"); triggerAsyncRename (item); @@ -538,7 +538,7 @@ public: if (correspondingItem.isValid()) { - AlertWindow::showOkCancelBox (AlertWindow::NoIcon, + AlertWindow::showOkCancelBox (MessageBoxIconType::NoIcon, "File Rename", "Do you also want to rename the corresponding file \"" + correspondingFile.getFileName() + "\" to match?", {}, @@ -552,7 +552,7 @@ public: if (! parent->item.renameFile (newFile)) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "File Rename", "Failed to rename \"" + oldFile.getFullPathName() + "\"!\n\nCheck your file permissions!"); return; @@ -560,7 +560,7 @@ public: if (! correspondingItem.renameFile (newFile.withFileExtension (correspondingFile.getFileExtension()))) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "File Rename", "Failed to rename \"" + correspondingFile.getFullPathName() + "\"!\n\nCheck your file permissions!"); } @@ -571,7 +571,7 @@ public: if (! item.renameFile (newFile)) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "File Rename", "Failed to rename the file!\n\nCheck your file permissions!"); } diff --git a/extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h b/extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h index 7a4a87d599..bf046082df 100644 --- a/extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h +++ b/extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h @@ -378,7 +378,7 @@ private: { missingDependencies = enabledModules.getExtraDependenciesNeeded (moduleID); - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Adding Missing Dependencies", "Couldn't locate some of these modules - you'll need to find their " "folders manually and add them to the list."); diff --git a/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.cpp b/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.cpp index b5750fae69..2e9f01b7ef 100644 --- a/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.cpp +++ b/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.cpp @@ -320,7 +320,7 @@ void ProjectContentComponent::closeDocument() static void showSaveWarning (OpenDocumentManager::Document* currentDocument) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS("Save failed!"), TRANS("Couldn't save the file:") + "\n" + currentDocument->getFile().getFullPathName()); diff --git a/extras/Projucer/Source/Project/jucer_Project.cpp b/extras/Projucer/Source/Project/jucer_Project.cpp index 8fbd6c455f..a93394d95d 100644 --- a/extras/Projucer/Source/Project/jucer_Project.cpp +++ b/extras/Projucer/Source/Project/jucer_Project.cpp @@ -407,7 +407,7 @@ void Project::removeDefunctExporters() if (ProjucerApplication::getApp().isRunningCommandLine) std::cout << "WARNING! The " + oldExporters[key] + " Exporter is deprecated. The exporter will be removed from this project." << std::endl; else - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS (oldExporters[key]), TRANS ("The " + oldExporters[key] + " Exporter is deprecated. The exporter will be removed from this project.")); diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h index 76f5a3a633..9db3f99f44 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h @@ -723,7 +723,7 @@ public: "Since JUCE 4.2, this is instead done using \"AU/VST/VST2/AAX/RTAS Binary Location\" in the Xcode (OS X) configuration settings.\n\n" "Click 'Update' to remove the script (otherwise your plug-in may not compile correctly)."; - if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + if (AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, "Project settings: " + project.getDocumentTitle(), alertWindowText, "Update", "Cancel", nullptr, nullptr)) postbuildCommandValue.resetToDefault(); diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_NewFileWizard.cpp b/extras/Projucer/Source/Utility/Helpers/jucer_NewFileWizard.cpp index 96f4f6a3a7..88ddd38b0d 100644 --- a/extras/Projucer/Source/Utility/Helpers/jucer_NewFileWizard.cpp +++ b/extras/Projucer/Source/Utility/Helpers/jucer_NewFileWizard.cpp @@ -171,7 +171,7 @@ private: { asyncAlertWindow = std::make_unique (TRANS ("Create new Component class"), TRANS ("Please enter the name for the new class"), - AlertWindow::NoIcon, nullptr); + MessageBoxIconType::NoIcon, nullptr); asyncAlertWindow->addTextEditor (getClassNameFieldName(), String(), String(), false); asyncAlertWindow->addButton (TRANS ("Create Files"), 1, KeyPress (KeyPress::returnKey)); @@ -235,7 +235,7 @@ public: //============================================================================== void NewFileWizard::Type::showFailedToWriteMessage (const File& file) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Failed to Create File!", "Couldn't write to the file: " + file.getFullPathName()); } diff --git a/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp b/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp index 88c3f81835..5f6c8a3a7a 100644 --- a/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp +++ b/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp @@ -388,8 +388,8 @@ public: PropertiesFile* properties, bool allowPluginsWhichRequireAsynchronousInstantiation, int threads, const String& title, const String& text) : owner (plc), formatToScan (format), filesOrIdentifiersToScan (filesOrIdentifiers), propertiesToUse (properties), - pathChooserWindow (TRANS("Select folders to scan..."), String(), AlertWindow::NoIcon), - progressWindow (title, text, AlertWindow::NoIcon), + pathChooserWindow (TRANS("Select folders to scan..."), String(), MessageBoxIconType::NoIcon), + progressWindow (title, text, MessageBoxIconType::NoIcon), numThreads (threads), allowAsync (allowPluginsWhichRequireAsynchronousInstantiation) { FileSearchPath path (formatToScan.getDefaultLocationsToSearch()); @@ -467,7 +467,7 @@ private: if (isStupidPath (f)) { - AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, TRANS("Plugin Scanning"), TRANS("If you choose to scan folders that contain non-plugin files, " "then scanning may take a long time, and can cause crashes when " @@ -642,7 +642,7 @@ void PluginListComponent::scanFinished (const StringArray& failedFiles) currentScanner.reset(); // mustn't delete this before using the failed files array if (shortNames.size() > 0) - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, TRANS("Scan complete"), TRANS("Note that the following files appeared to be plugin files, but failed to load correctly") + ":\n\n" diff --git a/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp b/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp index 9af1cef881..af5c993a12 100644 --- a/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp +++ b/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp @@ -405,7 +405,7 @@ public: } if (error.isNotEmpty()) - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS("Error when trying to open audio device!"), error); } diff --git a/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp b/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp index 44b54642ae..befe28b6e2 100644 --- a/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp +++ b/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp @@ -289,25 +289,4 @@ int ModalComponentManager::runEventLoopForCurrentComponent() } #endif -//============================================================================== -struct LambdaCallback : public ModalComponentManager::Callback -{ - LambdaCallback (std::function fn) noexcept : function (fn) {} - - void modalStateFinished (int result) override - { - if (function != nullptr) - function (result); - } - - std::function function; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LambdaCallback) -}; - -ModalComponentManager::Callback* ModalCallbackFunction::create (std::function f) -{ - return new LambdaCallback (f); -} - } // namespace juce diff --git a/modules/juce_gui_basics/components/juce_ModalComponentManager.h b/modules/juce_gui_basics/components/juce_ModalComponentManager.h index ca1dde2469..d2ae14b092 100644 --- a/modules/juce_gui_basics/components/juce_ModalComponentManager.h +++ b/modules/juce_gui_basics/components/juce_ModalComponentManager.h @@ -163,14 +163,26 @@ class JUCE_API ModalCallbackFunction { public: /** This is a utility function to create a ModalComponentManager::Callback that will - call a lambda function. + call a callable object. - The lambda that you supply must take an integer parameter, which is the result code that + The function that you supply must take an integer parameter, which is the result code that was returned when the modal component was dismissed. @see ModalComponentManager::Callback */ - static ModalComponentManager::Callback* create (std::function); + template + static ModalComponentManager::Callback* create (CallbackFn&& fn) + { + struct Callable : public ModalComponentManager::Callback + { + explicit Callable (CallbackFn&& f) : fn (std::forward (f)) {} + void modalStateFinished (int result) override { fn (result); } + + CallbackFn fn; + }; + + return new Callable (std::forward (fn)); + } //============================================================================== /** This is a utility function to create a ModalComponentManager::Callback that will diff --git a/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp b/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp index b00ccc9264..ffae88cc4c 100644 --- a/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp +++ b/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp @@ -194,7 +194,7 @@ void FileChooserDialogBox::okButtonPressed() && content->chooserComponent.isSaveMode() && content->chooserComponent.getSelectedFile(0).exists()) { - AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, TRANS("File already exists"), TRANS("There's already a file called: FLNM") .replace ("FLNM", content->chooserComponent.getSelectedFile(0).getFullPathName()) @@ -229,7 +229,7 @@ void FileChooserDialogBox::createNewFolder() { auto* aw = new AlertWindow (TRANS("New Folder"), TRANS("Please enter the name for the folder"), - AlertWindow::NoIcon, this); + MessageBoxIconType::NoIcon, this); aw->addTextEditor ("Folder Name", String(), String(), false); aw->addButton (TRANS("Create Folder"), 1, KeyPress (KeyPress::returnKey)); @@ -251,7 +251,7 @@ void FileChooserDialogBox::createNewFolderConfirmed (const String& nameFromDialo auto parent = content->chooserComponent.getRoot(); if (! parent.getChildFile (name).createDirectory()) - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS ("New Folder"), TRANS ("Couldn't create the folder!")); diff --git a/modules/juce_gui_basics/juce_gui_basics.cpp b/modules/juce_gui_basics/juce_gui_basics.cpp index 40b4ffdec3..bab71538b6 100644 --- a/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/modules/juce_gui_basics/juce_gui_basics.cpp @@ -66,6 +66,7 @@ #include #include #include + #include #if ! JUCE_MINGW #include @@ -82,6 +83,7 @@ #elif ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES #pragma comment(lib, "vfw32.lib") #pragma comment(lib, "imm32.lib") + #pragma comment(lib, "comctl32.lib") #if JUCE_OPENGL #pragma comment(lib, "OpenGL32.Lib") diff --git a/modules/juce_gui_basics/juce_gui_basics.h b/modules/juce_gui_basics/juce_gui_basics.h index 205ffdd4cc..033f0ec575 100644 --- a/modules/juce_gui_basics/juce_gui_basics.h +++ b/modules/juce_gui_basics/juce_gui_basics.h @@ -258,6 +258,7 @@ namespace juce #include "misc/juce_JUCESplashScreen.h" #include "widgets/juce_TreeView.h" #include "windows/juce_TopLevelWindow.h" +#include "windows/juce_MessageBoxOptions.h" #include "windows/juce_AlertWindow.h" #include "windows/juce_CallOutBox.h" #include "windows/juce_ComponentPeer.h" diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp index 27094fb3fc..ab2618a989 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp @@ -407,7 +407,7 @@ void LookAndFeel_V2::drawDrawableButton (Graphics& g, DrawableButton& button, //============================================================================== AlertWindow* LookAndFeel_V2::createAlertWindow (const String& title, const String& message, const String& button1, const String& button2, const String& button3, - AlertWindow::AlertIconType iconType, + MessageBoxIconType iconType, int numButtons, Component* associatedComponent) { AlertWindow* aw = new AlertWindow (title, message, iconType, associatedComponent); @@ -457,13 +457,13 @@ void LookAndFeel_V2::drawAlertBox (Graphics& g, AlertWindow& alert, const Rectangle iconRect (iconSize / -10, iconSize / -10, iconSize, iconSize); - if (alert.getAlertType() != AlertWindow::NoIcon) + if (alert.getAlertType() != MessageBoxIconType::NoIcon) { Path icon; uint32 colour; char character; - if (alert.getAlertType() == AlertWindow::WarningIcon) + if (alert.getAlertType() == MessageBoxIconType::WarningIcon) { colour = 0x55ff5555; character = '!'; @@ -476,8 +476,8 @@ void LookAndFeel_V2::drawAlertBox (Graphics& g, AlertWindow& alert, } else { - colour = alert.getAlertType() == AlertWindow::InfoIcon ? (uint32) 0x605555ff : (uint32) 0x40b69900; - character = alert.getAlertType() == AlertWindow::InfoIcon ? 'i' : '?'; + colour = alert.getAlertType() == MessageBoxIconType::InfoIcon ? (uint32) 0x605555ff : (uint32) 0x40b69900; + character = alert.getAlertType() == MessageBoxIconType::InfoIcon ? 'i' : '?'; icon.addEllipse (iconRect.toFloat()); } diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h index 8d07e8cec9..425b3410e1 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h @@ -67,7 +67,7 @@ public: const String& button1, const String& button2, const String& button3, - AlertWindow::AlertIconType iconType, + MessageBoxIconType iconType, int numButtons, Component* associatedComponent) override; void drawAlertBox (Graphics&, AlertWindow&, const Rectangle& textArea, TextLayout&) override; diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp index d42106ef2f..ece512a131 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp @@ -385,7 +385,7 @@ void LookAndFeel_V4::changeToggleButtonWidthToFitText (ToggleButton& button) //============================================================================== AlertWindow* LookAndFeel_V4::createAlertWindow (const String& title, const String& message, const String& button1, const String& button2, const String& button3, - AlertWindow::AlertIconType iconType, + MessageBoxIconType iconType, int numButtons, Component* associatedComponent) { auto boundsOffset = 50; @@ -429,13 +429,13 @@ void LookAndFeel_V4::drawAlertBox (Graphics& g, AlertWindow& alert, Rectangle iconRect (iconSize / -10, iconSize / -10, iconSize, iconSize); - if (alert.getAlertType() != AlertWindow::NoIcon) + if (alert.getAlertType() != MessageBoxIconType::NoIcon) { Path icon; char character; uint32 colour; - if (alert.getAlertType() == AlertWindow::WarningIcon) + if (alert.getAlertType() == MessageBoxIconType::WarningIcon) { character = '!'; @@ -449,7 +449,7 @@ void LookAndFeel_V4::drawAlertBox (Graphics& g, AlertWindow& alert, else { colour = Colour (0xff00b0b9).withAlpha (0.4f).getARGB(); - character = alert.getAlertType() == AlertWindow::InfoIcon ? 'i' : '?'; + character = alert.getAlertType() == MessageBoxIconType::InfoIcon ? 'i' : '?'; icon.addEllipse (iconRect.toFloat()); } diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.h b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.h index 6b5bcaf553..99c621f47b 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.h +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.h @@ -130,7 +130,7 @@ public: const String& button1, const String& button2, const String& button3, - AlertWindow::AlertIconType iconType, + MessageBoxIconType iconType, int numButtons, Component* associatedComponent) override; void drawAlertBox (Graphics&, AlertWindow&, const Rectangle& textArea, TextLayout&) override; diff --git a/modules/juce_gui_basics/native/juce_android_Windowing.cpp b/modules/juce_gui_basics/native/juce_android_Windowing.cpp index db2080dd9a..e8333ef3d1 100644 --- a/modules/juce_gui_basics/native/juce_android_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_android_Windowing.cpp @@ -1226,8 +1226,8 @@ DECLARE_JNI_CLASS (AndroidDialogOnClickListener, "android/content/DialogInterfac class DialogListener : public juce::AndroidInterfaceImplementer { public: - DialogListener (ModalComponentManager::Callback* callbackToUse, int resultToUse) - : callback (callbackToUse), result (resultToUse) + DialogListener (std::shared_ptr callbackToUse, int resultToUse) + : callback (std::move (callbackToUse)), result (resultToUse) {} void onResult (jobject dialog) @@ -1257,46 +1257,44 @@ private: return AndroidInterfaceImplementer::invoke (proxy, method, args); } - std::unique_ptr callback; + std::shared_ptr callback; int result; }; //============================================================================== -static void createAndroidDialog (const String& title, const String& message, - ModalComponentManager::Callback* callback, - const String& positiveButton = {}, const String& negativeButton = {}, - const String& neutralButton = {}) +static void createAndroidDialog (const MessageBoxOptions& opts, + std::unique_ptr callback) { auto* env = getEnv(); LocalRef builder (env->NewObject (AndroidAlertDialogBuilder, AndroidAlertDialogBuilder.construct, getMainActivity().get())); - builder = LocalRef (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setTitle, javaString (title).get())); - builder = LocalRef (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setMessage, javaString (message).get())); + builder = LocalRef (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setTitle, javaString (opts.getTitle()).get())); + builder = LocalRef (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setMessage, javaString (opts.getMessage()).get())); builder = LocalRef (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setCancelable, true)); - builder = LocalRef (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setOnCancelListener, - CreateJavaInterface (new DialogListener (callback, 0), - "android/content/DialogInterface$OnCancelListener").get())); + std::shared_ptr sharedCallback (std::move (callback)); - auto positiveButtonText = positiveButton.isEmpty() ? String ("OK") : positiveButton; + builder = LocalRef (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setOnCancelListener, + CreateJavaInterface (new DialogListener (sharedCallback, 0), + "android/content/DialogInterface$OnCancelListener").get())); builder = LocalRef (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setPositiveButton, - javaString (positiveButtonText).get(), - CreateJavaInterface (new DialogListener (callback, positiveButton.isEmpty() ? 0 : 1), - "android/content/DialogInterface$OnClickListener").get())); + javaString (opts.getButtonText (0)).get(), + CreateJavaInterface (new DialogListener (sharedCallback, 0), + "android/content/DialogInterface$OnClickListener").get())); - if (negativeButton.isNotEmpty()) + if (opts.getButtonText (1).isNotEmpty()) builder = LocalRef (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setNegativeButton, - javaString (negativeButton).get(), - CreateJavaInterface (new DialogListener (callback, neutralButton.isEmpty() ? 0 : 2), - "android/content/DialogInterface$OnClickListener").get())); + javaString (opts.getButtonText (1)).get(), + CreateJavaInterface (new DialogListener (sharedCallback, 1), + "android/content/DialogInterface$OnClickListener").get())); - if (neutralButton.isNotEmpty()) - builder = LocalRef (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setNegativeButton, - javaString (neutralButton).get(), - CreateJavaInterface (new DialogListener (callback, 0), - "android/content/DialogInterface$OnClickListener").get())); + if (opts.getButtonText (2).isNotEmpty()) + builder = LocalRef (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.setNeutralButton, + javaString (opts.getButtonText (2)).get(), + CreateJavaInterface (new DialogListener (sharedCallback, 2), + "android/content/DialogInterface$OnClickListener").get())); LocalRef dialog (env->CallObjectMethod (builder.get(), AndroidAlertDialogBuilder.create)); @@ -1315,47 +1313,76 @@ static void createAndroidDialog (const String& title, const String& message, env->CallVoidMethod (window.get(), AndroidWindow.clearFlags, FLAG_NOT_FOCUSABLE); } -void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType /*iconType*/, +void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType /*iconType*/, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { - createAndroidDialog (title, message, callback); + showAsync (MessageBoxOptions() + .withTitle (title) + .withMessage (message) + .withButton (TRANS("OK")), + AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::messageBox)); } -bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType /*iconType*/, +bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType /*iconType*/, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { - jassert (callback != nullptr); // on android, all alerts must be non-modal!! + showAsync (MessageBoxOptions() + .withTitle (title) + .withMessage (message) + .withButton (TRANS("OK")) + .withButton (TRANS("Cancel")), + AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::okCancel)); - createAndroidDialog (title, message, callback, "OK", "Cancel"); return false; } -int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType /*iconType*/, +int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType /*iconType*/, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { - jassert (callback != nullptr); // on android, all alerts must be non-modal!! + showAsync (MessageBoxOptions() + .withTitle (title) + .withMessage (message) + .withButton (TRANS("Yes")) + .withButton (TRANS("No")) + .withButton (TRANS("Cancel")), + AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::yesNoCancel)); - createAndroidDialog (title, message, callback, "Yes", "No", "Cancel"); return 0; } -int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType /*iconType*/, - const String& title, const String& message, - Component* /*associatedComponent*/, - ModalComponentManager::Callback* callback) +int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType /*iconType*/, + const String& title, const String& message, + Component* /*associatedComponent*/, + ModalComponentManager::Callback* callback) { - jassert (callback != nullptr); // on android, all alerts must be non-modal!! + showAsync (MessageBoxOptions() + .withTitle (title) + .withMessage (message) + .withButton (TRANS("Yes")) + .withButton (TRANS("No")), + AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::okCancel)); - createAndroidDialog (title, message, callback, "Yes", "No"); return 0; } +void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, + ModalComponentManager::Callback* callback) +{ + createAndroidDialog (options, std::unique_ptr (callback)); +} + +void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, + std::function callback) +{ + showAsync (options, ModalCallbackFunction::create (callback)); +} + //============================================================================== static bool androidScreenSaverEnabled = true; diff --git a/modules/juce_gui_basics/native/juce_ios_Windowing.mm b/modules/juce_gui_basics/native/juce_ios_Windowing.mm index bf9b588882..6df6ff2577 100644 --- a/modules/juce_gui_basics/native/juce_ios_Windowing.mm +++ b/modules/juce_gui_basics/native/juce_ios_Windowing.mm @@ -437,19 +437,18 @@ void LookAndFeel::playAlertSound() class iOSMessageBox { public: - iOSMessageBox (const String& title, const String& message, - NSString* button1, NSString* button2, NSString* button3, - ModalComponentManager::Callback* cb, const bool async) - : result (0), resultReceived (false), callback (cb), isAsync (async) + iOSMessageBox (const MessageBoxOptions& opts, std::unique_ptr&& cb) + : callback (std::move (cb)) { if (currentlyFocusedPeer != nullptr) { - UIAlertController* alert = [UIAlertController alertControllerWithTitle: juceStringToNS (title) - message: juceStringToNS (message) + UIAlertController* alert = [UIAlertController alertControllerWithTitle: juceStringToNS (opts.getTitle()) + message: juceStringToNS (opts.getMessage()) preferredStyle: UIAlertControllerStyleAlert]; - addButton (alert, button1, 0); - addButton (alert, button2, 1); - addButton (alert, button3, 2); + + addButton (alert, opts.getButtonText (0)); + addButton (alert, opts.getButtonText (1)); + addButton (alert, opts.getButtonText (2)); [currentlyFocusedPeer->controller presentViewController: alert animated: YES @@ -469,105 +468,146 @@ public: JUCE_AUTORELEASEPOOL { - while (! resultReceived) + while (result < 0) [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; } return result; } - void buttonClicked (const int buttonIndex) noexcept + void buttonClicked (int buttonIndex) noexcept { - result = buttonIndex; - resultReceived = true; - if (callback != nullptr) - callback->modalStateFinished (result); + callback->modalStateFinished (buttonIndex); - if (isAsync) - delete this; + delete this; } private: - int result; - bool resultReceived; - std::unique_ptr callback; - const bool isAsync; - - void addButton (UIAlertController* alert, NSString* text, int index) + void addButton (UIAlertController* alert, const String& text) { - if (text != nil) - [alert addAction: [UIAlertAction actionWithTitle: text + if (! text.isEmpty()) + { + const auto index = [[alert actions] count]; + + [alert addAction: [UIAlertAction actionWithTitle: juceStringToNS (text) style: UIAlertActionStyleDefault - handler: ^(UIAlertAction*) { this->buttonClicked (index); }]]; + handler: ^(UIAlertAction*) { this->buttonClicked ((int) index); }]]; + } } + int result = -1; + std::unique_ptr callback; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSMessageBox) }; //============================================================================== +static int showDialog (const MessageBoxOptions& options, + std::unique_ptr callback, + Async async) +{ + #if JUCE_MODAL_LOOPS_PERMITTED + if (async == Async::no) + { + JUCE_AUTORELEASEPOOL + { + iOSMessageBox messageBox (options, std::move (callback)); + return messageBox.getResult(); + } + } + #endif + + ignoreUnused (async); + + new iOSMessageBox (options, std::move (callback)); + return 0; +} + #if JUCE_MODAL_LOOPS_PERMITTED -void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType /*iconType*/, +void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType /*iconType*/, const String& title, const String& message, Component* /*associatedComponent*/) { - JUCE_AUTORELEASEPOOL - { - iOSMessageBox mb (title, message, @"OK", nil, nil, nullptr, false); - ignoreUnused (mb.getResult()); - } + showDialog (MessageBoxOptions() + .withTitle (title) + .withMessage (message) + .withButton (TRANS("OK")), + nullptr, Async::no); +} + +int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options) +{ + return showDialog (options, nullptr, Async::no); } #endif -void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType /*iconType*/, +void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType /*iconType*/, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { - new iOSMessageBox (title, message, @"OK", nil, nil, callback, true); + showDialog (MessageBoxOptions() + .withTitle (title) + .withMessage (message) + .withButton (TRANS("OK")), + rawToUniquePtr (AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::messageBox)), + Async::yes); } -bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType /*iconType*/, +bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType /*iconType*/, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { - std::unique_ptr mb (new iOSMessageBox (title, message, @"Cancel", @"OK", - nil, callback, callback != nullptr)); - - if (callback == nullptr) - return mb->getResult() == 1; - - mb.release(); - return false; + return showDialog (MessageBoxOptions() + .withTitle (title) + .withMessage (message) + .withButton (TRANS("OK")) + .withButton (TRANS("Cancel")), + rawToUniquePtr (AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::okCancel)), + callback != nullptr ? Async::yes : Async::no) == 1; } -int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType /*iconType*/, +int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType /*iconType*/, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { - std::unique_ptr mb (new iOSMessageBox (title, message, @"Cancel", @"Yes", @"No", callback, callback != nullptr)); - - if (callback == nullptr) - return mb->getResult(); - - mb.release(); - return 0; + return showDialog (MessageBoxOptions() + .withTitle (title) + .withMessage (message) + .withButton (TRANS("Yes")) + .withButton (TRANS("No")) + .withButton (TRANS("Cancel")), + rawToUniquePtr (AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::yesNoCancel)), + callback != nullptr ? Async::yes : Async::no); } -int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType /*iconType*/, +int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType /*iconType*/, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { - std::unique_ptr mb (new iOSMessageBox (title, message, @"No", @"Yes", nil, callback, callback != nullptr)); + return showDialog (MessageBoxOptions() + .withTitle (title) + .withMessage (message) + .withButton (TRANS("Yes")) + .withButton (TRANS("No")), + rawToUniquePtr (AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::okCancel)), + callback != nullptr ? Async::yes : Async::no); +} - if (callback == nullptr) - return mb->getResult(); +void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, + ModalComponentManager::Callback* callback) +{ + showDialog (options, rawToUniquePtr (callback), Async::yes); +} - mb.release(); - return 0; +void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, + std::function callback) +{ + showAsync (options, ModalCallbackFunction::create (callback)); } //============================================================================== diff --git a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index 726c38eff8..468fcf1aba 100644 --- a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -654,16 +654,86 @@ void LookAndFeel::playAlertSound() } //============================================================================== +static int showDialog (const MessageBoxOptions& options, + ModalComponentManager::Callback* callback, + Async async) +{ + const auto dummyCallback = [] (int) {}; + + switch (options.getNumButtons()) + { + case 2: + { + if (async == Async::yes && callback == nullptr) + callback = ModalCallbackFunction::create (dummyCallback); + + return AlertWindow::showOkCancelBox (options.getIconType(), + options.getTitle(), + options.getMessage(), + options.getButtonText (0), + options.getButtonText (1), + options.getAssociatedComponent(), + callback) ? 1 : 0; + } + + case 3: + { + if (async == Async::yes && callback == nullptr) + callback = ModalCallbackFunction::create (dummyCallback); + + return AlertWindow::showYesNoCancelBox (options.getIconType(), + options.getTitle(), + options.getMessage(), + options.getButtonText (0), + options.getButtonText (1), + options.getButtonText (2), + options.getAssociatedComponent(), + callback); + } + + case 1: + default: + break; + } + + #if JUCE_MODAL_LOOPS_PERMITTED + if (async == Async::no) + { + AlertWindow::showMessageBox (options.getIconType(), + options.getTitle(), + options.getMessage(), + options.getButtonText (0), + options.getAssociatedComponent()); + } + else + #endif + { + AlertWindow::showMessageBoxAsync (options.getIconType(), + options.getTitle(), + options.getMessage(), + options.getButtonText (0), + options.getAssociatedComponent(), + callback); + } + + return 0; +} + #if JUCE_MODAL_LOOPS_PERMITTED -void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType, +void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType iconType, const String& title, const String& message, - Component*) + Component* /*associatedComponent*/) { AlertWindow::showMessageBox (iconType, title, message); } + +int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options) +{ + return showDialog (options, nullptr, Async::no); +} #endif -void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType, +void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType iconType, const String& title, const String& message, Component* associatedComponent, ModalComponentManager::Callback* callback) @@ -671,7 +741,7 @@ void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIcon AlertWindow::showMessageBoxAsync (iconType, title, message, {}, associatedComponent, callback); } -bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType, +bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType iconType, const String& title, const String& message, Component* associatedComponent, ModalComponentManager::Callback* callback) @@ -679,7 +749,7 @@ bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType return AlertWindow::showOkCancelBox (iconType, title, message, {}, {}, associatedComponent, callback); } -int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType, +int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType iconType, const String& title, const String& message, Component* associatedComponent, ModalComponentManager::Callback* callback) @@ -688,15 +758,27 @@ int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconTy associatedComponent, callback); } -int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType iconType, +int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType iconType, const String& title, const String& message, Component* associatedComponent, ModalComponentManager::Callback* callback) { - return AlertWindow::showOkCancelBox (iconType, title, message, TRANS ("Yes"), TRANS ("No"), + return AlertWindow::showOkCancelBox (iconType, title, message, TRANS("Yes"), TRANS("No"), associatedComponent, callback); } +void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, + ModalComponentManager::Callback* callback) +{ + showDialog (options, callback, Async::yes); +} + +void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, + std::function callback) +{ + showAsync (options, ModalCallbackFunction::create (callback)); +} + //============================================================================== Image juce_createIconForFile (const File&) { diff --git a/modules/juce_gui_basics/native/juce_mac_Windowing.mm b/modules/juce_gui_basics/native/juce_mac_Windowing.mm index 3ff2dc7c01..06fdce54d7 100644 --- a/modules/juce_gui_basics/native/juce_mac_Windowing.mm +++ b/modules/juce_gui_basics/native/juce_mac_Windowing.mm @@ -35,47 +35,29 @@ void LookAndFeel::playAlertSound() class OSXMessageBox : private AsyncUpdater { public: - OSXMessageBox (AlertWindow::AlertIconType type, const String& t, const String& m, - const char* b1, const char* b2, const char* b3, - ModalComponentManager::Callback* c, const bool runAsync) - : iconType (type), title (t), message (m), callback (c), - button1 (b1), button2 (b2), button3 (b3) + OSXMessageBox (const MessageBoxOptions& opts, + std::unique_ptr&& c) + : options (opts), callback (std::move (c)) { - if (runAsync) - triggerAsyncUpdate(); } int getResult() const { switch (getRawResult()) { - case NSAlertFirstButtonReturn: return 1; - case NSAlertThirdButtonReturn: return 2; - default: return 0; + case NSAlertFirstButtonReturn: return 0; + case NSAlertSecondButtonReturn: return 1; + case NSAlertThirdButtonReturn: return 2; + default: break; } - } - static int show (AlertWindow::AlertIconType iconType, const String& title, const String& message, - ModalComponentManager::Callback* callback, const char* b1, const char* b2, const char* b3, - bool runAsync) - { - std::unique_ptr mb (new OSXMessageBox (iconType, title, message, b1, b2, b3, - callback, runAsync)); - if (! runAsync) - return mb->getResult(); - - mb.release(); + jassertfalse; return 0; } -private: - AlertWindow::AlertIconType iconType; - String title, message; - std::unique_ptr callback; - const char* button1; - const char* button2; - const char* button3; + using AsyncUpdater::triggerAsyncUpdate; +private: void handleAsyncUpdate() override { auto result = getResult(); @@ -86,73 +68,147 @@ private: delete this; } + static void addButton (NSAlert* alert, const String& button) + { + if (! button.isEmpty()) + [alert addButtonWithTitle: juceStringToNS (button)]; + } + NSInteger getRawResult() const { NSAlert* alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText: juceStringToNS (title)]; - [alert setInformativeText: juceStringToNS (message)]; + [alert setMessageText: juceStringToNS (options.getTitle())]; + [alert setInformativeText: juceStringToNS (options.getMessage())]; - [alert setAlertStyle: iconType == AlertWindow::WarningIcon ? NSAlertStyleCritical - : NSAlertStyleInformational]; - addButton (alert, button1); - addButton (alert, button2); - addButton (alert, button3); + [alert setAlertStyle: options.getIconType() == MessageBoxIconType::WarningIcon ? NSAlertStyleCritical + : NSAlertStyleInformational]; + + const auto button1Text = options.getButtonText (0); + + addButton (alert, button1Text.isEmpty() ? "OK" : button1Text); + addButton (alert, options.getButtonText (1)); + addButton (alert, options.getButtonText (2)); return [alert runModal]; } - static void addButton (NSAlert* alert, const char* button) - { - if (button != nullptr) - [alert addButtonWithTitle: juceStringToNS (TRANS (button))]; - } + MessageBoxOptions options; + std::unique_ptr callback; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXMessageBox) }; +static int showDialog (const MessageBoxOptions& options, + std::unique_ptr callback, + Async async) +{ + auto messageBox = std::make_unique (options, std::move (callback)); + + #if JUCE_MODAL_LOOPS_PERMITTED + if (async == Async::no) + return messageBox->getResult(); + #endif + + ignoreUnused (async); + + messageBox->triggerAsyncUpdate(); + messageBox.release(); + + return 0; +} + #if JUCE_MODAL_LOOPS_PERMITTED -void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType, +void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType iconType, const String& title, const String& message, Component* /*associatedComponent*/) { - OSXMessageBox::show (iconType, title, message, nullptr, "OK", nullptr, nullptr, false); + showDialog (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (TRANS("OK")), + nullptr, + Async::no); +} + +int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options) +{ + return showDialog (options, nullptr, Async::no); } #endif -void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType, +void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType iconType, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { - OSXMessageBox::show (iconType, title, message, callback, "OK", nullptr, nullptr, true); + showDialog (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (TRANS("OK")), + rawToUniquePtr (AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::messageBox)), + Async::yes); } -bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType, +bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType iconType, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { - return OSXMessageBox::show (iconType, title, message, callback, - "OK", "Cancel", nullptr, callback != nullptr) == 1; + return showDialog (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (TRANS("OK")) + .withButton (TRANS("Cancel")), + rawToUniquePtr (AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::okCancel)), + callback != nullptr ? Async::yes : Async::no) != 0; } -int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType, +int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType iconType, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { - return OSXMessageBox::show (iconType, title, message, callback, - "Yes", "Cancel", "No", callback != nullptr); + return showDialog (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (TRANS("Yes")) + .withButton (TRANS("No")) + .withButton (TRANS("Cancel")), + rawToUniquePtr (AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::yesNoCancel)), + callback != nullptr ? Async::yes : Async::no); } -int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType iconType, +int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType iconType, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { - return OSXMessageBox::show (iconType, title, message, callback, - "Yes", "No", nullptr, callback != nullptr); + return showDialog (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (TRANS("Yes")) + .withButton (TRANS("No")), + rawToUniquePtr (AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::okCancel)), + callback != nullptr ? Async::yes : Async::no); } +void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, + ModalComponentManager::Callback* callback) +{ + showDialog (options, rawToUniquePtr (callback), Async::yes); +} + +void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, + std::function callback) +{ + showAsync (options, ModalCallbackFunction::create (callback)); +} //============================================================================== static NSRect getDragRect (NSView* view, NSEvent* event) diff --git a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index a624953b3d..8cdbd2518a 100644 --- a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -4411,30 +4411,44 @@ bool juce_areThereAnyAlwaysOnTopWindows() } //============================================================================== -class WindowsMessageBox : public AsyncUpdater +#if JUCE_MSVC + // required to enable the newer dialog box on vista and above + #pragma comment(linker, \ + "\"/MANIFESTDEPENDENCY:type='Win32' " \ + "name='Microsoft.Windows.Common-Controls' " \ + "version='6.0.0.0' " \ + "processorArchitecture='*' " \ + "publicKeyToken='6595b64144ccf1df' " \ + "language='*'\"" \ + ) +#endif + +class WindowsMessageBoxBase : private AsyncUpdater { public: - WindowsMessageBox (AlertWindow::AlertIconType iconType, - const String& boxTitle, const String& m, - Component* associatedComponent, UINT extraFlags, - ModalComponentManager::Callback* cb, const bool runAsync) - : flags (extraFlags | getMessageBoxFlags (iconType)), - owner (getWindowForMessageBox (associatedComponent)), - title (boxTitle), message (m), callback (cb) + WindowsMessageBoxBase (Component* comp, + std::unique_ptr&& cb) + : associatedComponent (comp), + callback (std::move (cb)) { - if (runAsync) - triggerAsyncUpdate(); } - int getResult() const + virtual int getResult() = 0; + + HWND getParentHWND() const { - const int r = MessageBox (owner, message.toWideCharPointer(), title.toWideCharPointer(), flags); - return (r == IDYES || r == IDOK) ? 1 : (r == IDNO && (flags & 1) != 0 ? 2 : 0); + if (associatedComponent != nullptr) + return (HWND) associatedComponent->getWindowHandle(); + + return nullptr; } + using AsyncUpdater::triggerAsyncUpdate; + +private: void handleAsyncUpdate() override { - const int result = getResult(); + const auto result = getResult(); if (callback != nullptr) callback->modalStateFinished (result); @@ -4442,97 +4456,303 @@ public: delete this; } -private: - UINT flags; - HWND owner; - String title, message; + Component::SafePointer associatedComponent; std::unique_ptr callback; - static UINT getMessageBoxFlags (AlertWindow::AlertIconType iconType) noexcept - { - UINT flags = MB_TASKMODAL | MB_SETFOREGROUND; - - // this window can get lost behind JUCE windows which are set to be alwaysOnTop - // so if there are any set it to be topmost - if (juce_areThereAnyAlwaysOnTopWindows()) - flags |= MB_TOPMOST; - - switch (iconType) - { - case AlertWindow::QuestionIcon: flags |= MB_ICONQUESTION; break; - case AlertWindow::WarningIcon: flags |= MB_ICONWARNING; break; - case AlertWindow::InfoIcon: flags |= MB_ICONINFORMATION; break; - case AlertWindow::NoIcon: JUCE_FALLTHROUGH - default: break; - } - - return flags; - } - - static HWND getWindowForMessageBox (Component* associatedComponent) - { - return associatedComponent != nullptr ? (HWND) associatedComponent->getWindowHandle() : nullptr; - } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsMessageBoxBase) }; +class PreVistaMessageBox : public WindowsMessageBoxBase +{ +public: + PreVistaMessageBox (const MessageBoxOptions& opts, + UINT extraFlags, + std::unique_ptr&& callback) + : WindowsMessageBoxBase (opts.getAssociatedComponent(), std::move (callback)), + flags (extraFlags | getMessageBoxFlags (opts.getIconType())), + title (opts.getTitle()), message (opts.getMessage()) + { + } + + int getResult() override + { + const auto result = MessageBox (getParentHWND(), message.toWideCharPointer(), title.toWideCharPointer(), flags); + + if (result == IDYES || result == IDOK) return 0; + if (result == IDNO && ((flags & 1) != 0)) return 1; + + return 2; + } + +private: + static UINT getMessageBoxFlags (MessageBoxIconType iconType) noexcept + { + // this window can get lost behind JUCE windows which are set to be alwaysOnTop + // so if there are any set it to be topmost + const auto topmostFlag = juce_areThereAnyAlwaysOnTopWindows() ? MB_TOPMOST : 0; + + const auto iconFlags = [&]() -> decltype (topmostFlag) + { + switch (iconType) + { + case MessageBoxIconType::QuestionIcon: return MB_ICONQUESTION; + case MessageBoxIconType::WarningIcon: return MB_ICONWARNING; + case MessageBoxIconType::InfoIcon: return MB_ICONINFORMATION; + case MessageBoxIconType::NoIcon: break; + } + + return 0; + }(); + + return static_cast (MB_TASKMODAL | MB_SETFOREGROUND | topmostFlag | iconFlags); + } + + const UINT flags; + const String title, message; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PreVistaMessageBox) +}; + +using TaskDialogIndirectFunc = HRESULT (WINAPI*) (const TASKDIALOGCONFIG*, INT*, INT*, BOOL*); +static TaskDialogIndirectFunc taskDialogIndirect = nullptr; + +class WindowsTaskDialog : public WindowsMessageBoxBase +{ +public: + WindowsTaskDialog (const MessageBoxOptions& opts, + std::unique_ptr&& callback) + : WindowsMessageBoxBase (opts.getAssociatedComponent(), std::move (callback)), + iconType (opts.getIconType()), + title (opts.getTitle()), message (opts.getMessage()), + button1 (opts.getButtonText (0)), button2 (opts.getButtonText (1)), button3 (opts.getButtonText (2)) + { + } + + int getResult() override + { + TASKDIALOGCONFIG config = { 0 }; + + config.cbSize = sizeof (config); + config.pszWindowTitle = title.toWideCharPointer(); + config.pszContent = message.toWideCharPointer(); + config.hInstance = (HINSTANCE) Process::getCurrentModuleInstanceHandle(); + + if (iconType == MessageBoxIconType::QuestionIcon) + { + if (auto* questionIcon = LoadIcon (nullptr, IDI_QUESTION)) + { + config.hMainIcon = questionIcon; + config.dwFlags |= TDF_USE_HICON_MAIN; + } + } + else + { + auto icon = [this]() -> LPWSTR + { + switch (iconType) + { + case MessageBoxIconType::WarningIcon: return TD_WARNING_ICON; + case MessageBoxIconType::InfoIcon: return TD_INFORMATION_ICON; + + case MessageBoxIconType::QuestionIcon: JUCE_FALLTHROUGH + case MessageBoxIconType::NoIcon: + break; + } + + return nullptr; + }(); + + if (icon != nullptr) + config.pszMainIcon = icon; + } + + std::vector buttons; + + for (const auto* buttonText : { &button1, &button2, &button3 }) + if (buttonText->isNotEmpty()) + buttons.push_back ({ (int) buttons.size(), buttonText->toWideCharPointer() }); + + config.pButtons = buttons.data(); + config.cButtons = (UINT) buttons.size(); + + int buttonIndex = 0; + taskDialogIndirect (&config, &buttonIndex, nullptr, nullptr); + + return buttonIndex; + } + + static bool loadTaskDialog() + { + static bool hasChecked = false; + + if (! hasChecked) + { + hasChecked = true; + + const auto comctl = "Comctl32.dll"; + LoadLibraryA (comctl); + const auto comctlModule = GetModuleHandleA (comctl); + + if (comctlModule != nullptr) + taskDialogIndirect = (TaskDialogIndirectFunc) GetProcAddress (comctlModule, "TaskDialogIndirect"); + } + + return taskDialogIndirect != nullptr; + } + +private: + MessageBoxIconType iconType; + String title, message, button1, button2, button3; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTaskDialog) +}; + +static std::unique_ptr createMessageBox (const MessageBoxOptions& options, + std::unique_ptr callback) +{ + std::unique_ptr messageBox; + + if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista + && WindowsTaskDialog::loadTaskDialog()) + { + messageBox.reset (new WindowsTaskDialog (options, std::move (callback))); + } + else + { + const auto extraFlags = [&options] + { + const auto numButtons = options.getNumButtons(); + + if (numButtons == 3) + return MB_YESNOCANCEL; + + if (numButtons == 2) + return options.getButtonText (0) == "OK" ? MB_OKCANCEL + : MB_YESNO; + + return MB_OK; + }(); + + messageBox.reset (new PreVistaMessageBox (options, (UINT) extraFlags, std::move (callback))); + } + + return messageBox; +} + +static int showDialog (const MessageBoxOptions& options, + std::unique_ptr callback, + Async async) +{ + auto messageBox = createMessageBox (options, std::move (callback)); + + #if JUCE_MODAL_LOOPS_PERMITTED + if (async == Async::no) + return messageBox->getResult(); + #endif + + ignoreUnused (async); + + messageBox->triggerAsyncUpdate(); + messageBox.release(); + + return 0; +} + #if JUCE_MODAL_LOOPS_PERMITTED -void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType, +void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType iconType, const String& title, const String& message, Component* associatedComponent) { - WindowsMessageBox box (iconType, title, message, associatedComponent, MB_OK, nullptr, false); - (void) box.getResult(); + showDialog (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (TRANS("OK")) + .withAssociatedComponent (associatedComponent), + nullptr, + Async::no); +} + +int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options) +{ + return showDialog (options, nullptr, Async::no); } #endif -void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType, +void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType iconType, const String& title, const String& message, Component* associatedComponent, ModalComponentManager::Callback* callback) { - new WindowsMessageBox (iconType, title, message, associatedComponent, MB_OK, callback, true); + showDialog (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (TRANS("OK")) + .withAssociatedComponent (associatedComponent), + rawToUniquePtr (AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::messageBox)), + Async::yes); } -bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType, +bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType iconType, const String& title, const String& message, Component* associatedComponent, ModalComponentManager::Callback* callback) { - std::unique_ptr mb (new WindowsMessageBox (iconType, title, message, associatedComponent, - MB_OKCANCEL, callback, callback != nullptr)); - if (callback == nullptr) - return mb->getResult() != 0; - - mb.release(); - return false; + return showDialog (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (TRANS("OK")) + .withButton (TRANS("Cancel")) + .withAssociatedComponent (associatedComponent), + rawToUniquePtr (AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::okCancel)), + callback != nullptr ? Async::yes : Async::no) == 1; } -int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType, +int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType iconType, const String& title, const String& message, Component* associatedComponent, ModalComponentManager::Callback* callback) { - std::unique_ptr mb (new WindowsMessageBox (iconType, title, message, associatedComponent, - MB_YESNOCANCEL, callback, callback != nullptr)); - if (callback == nullptr) - return mb->getResult(); - - mb.release(); - return 0; + return showDialog (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (TRANS("Yes")) + .withButton (TRANS("No")) + .withButton (TRANS("Cancel")) + .withAssociatedComponent (associatedComponent), + rawToUniquePtr (AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::yesNoCancel)), + callback != nullptr ? Async::yes : Async::no); } -int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType iconType, +int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType iconType, const String& title, const String& message, Component* associatedComponent, ModalComponentManager::Callback* callback) { - std::unique_ptr mb (new WindowsMessageBox (iconType, title, message, associatedComponent, - MB_YESNO, callback, callback != nullptr)); - if (callback == nullptr) - return mb->getResult(); + return showDialog (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (TRANS("Yes")) + .withButton (TRANS("No")) + .withAssociatedComponent (associatedComponent), + rawToUniquePtr (AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::okCancel)), + callback != nullptr ? Async::yes : Async::no); +} - mb.release(); - return 0; +void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, + ModalComponentManager::Callback* callback) +{ + showDialog (options, rawToUniquePtr (callback), Async::yes); +} + +void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, + std::function callback) +{ + showAsync (options, ModalCallbackFunction::create (callback)); } //============================================================================== diff --git a/modules/juce_gui_basics/windows/juce_AlertWindow.cpp b/modules/juce_gui_basics/windows/juce_AlertWindow.cpp index bf4861ac39..8f4c8e4c4f 100644 --- a/modules/juce_gui_basics/windows/juce_AlertWindow.cpp +++ b/modules/juce_gui_basics/windows/juce_AlertWindow.cpp @@ -38,7 +38,7 @@ static juce_wchar getDefaultPasswordChar() noexcept //============================================================================== AlertWindow::AlertWindow (const String& title, const String& message, - AlertIconType iconType, + MessageBoxIconType iconType, Component* comp) : TopLevelWindow (title, true), alertIconType (iconType), @@ -561,20 +561,21 @@ int AlertWindow::getDesktopWindowStyleFlags() const return getLookAndFeel().getAlertBoxWindowFlags(); } +enum class Async { no, yes }; + //============================================================================== class AlertWindowInfo { public: - AlertWindowInfo (const String& t, const String& m, Component* component, - AlertWindow::AlertIconType icon, int numButts, - ModalComponentManager::Callback* cb, bool runModally) - : title (t), message (m), iconType (icon), numButtons (numButts), - associatedComponent (component), callback (cb), modal (runModally) + AlertWindowInfo (const MessageBoxOptions& opts, + std::unique_ptr&& cb, + Async showAsync) + : options (opts), + callback (std::move (cb)), + async (showAsync) { } - String title, message, button1, button2, button3; - int invoke() const { MessageManager::getInstance()->callFunctionOnMessageThread (showCallback, (void*) this); @@ -582,68 +583,113 @@ public: } private: - AlertWindow::AlertIconType iconType; - int numButtons, returnValue = 0; - WeakReference associatedComponent; - ModalComponentManager::Callback* callback; - bool modal; + static void* showCallback (void* userData) + { + static_cast (userData)->show(); + return nullptr; + } void show() { - auto& lf = associatedComponent != nullptr ? associatedComponent->getLookAndFeel() - : LookAndFeel::getDefaultLookAndFeel(); + auto* component = options.getAssociatedComponent(); - std::unique_ptr alertBox (lf.createAlertWindow (title, message, button1, button2, button3, - iconType, numButtons, associatedComponent)); + auto& lf = (component != nullptr ? component->getLookAndFeel() + : LookAndFeel::getDefaultLookAndFeel()); + + std::unique_ptr alertBox (lf.createAlertWindow (options.getTitle(), options.getMessage(), + options.getButtonText (0), options.getButtonText (1), options.getButtonText (2), + options.getIconType(), options.getNumButtons(), component)); jassert (alertBox != nullptr); // you have to return one of these! alertBox->setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows()); #if JUCE_MODAL_LOOPS_PERMITTED - if (modal) - { + if (async == Async::no) returnValue = alertBox->runModalLoop(); - } else #endif { - ignoreUnused (modal); + ignoreUnused (async); - alertBox->enterModalState (true, callback, true); + alertBox->enterModalState (true, callback.release(), true); alertBox.release(); } } - static void* showCallback (void* userData) - { - static_cast (userData)->show(); - return nullptr; - } + MessageBoxOptions options; + std::unique_ptr callback; + const Async async; + int returnValue = 0; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlertWindowInfo) }; +namespace AlertWindowMappings +{ + static int messageBox (int) { return 0; } + static int okCancel (int buttonIndex) { return buttonIndex == 0 ? 1 : 0; } + static int yesNoCancel (int buttonIndex) { return buttonIndex == 2 ? 0 : buttonIndex + 1; } + + ModalComponentManager::Callback* getWrappedCallback (ModalComponentManager::Callback* callbackIn, + std::function mapFn) + { + if (callbackIn == nullptr) + return nullptr; + + auto wrappedCallback = [innerCallback = rawToUniquePtr (callbackIn), mapFn] (int buttonIndex) + { + innerCallback->modalStateFinished (mapFn (buttonIndex)); + }; + + return ModalCallbackFunction::create (std::move (wrappedCallback)); + } +} + #if JUCE_MODAL_LOOPS_PERMITTED -void AlertWindow::showMessageBox (AlertIconType iconType, +void AlertWindow::showMessageBox (MessageBoxIconType iconType, const String& title, const String& message, const String& buttonText, Component* associatedComponent) { - if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows()) - { - NativeMessageBox::showMessageBox (iconType, title, message, associatedComponent); - } - else - { - AlertWindowInfo info (title, message, associatedComponent, iconType, 1, nullptr, true); - info.button1 = buttonText.isEmpty() ? TRANS("OK") : buttonText; + show (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (buttonText.isEmpty() ? TRANS("OK") : buttonText) + .withAssociatedComponent (associatedComponent)); +} - info.invoke(); - } +int AlertWindow::show (const MessageBoxOptions& options) +{ + if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows()) + return NativeMessageBox::show (options); + + AlertWindowInfo info (options, nullptr, Async::no); + return info.invoke(); } #endif -void AlertWindow::showMessageBoxAsync (AlertIconType iconType, +void AlertWindow::showAsync (const MessageBoxOptions& options, ModalComponentManager::Callback* callback) +{ + if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows()) + { + NativeMessageBox::showAsync (options, callback); + } + else + { + AlertWindowInfo info (options, rawToUniquePtr (callback), Async::yes); + info.invoke(); + } +} + +void AlertWindow::showAsync (const MessageBoxOptions& options, std::function callback) +{ + showAsync (options, ModalCallbackFunction::create (callback)); +} + +void AlertWindow::showMessageBoxAsync (MessageBoxIconType iconType, const String& title, const String& message, const String& buttonText, @@ -651,19 +697,39 @@ void AlertWindow::showMessageBoxAsync (AlertIconType iconType, ModalComponentManager::Callback* callback) { if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows()) - { - NativeMessageBox::showMessageBoxAsync (iconType, title, message, associatedComponent, callback); - } - else - { - AlertWindowInfo info (title, message, associatedComponent, iconType, 1, callback, false); - info.button1 = buttonText.isEmpty() ? TRANS("OK") : buttonText; + callback = AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::messageBox); - info.invoke(); - } + showAsync (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (buttonText.isEmpty() ? TRANS("OK") : buttonText) + .withAssociatedComponent (associatedComponent), + callback); } -bool AlertWindow::showOkCancelBox (AlertIconType iconType, +static int showMaybeAsync (const MessageBoxOptions& options, + std::unique_ptr callback) +{ + const auto showAsync = (callback != nullptr ? Async::yes + : Async::no); + + if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows()) + { + #if JUCE_MODAL_LOOPS_PERMITTED + if (showAsync == Async::no) + return NativeMessageBox::show (options); + #endif + + NativeMessageBox::showAsync (options, callback.release()); + return false; + } + + AlertWindowInfo info (options, std::move (callback), showAsync); + return info.invoke(); +} + +bool AlertWindow::showOkCancelBox (MessageBoxIconType iconType, const String& title, const String& message, const String& button1Text, @@ -672,16 +738,19 @@ bool AlertWindow::showOkCancelBox (AlertIconType iconType, ModalComponentManager::Callback* callback) { if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows()) - return NativeMessageBox::showOkCancelBox (iconType, title, message, associatedComponent, callback); + callback = AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::okCancel); - AlertWindowInfo info (title, message, associatedComponent, iconType, 2, callback, callback == nullptr); - info.button1 = button1Text.isEmpty() ? TRANS("OK") : button1Text; - info.button2 = button2Text.isEmpty() ? TRANS("Cancel") : button2Text; - - return info.invoke() != 0; + return showMaybeAsync (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (button1Text.isEmpty() ? TRANS("OK") : button1Text) + .withButton (button2Text.isEmpty() ? TRANS("Cancel") : button2Text) + .withAssociatedComponent (associatedComponent), + rawToUniquePtr (callback)) == 1; } -int AlertWindow::showYesNoCancelBox (AlertIconType iconType, +int AlertWindow::showYesNoCancelBox (MessageBoxIconType iconType, const String& title, const String& message, const String& button1Text, @@ -691,29 +760,19 @@ int AlertWindow::showYesNoCancelBox (AlertIconType iconType, ModalComponentManager::Callback* callback) { if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows()) - return NativeMessageBox::showYesNoCancelBox (iconType, title, message, associatedComponent, callback); + callback = AlertWindowMappings::getWrappedCallback (callback, AlertWindowMappings::yesNoCancel); - AlertWindowInfo info (title, message, associatedComponent, iconType, 3, callback, callback == nullptr); - info.button1 = button1Text.isEmpty() ? TRANS("Yes") : button1Text; - info.button2 = button2Text.isEmpty() ? TRANS("No") : button2Text; - info.button3 = button3Text.isEmpty() ? TRANS("Cancel") : button3Text; - - return info.invoke(); + return showMaybeAsync (MessageBoxOptions() + .withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (button1Text.isEmpty() ? TRANS("Yes") : button1Text) + .withButton (button2Text.isEmpty() ? TRANS("No") : button2Text) + .withButton (button3Text.isEmpty() ? TRANS("Cancel") : button3Text) + .withAssociatedComponent (associatedComponent), + rawToUniquePtr (callback)); } -#if JUCE_MODAL_LOOPS_PERMITTED -bool AlertWindow::showNativeDialogBox (const String& title, - const String& bodyText, - bool isOkCancel) -{ - if (isOkCancel) - return NativeMessageBox::showOkCancelBox (AlertWindow::NoIcon, title, bodyText); - - NativeMessageBox::showMessageBox (AlertWindow::NoIcon, title, bodyText); - return true; -} -#endif - //============================================================================== std::unique_ptr AlertWindow::createAccessibilityHandler() { diff --git a/modules/juce_gui_basics/windows/juce_AlertWindow.h b/modules/juce_gui_basics/windows/juce_AlertWindow.h index de8316430a..7c9f6331b8 100644 --- a/modules/juce_gui_basics/windows/juce_AlertWindow.h +++ b/modules/juce_gui_basics/windows/juce_AlertWindow.h @@ -44,20 +44,6 @@ namespace juce class JUCE_API AlertWindow : public TopLevelWindow { public: - //============================================================================== - /** The type of icon to show in the dialog box. */ - enum AlertIconType - { - NoIcon, /**< No icon will be shown on the dialog box. */ - QuestionIcon, /**< A question-mark icon, for dialog boxes that need the - user to answer a question. */ - WarningIcon, /**< An exclamation mark to indicate that the dialog is a - warning about something and shouldn't be ignored. */ - InfoIcon /**< An icon that indicates that the dialog box is just - giving the user some information, which doesn't require - a response from them. */ - }; - //============================================================================== /** Creates an AlertWindow. @@ -71,7 +57,7 @@ public: */ AlertWindow (const String& title, const String& message, - AlertIconType iconType, + MessageBoxIconType iconType, Component* associatedComponent = nullptr); /** Destroys the AlertWindow */ @@ -80,7 +66,7 @@ public: //============================================================================== /** Returns the type of alert icon that was specified when the window was created. */ - AlertIconType getAlertType() const noexcept { return alertIconType; } + MessageBoxIconType getAlertType() const noexcept { return alertIconType; } //============================================================================== /** Changes the dialog box's message. @@ -226,8 +212,6 @@ public: bool containsAnyExtraComponents() const; //============================================================================== - // easy-to-use message box functions: - #if JUCE_MODAL_LOOPS_PERMITTED /** Shows a dialog box that just has a message and a single button to get rid of it. @@ -244,13 +228,57 @@ public: alert window should be associated with. Depending on the look and feel, this might be used for positioning of the alert window. */ - static void JUCE_CALLTYPE showMessageBox (AlertIconType iconType, + static void JUCE_CALLTYPE showMessageBox (MessageBoxIconType iconType, const String& title, const String& message, const String& buttonText = String(), Component* associatedComponent = nullptr); + + /** Shows a dialog box using the specified options. + + The box is shown modally, and the method will block until the user dismisses it. + + @param options the options to use when creating the dialog. + + @returns the index of the button that was clicked. + + @see MessageBoxOptions + */ + static int JUCE_CALLTYPE show (const MessageBoxOptions& options); #endif + /** Shows a dialog box using the specified options. + + The box will be displayed and placed into a modal state, but this method will return + immediately, and the callback will be invoked later when the user dismisses the box. + + @param options the options to use when creating the dialog. + @param callback if this is non-null, the callback will receive a call to its + modalStateFinished() when the box is dismissed with the index of the + button that was clicked as its argument. + The callback object will be owned and deleted by the system, so make sure + that it works safely and doesn't keep any references to objects that might + be deleted before it gets called. + + @see MessageBoxOptions + */ + static void JUCE_CALLTYPE showAsync (const MessageBoxOptions& options, + ModalComponentManager::Callback* callback); + + /** Shows a dialog box using the specified options. + + The box will be displayed and placed into a modal state, but this method will return + immediately, and the callback will be invoked later when the user dismisses the box. + + @param options the options to use when creating the dialog. + @param callback if this is non-null, the callback will be called when the box is + dismissed with the index of the button that was clicked as its argument. + + @see MessageBoxOptions + */ + static void JUCE_CALLTYPE showAsync (const MessageBoxOptions& options, + std::function callback); + /** Shows a dialog box that just has a message and a single button to get rid of it. The box will be displayed and placed into a modal state, but this method will @@ -272,7 +300,7 @@ public: safely and doesn't keep any references to objects that might be deleted before it gets called. */ - static void JUCE_CALLTYPE showMessageBoxAsync (AlertIconType iconType, + static void JUCE_CALLTYPE showMessageBoxAsync (MessageBoxIconType iconType, const String& title, const String& message, const String& buttonText = String(), @@ -314,20 +342,20 @@ public: is not null, the method always returns false, and the user's choice is delivered later by the callback. */ - static bool JUCE_CALLTYPE showOkCancelBox (AlertIconType iconType, + static bool JUCE_CALLTYPE showOkCancelBox (MessageBoxIconType iconType, const String& title, const String& message, - #if JUCE_MODAL_LOOPS_PERMITTED + #if JUCE_MODAL_LOOPS_PERMITTED const String& button1Text = String(), const String& button2Text = String(), Component* associatedComponent = nullptr, ModalComponentManager::Callback* callback = nullptr); - #else + #else const String& button1Text, const String& button2Text, Component* associatedComponent, ModalComponentManager::Callback* callback); - #endif + #endif /** Shows a dialog box with three buttons. @@ -368,24 +396,27 @@ public: - 1 if the first button was pressed (normally used for 'yes') - 2 if the middle button was pressed (normally used for 'no') */ - static int JUCE_CALLTYPE showYesNoCancelBox (AlertIconType iconType, + static int JUCE_CALLTYPE showYesNoCancelBox (MessageBoxIconType iconType, const String& title, const String& message, - #if JUCE_MODAL_LOOPS_PERMITTED + #if JUCE_MODAL_LOOPS_PERMITTED const String& button1Text = String(), const String& button2Text = String(), const String& button3Text = String(), Component* associatedComponent = nullptr, ModalComponentManager::Callback* callback = nullptr); - #else + #else const String& button1Text, const String& button2Text, const String& button3Text, Component* associatedComponent, ModalComponentManager::Callback* callback); - #endif + #endif //============================================================================== + #if JUCE_MODAL_LOOPS_PERMITTED + // This has been deprecated, use the NativeMessageBox methods instead for more options. + /** Shows an operating-system native dialog box. @param title the title to use at the top @@ -394,10 +425,9 @@ public: it'll show a box with just an ok button @returns true if the ok button was pressed, false if they pressed cancel. */ - #if JUCE_MODAL_LOOPS_PERMITTED - static bool JUCE_CALLTYPE showNativeDialogBox (const String& title, - const String& bodyText, - bool isOkCancel); + JUCE_DEPRECATED (static bool JUCE_CALLTYPE showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel)); #endif @@ -428,7 +458,7 @@ public: const String& button1, const String& button2, const String& button3, - AlertWindow::AlertIconType iconType, + MessageBoxIconType iconType, int numButtons, Component* associatedComponent) = 0; @@ -444,6 +474,14 @@ public: virtual Font getAlertWindowFont() = 0; }; + //============================================================================== + using AlertIconType = MessageBoxIconType; + + static constexpr auto NoIcon = MessageBoxIconType::NoIcon; + static constexpr auto QuestionIcon = MessageBoxIconType::QuestionIcon; + static constexpr auto WarningIcon = MessageBoxIconType::WarningIcon; + static constexpr auto InfoIcon = MessageBoxIconType::InfoIcon; + protected: //============================================================================== /** @internal */ @@ -470,7 +508,7 @@ private: String text; TextLayout textLayout; Label accessibleMessageLabel; - AlertIconType alertIconType; + MessageBoxIconType alertIconType; ComponentBoundsConstrainer constrainer; ComponentDragger dragger; Rectangle textArea; diff --git a/modules/juce_gui_basics/windows/juce_MessageBoxOptions.h b/modules/juce_gui_basics/windows/juce_MessageBoxOptions.h new file mode 100644 index 0000000000..227e43bd31 --- /dev/null +++ b/modules/juce_gui_basics/windows/juce_MessageBoxOptions.h @@ -0,0 +1,141 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 6 End-User License + Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). + + End User License Agreement: www.juce.com/juce-6-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +/** The type of icon to show in the dialog box. */ +enum class MessageBoxIconType +{ + NoIcon, /**< No icon will be shown on the dialog box. */ + QuestionIcon, /**< A question-mark icon, for dialog boxes that need the + user to answer a question. */ + WarningIcon, /**< An exclamation mark to indicate that the dialog is a + warning about something and shouldn't be ignored. */ + InfoIcon /**< An icon that indicates that the dialog box is just + giving the user some information, which doesn't require + a response from them. */ +}; + +//============================================================================== +/** Class used to create a set of options to pass to the AlertWindow and NativeMessageBox + methods for showing dialog boxes. + + You can chain together a series of calls to this class's methods to create + a set of whatever options you want to specify. + + E.g. @code + AlertWindow::showAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("A Title") + .withMessage ("A message.") + .withButton ("OK") + .withButton ("Cancel") + .withAssociatedComponent (myComp), + myCallback); + @endcode +*/ +class JUCE_API MessageBoxOptions +{ +public: + MessageBoxOptions() = default; + MessageBoxOptions (const MessageBoxOptions&) = default; + MessageBoxOptions& operator= (const MessageBoxOptions&) = default; + + //============================================================================== + /** Sets the type of icon that should be used for the dialog box. */ + MessageBoxOptions withIconType (MessageBoxIconType type) const { return with (*this, &MessageBoxOptions::iconType, type); } + + /** Sets the title of the dialog box. */ + MessageBoxOptions withTitle (const String& boxTitle) const { return with (*this, &MessageBoxOptions::title, boxTitle); } + + /** Sets the message that should be displayed in the dialog box. */ + MessageBoxOptions withMessage (const String& boxMessage) const { return with (*this, &MessageBoxOptions::message, boxMessage); } + + /** If the string passed in is not empty, this will add a button to the + dialog box with the specified text. + + Generally up to 3 buttons are supported for dialog boxes, so adding any more + than this may have no effect. + */ + MessageBoxOptions withButton (const String& text) const { auto copy = *this; copy.buttons.add (text); return copy; } + + /** The component that the dialog box should be associated with. */ + MessageBoxOptions withAssociatedComponent (Component* component) const { return with (*this, &MessageBoxOptions::associatedComponent, component); } + + //============================================================================== + /** Returns the icon type of the dialog box. + + @see withIconType + */ + MessageBoxIconType getIconType() const noexcept { return iconType; } + + /** Returns the title of the dialog box. + + @see withTitle + */ + String getTitle() const { return title; } + + /** Returns the message of the dialog box. + + @see withMessage + */ + String getMessage() const { return message; } + + /** Returns the number of buttons that have been added to the dialog box. + + @see withButtonText + */ + int getNumButtons() const noexcept { return buttons.size(); } + + /** Returns the text that has been set for one of the buttons of the dialog box. + + @see withButtonText, getNumButtons + */ + String getButtonText (int buttonIndex) const { return buttons[buttonIndex]; } + + /** Returns the component that the dialog box is associated with. + + @see withAssociatedComponent + */ + Component* getAssociatedComponent() const noexcept { return associatedComponent; } + +private: + //============================================================================== + template + static MessageBoxOptions with (MessageBoxOptions options, Member&& member, Item&& item) + { + options.*member = std::forward (item); + return options; + } + + //============================================================================== + MessageBoxIconType iconType = MessageBoxIconType::InfoIcon; + String title, message; + StringArray buttons; + WeakReference associatedComponent; +}; + +} // namespace juce diff --git a/modules/juce_gui_basics/windows/juce_NativeMessageBox.h b/modules/juce_gui_basics/windows/juce_NativeMessageBox.h index 70b097a59f..8f249da490 100644 --- a/modules/juce_gui_basics/windows/juce_NativeMessageBox.h +++ b/modules/juce_gui_basics/windows/juce_NativeMessageBox.h @@ -35,33 +35,77 @@ namespace juce class NativeMessageBox { public: + #if JUCE_MODAL_LOOPS_PERMITTED /** Shows a dialog box that just has a message and a single 'ok' button to close it. The box is shown modally, and the method will block until the user has clicked its button (or pressed the escape or return keys). - @param iconType the type of icon to show - @param title the headline to show at the top of the box - @param message a longer, more descriptive message to show underneath the title + @param iconType the type of icon to show. + @param title the headline to show at the top of the box. + @param message a longer, more descriptive message to show underneath the title. @param associatedComponent if this is non-null, it specifies the component that the alert window should be associated with. Depending on the look and feel, this might be used for positioning of the alert window. */ - #if JUCE_MODAL_LOOPS_PERMITTED - static void JUCE_CALLTYPE showMessageBox (AlertWindow::AlertIconType iconType, + static void JUCE_CALLTYPE showMessageBox (MessageBoxIconType iconType, const String& title, const String& message, Component* associatedComponent = nullptr); + + /** Shows a dialog box using the specified options. + + The box is shown modally, and the method will block until the user dismisses it. + + @param options the options to use when creating the dialog. + + @returns the index of the button that was clicked. + + @see MessageBoxOptions + */ + static int JUCE_CALLTYPE show (const MessageBoxOptions& options); #endif + /** Shows a dialog box using the specified options. + + The box will be displayed and placed into a modal state, but this method will return + immediately, and the callback will be invoked later when the user dismisses the box. + + @param options the options to use when creating the dialog. + @param callback if this is non-null, the callback will receive a call to its + modalStateFinished() when the box is dismissed with the index of the + button that was clicked as its argument. + The callback object will be owned and deleted by the system, so make sure + that it works safely and doesn't keep any references to objects that might + be deleted before it gets called. + + @see MessageBoxOptions + */ + static void JUCE_CALLTYPE showAsync (const MessageBoxOptions& options, + ModalComponentManager::Callback* callback); + + /** Shows a dialog box using the specified options. + + The box will be displayed and placed into a modal state, but this method will return + immediately, and the callback will be invoked later when the user dismisses the box. + + @param options the options to use when creating the dialog. + @param callback if this is non-null, the callback will be called when the box is + dismissed with the index of the button that was clicked as its argument. + + @see MessageBoxOptions + */ + static void JUCE_CALLTYPE showAsync (const MessageBoxOptions& options, + std::function callback); + /** Shows a dialog box that just has a message and a single 'ok' button to close it. The box will be displayed and placed into a modal state, but this method will return immediately, and the callback will be invoked later when the user dismisses the box. - @param iconType the type of icon to show - @param title the headline to show at the top of the box - @param message a longer, more descriptive message to show underneath the title + @param iconType the type of icon to show. + @param title the headline to show at the top of the box. + @param message a longer, more descriptive message to show underneath the title. @param associatedComponent if this is non-null, it specifies the component that the alert window should be associated with. Depending on the look and feel, this might be used for positioning of the alert window. @@ -74,26 +118,26 @@ public: @see ModalCallbackFunction */ - static void JUCE_CALLTYPE showMessageBoxAsync (AlertWindow::AlertIconType iconType, - const String& title, - const String& message, - Component* associatedComponent = nullptr, - ModalComponentManager::Callback* callback = nullptr); + static void JUCE_CALLTYPE showMessageBoxAsync (MessageBoxIconType iconType, + const String& title, + const String& message, + Component* associatedComponent = nullptr, + ModalComponentManager::Callback* callback = nullptr); /** Shows a dialog box with two buttons. Ideal for ok/cancel or yes/no choices. The return key can also be used to trigger the first button, and the escape key for the second button. - If the callback parameter is null, the box is shown modally, and the method will - block until the user has clicked the button (or pressed the escape or return keys). - If the callback parameter is non-null, the box will be displayed and placed into a - modal state, but this method will return immediately, and the callback will be invoked + If the callback parameter is null and modal loops are enabled, the box is shown modally, + and the method will block until the user has clicked the button (or pressed the escape or + return keys). If the callback parameter is non-null, the box will be displayed and placed + into a modal state, but this method will return immediately, and the callback will be invoked later when the user dismisses the box. - @param iconType the type of icon to show - @param title the headline to show at the top of the box - @param message a longer, more descriptive message to show underneath the title + @param iconType the type of icon to show. + @param title the headline to show at the top of the box. + @param message a longer, more descriptive message to show underneath the title. @param associatedComponent if this is non-null, it specifies the component that the alert window should be associated with. Depending on the look and feel, this might be used for positioning of the alert window. @@ -111,16 +155,16 @@ public: @see ModalCallbackFunction */ - static bool JUCE_CALLTYPE showOkCancelBox (AlertWindow::AlertIconType iconType, + static bool JUCE_CALLTYPE showOkCancelBox (MessageBoxIconType iconType, const String& title, const String& message, - #if JUCE_MODAL_LOOPS_PERMITTED + #if JUCE_MODAL_LOOPS_PERMITTED Component* associatedComponent = nullptr, ModalComponentManager::Callback* callback = nullptr); - #else + #else Component* associatedComponent, ModalComponentManager::Callback* callback); - #endif + #endif /** Shows a dialog box with three buttons. @@ -128,15 +172,15 @@ public: The escape key can be used to trigger the third button. - If the callback parameter is null, the box is shown modally, and the method will - block until the user has clicked the button (or pressed the escape or return keys). - If the callback parameter is non-null, the box will be displayed and placed into a - modal state, but this method will return immediately, and the callback will be invoked + If the callback parameter is null and modal loops are enabled, the box is shown modally, + and the method will block until the user has clicked the button (or pressed the escape or + return keys). If the callback parameter is non-null, the box will be displayed and placed + into a modal state, but this method will return immediately, and the callback will be invoked later when the user dismisses the box. - @param iconType the type of icon to show - @param title the headline to show at the top of the box - @param message a longer, more descriptive message to show underneath the title + @param iconType the type of icon to show. + @param title the headline to show at the top of the box. + @param message a longer, more descriptive message to show underneath the title. @param associatedComponent if this is non-null, it specifies the component that the alert window should be associated with. Depending on the look and feel, this might be used for positioning of the alert window. @@ -157,16 +201,16 @@ public: @see ModalCallbackFunction */ - static int JUCE_CALLTYPE showYesNoCancelBox (AlertWindow::AlertIconType iconType, + static int JUCE_CALLTYPE showYesNoCancelBox (MessageBoxIconType iconType, const String& title, const String& message, - #if JUCE_MODAL_LOOPS_PERMITTED + #if JUCE_MODAL_LOOPS_PERMITTED Component* associatedComponent = nullptr, ModalComponentManager::Callback* callback = nullptr); - #else + #else Component* associatedComponent, ModalComponentManager::Callback* callback); - #endif + #endif /** Shows a dialog box with two buttons. @@ -174,15 +218,15 @@ public: The escape key can be used to trigger the no button. - If the callback parameter is null, the box is shown modally, and the method will - block until the user has clicked the button (or pressed the escape or return keys). - If the callback parameter is non-null, the box will be displayed and placed into a - modal state, but this method will return immediately, and the callback will be invoked + If the callback parameter is null and modal loops are enabled, the box is shown modally, + and the method will block until the user has clicked the button (or pressed the escape or + return keys). If the callback parameter is non-null, the box will be displayed and placed + into a modal state, but this method will return immediately, and the callback will be invoked later when the user dismisses the box. - @param iconType the type of icon to show - @param title the headline to show at the top of the box - @param message a longer, more descriptive message to show underneath the title + @param iconType the type of icon to show. + @param title the headline to show at the top of the box. + @param message a longer, more descriptive message to show underneath the title. @param associatedComponent if this is non-null, it specifies the component that the alert window should be associated with. Depending on the look and feel, this might be used for positioning of the alert window. @@ -202,7 +246,7 @@ public: @see ModalCallbackFunction */ - static int JUCE_CALLTYPE showYesNoBox (AlertWindow::AlertIconType iconType, + static int JUCE_CALLTYPE showYesNoBox (MessageBoxIconType iconType, const String& title, const String& message, #if JUCE_MODAL_LOOPS_PERMITTED diff --git a/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp b/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp index b2d0d497ef..1b891b6f0f 100644 --- a/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp +++ b/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp @@ -41,7 +41,7 @@ ThreadWithProgressWindow::ThreadWithProgressWindow (const String& title, .createAlertWindow (title, {}, cancelButtonText.isEmpty() ? TRANS("Cancel") : cancelButtonText, - {}, {}, AlertWindow::NoIcon, hasCancelButton ? 1 : 0, + {}, {}, MessageBoxIconType::NoIcon, hasCancelButton ? 1 : 0, componentToCentreAround)); // if there are no buttons, we won't allow the user to interrupt the thread. diff --git a/modules/juce_gui_extra/documents/juce_FileBasedDocument.cpp b/modules/juce_gui_extra/documents/juce_FileBasedDocument.cpp index 693e2a35fd..8c3dca9bda 100644 --- a/modules/juce_gui_extra/documents/juce_FileBasedDocument.cpp +++ b/modules/juce_gui_extra/documents/juce_FileBasedDocument.cpp @@ -339,7 +339,7 @@ private: auto result = Result::fail (TRANS ("The file doesn't exist")); if (showMessageOnFailure) - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS ("Failed to open file..."), TRANS ("There was an error while trying to load the file: FLNM") .replace ("FLNM", "\n" + newFile.getFullPathName()) @@ -448,7 +448,7 @@ private: callback (parent, alertResult); }); - return AlertWindow::showYesNoCancelBox (AlertWindow::QuestionIcon, + return AlertWindow::showYesNoCancelBox (MessageBoxIconType::QuestionIcon, TRANS ("Closing document..."), TRANS ("Do you want to save the changes to \"DCNM\"?") .replace ("DCNM", document.getDocumentTitle()), @@ -510,7 +510,7 @@ private: MouseCursor::hideWaitCursor(); if (showMessageOnFailure) - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS ("Error writing to file..."), TRANS ("An error occurred while trying to save \"DCNM\" to the file: FLNM") .replace ("DCNM", parent->document.getDocumentTitle()) @@ -682,7 +682,7 @@ private: callback (parent, r == 1); }); - return AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + return AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, TRANS ("File already exists"), TRANS ("There's already a file called: FLNM") .replace ("FLNM", newFile.getFullPathName()) diff --git a/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp b/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp index ad958528e9..bdae6313b9 100644 --- a/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp +++ b/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp @@ -98,7 +98,7 @@ public: KeyEntryWindow (KeyMappingEditorComponent& kec) : AlertWindow (TRANS("New key-mapping"), TRANS("Please press a key combination now..."), - AlertWindow::NoIcon), + MessageBoxIconType::NoIcon), owner (kec) { addButton (TRANS("OK"), 1); @@ -165,7 +165,7 @@ public: } else { - AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, TRANS("Change key-mapping"), TRANS("This key is already assigned to the command \"CMDN\"") .replace ("CMDN", owner.getCommandManager().getNameOfCommand (previousCommand)) @@ -403,7 +403,7 @@ KeyMappingEditorComponent::KeyMappingEditorComponent (KeyPressMappingSet& mappin resetButton.onClick = [this] { - AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon, + AlertWindow::showOkCancelBox (MessageBoxIconType::QuestionIcon, TRANS("Reset to defaults"), TRANS("Are you sure you want to reset all the key-mappings to their default state?"), TRANS("Reset"), diff --git a/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockForm.cpp b/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockForm.cpp index 28b0c72505..a0687be28c 100644 --- a/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockForm.cpp +++ b/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockForm.cpp @@ -100,13 +100,13 @@ struct OnlineUnlockForm::OverlayComp : public Component, if (result.errorMessage.isNotEmpty()) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS("Registration Failed"), result.errorMessage); } else if (result.informativeMessage.isNotEmpty()) { - AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, + AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, TRANS("Registration Complete!"), result.informativeMessage); }