The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

462 lines
21KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE examples.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. The code included in this file is provided under the terms of the ISC license
  6. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  7. To use, copy, modify, and/or distribute this software for any purpose with or
  8. without fee is hereby granted provided that the above copyright notice and
  9. this permission notice appear in all copies.
  10. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
  11. WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
  12. PURPOSE, ARE DISCLAIMED.
  13. ==============================================================================
  14. */
  15. /*******************************************************************************
  16. The block below describes the properties of this PIP. A PIP is a short snippet
  17. of code that can be read by the Projucer and used to generate a JUCE project.
  18. BEGIN_JUCE_PIP_METADATA
  19. name: DialogsDemo
  20. version: 1.0.0
  21. vendor: JUCE
  22. website: http://juce.com
  23. description: Displays different types of dialog windows.
  24. dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
  25. juce_gui_basics, juce_gui_extra
  26. exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
  27. moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
  28. type: Component
  29. mainClass: DialogsDemo
  30. useLocalCopy: 1
  31. END_JUCE_PIP_METADATA
  32. *******************************************************************************/
  33. #pragma once
  34. #include "../Assets/DemoUtilities.h"
  35. //==============================================================================
  36. class DemoBackgroundThread : public ThreadWithProgressWindow
  37. {
  38. public:
  39. DemoBackgroundThread()
  40. : ThreadWithProgressWindow ("busy doing some important things...", true, true)
  41. {
  42. setStatusMessage ("Getting ready...");
  43. }
  44. void run() override
  45. {
  46. setProgress (-1.0); // setting a value beyond the range 0 -> 1 will show a spinning bar..
  47. setStatusMessage ("Preparing to do some stuff...");
  48. wait (2000);
  49. int thingsToDo = 10;
  50. for (int i = 0; i < thingsToDo; ++i)
  51. {
  52. // must check this as often as possible, because this is
  53. // how we know if the user's pressed 'cancel'
  54. if (threadShouldExit())
  55. return;
  56. // this will update the progress bar on the dialog box
  57. setProgress (i / (double) thingsToDo);
  58. setStatusMessage (String (thingsToDo - i) + " things left to do...");
  59. wait (500);
  60. }
  61. setProgress (-1.0); // setting a value beyond the range 0 -> 1 will show a spinning bar..
  62. setStatusMessage ("Finishing off the last few bits and pieces!");
  63. wait (2000);
  64. }
  65. // This method gets called on the message thread once our thread has finished..
  66. void threadComplete (bool userPressedCancel) override
  67. {
  68. if (userPressedCancel)
  69. {
  70. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  71. "Progress window",
  72. "You pressed cancel!");
  73. }
  74. else
  75. {
  76. // thread finished normally..
  77. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  78. "Progress window",
  79. "Thread finished ok!");
  80. }
  81. // ..and clean up by deleting our thread object..
  82. delete this;
  83. }
  84. };
  85. //==============================================================================
  86. class DialogsDemo : public Component
  87. {
  88. public:
  89. enum DialogType
  90. {
  91. plainAlertWindow,
  92. warningAlertWindow,
  93. infoAlertWindow,
  94. questionAlertWindow,
  95. okCancelAlertWindow,
  96. extraComponentsAlertWindow,
  97. calloutBoxWindow,
  98. progressWindow,
  99. loadChooser,
  100. loadWithPreviewChooser,
  101. directoryChooser,
  102. saveChooser,
  103. shareText,
  104. shareFile,
  105. shareImage,
  106. numDialogs
  107. };
  108. DialogsDemo()
  109. {
  110. setOpaque (true);
  111. addAndMakeVisible (nativeButton);
  112. nativeButton.setButtonText ("Use Native Windows");
  113. nativeButton.onClick = [this] { getLookAndFeel().setUsingNativeAlertWindows (nativeButton.getToggleState()); };
  114. StringArray windowNames { "Plain Alert Window", "Alert Window With Warning Icon", "Alert Window With Info Icon", "Alert Window With Question Icon",
  115. "OK Cancel Alert Window", "Alert Window With Extra Components", "CalloutBox", "Thread With Progress Window",
  116. "'Load' File Browser", "'Load' File Browser With Image Preview", "'Choose Directory' File Browser", "'Save' File Browser",
  117. "Share Text", "Share Files", "Share Images" };
  118. // warn in case we add any windows
  119. jassert (windowNames.size() == numDialogs);
  120. for (auto windowName : windowNames)
  121. {
  122. auto* newButton = new TextButton();
  123. addAndMakeVisible (windowButtons.add (newButton));
  124. newButton->setButtonText (windowName);
  125. auto index = windowNames.indexOf (windowName);
  126. newButton->onClick = [this, index, newButton] { showWindow (*newButton, static_cast<DialogType> (index)); };
  127. }
  128. setSize (500, 500);
  129. RuntimePermissions::request (RuntimePermissions::readExternalStorage,
  130. [] (bool granted)
  131. {
  132. if (! granted)
  133. {
  134. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  135. "Permissions warning",
  136. "External storage access permission not granted, some files"
  137. " may be inaccessible.");
  138. }
  139. });
  140. }
  141. //==============================================================================
  142. void paint (Graphics& g) override
  143. {
  144. g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
  145. }
  146. void resized() override
  147. {
  148. auto area = getLocalBounds().reduced (5, 15);
  149. Rectangle<int> topRow;
  150. for (auto* b : windowButtons)
  151. {
  152. auto index = windowButtons.indexOf (b);
  153. if (topRow.getWidth() < 10 || index == loadChooser)
  154. topRow = area.removeFromTop (26);
  155. if (index == progressWindow)
  156. area.removeFromTop (20);
  157. b->setBounds (topRow.removeFromLeft (area.getWidth() / 2).reduced (4, 2));
  158. }
  159. area.removeFromTop (15);
  160. nativeButton.setBounds (area.removeFromTop (24));
  161. }
  162. private:
  163. OwnedArray<TextButton> windowButtons;
  164. ToggleButton nativeButton;
  165. static void alertBoxResultChosen (int result, DialogsDemo*)
  166. {
  167. AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, "Alert Box",
  168. "Result code: " + String (result));
  169. }
  170. void showWindow (Component& button, DialogType type)
  171. {
  172. if (type >= plainAlertWindow && type <= questionAlertWindow)
  173. {
  174. AlertWindow::AlertIconType icon = AlertWindow::NoIcon;
  175. if (type == warningAlertWindow) icon = AlertWindow::WarningIcon;
  176. if (type == infoAlertWindow) icon = AlertWindow::InfoIcon;
  177. if (type == questionAlertWindow) icon = AlertWindow::QuestionIcon;
  178. AlertWindow::showMessageBoxAsync (icon, "This is an AlertWindow",
  179. "And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah.",
  180. "OK");
  181. }
  182. else if (type == okCancelAlertWindow)
  183. {
  184. AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon, "This is an ok/cancel AlertWindow",
  185. "And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah.",
  186. {}, {}, {},
  187. ModalCallbackFunction::forComponent (alertBoxResultChosen, this));
  188. }
  189. else if (type == calloutBoxWindow)
  190. {
  191. auto colourSelector = std::make_unique<ColourSelector>();
  192. colourSelector->setName ("background");
  193. colourSelector->setCurrentColour (findColour (TextButton::buttonColourId));
  194. colourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
  195. colourSelector->setSize (300, 400);
  196. CallOutBox::launchAsynchronously (std::move (colourSelector), button.getScreenBounds(), nullptr);
  197. }
  198. else if (type == extraComponentsAlertWindow)
  199. {
  200. #if JUCE_MODAL_LOOPS_PERMITTED
  201. AlertWindow w ("AlertWindow demo..",
  202. "This AlertWindow has a couple of extra components added to show how to add drop-down lists and text entry boxes.",
  203. AlertWindow::QuestionIcon);
  204. w.addTextEditor ("text", "enter some text here", "text field:");
  205. w.addComboBox ("option", { "option 1", "option 2", "option 3", "option 4" }, "some options");
  206. w.addButton ("OK", 1, KeyPress (KeyPress::returnKey, 0, 0));
  207. w.addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey, 0, 0));
  208. if (w.runModalLoop() != 0) // is they picked 'ok'
  209. {
  210. // this is the item they chose in the drop-down list..
  211. auto optionIndexChosen = w.getComboBoxComponent ("option")->getSelectedItemIndex();
  212. ignoreUnused (optionIndexChosen);
  213. // this is the text they entered..
  214. auto text = w.getTextEditorContents ("text");
  215. }
  216. #endif
  217. }
  218. else if (type == progressWindow)
  219. {
  220. // This will launch our ThreadWithProgressWindow in a modal state. (Our subclass
  221. // will take care of deleting the object when the task has finished)
  222. (new DemoBackgroundThread())->launchThread();
  223. }
  224. else if (type >= loadChooser && type <= saveChooser)
  225. {
  226. auto useNativeVersion = nativeButton.getToggleState();
  227. if (type == loadChooser)
  228. {
  229. fc.reset (new FileChooser ("Choose a file to open...", File::getCurrentWorkingDirectory(),
  230. "*", useNativeVersion));
  231. fc->launchAsync (FileBrowserComponent::canSelectMultipleItems | FileBrowserComponent::openMode
  232. | FileBrowserComponent::canSelectFiles,
  233. [] (const FileChooser& chooser)
  234. {
  235. String chosen;
  236. auto results = chooser.getURLResults();
  237. for (auto result : results)
  238. chosen << (result.isLocalFile() ? result.getLocalFile().getFullPathName()
  239. : result.toString (false)) << "\n";
  240. AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
  241. "File Chooser...",
  242. "You picked: " + chosen);
  243. });
  244. }
  245. else if (type == loadWithPreviewChooser)
  246. {
  247. imagePreview.setSize (200, 200);
  248. fc.reset (new FileChooser ("Choose an image to open...", File::getCurrentWorkingDirectory(),
  249. "*.jpg;*.jpeg;*.png;*.gif", useNativeVersion));
  250. fc->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles
  251. | FileBrowserComponent::canSelectMultipleItems,
  252. [] (const FileChooser& chooser)
  253. {
  254. String chosen;
  255. auto results = chooser.getURLResults();
  256. for (auto result : results)
  257. chosen << (result.isLocalFile() ? result.getLocalFile().getFullPathName()
  258. : result.toString (false)) << "\n";
  259. AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
  260. "File Chooser...",
  261. "You picked: " + chosen);
  262. },
  263. &imagePreview);
  264. }
  265. else if (type == saveChooser)
  266. {
  267. auto fileToSave = File::createTempFile ("saveChooserDemo");
  268. if (fileToSave.createDirectory().wasOk())
  269. {
  270. fileToSave = fileToSave.getChildFile ("JUCE.png");
  271. fileToSave.deleteFile();
  272. FileOutputStream outStream (fileToSave);
  273. if (outStream.openedOk())
  274. if (auto inStream = createAssetInputStream ("juce_icon.png"))
  275. outStream.writeFromInputStream (*inStream, -1);
  276. }
  277. fc.reset (new FileChooser ("Choose a file to save...",
  278. File::getCurrentWorkingDirectory().getChildFile (fileToSave.getFileName()),
  279. "*", useNativeVersion));
  280. fc->launchAsync (FileBrowserComponent::saveMode | FileBrowserComponent::canSelectFiles,
  281. [fileToSave] (const FileChooser& chooser)
  282. {
  283. auto result = chooser.getURLResult();
  284. auto name = result.isEmpty() ? String()
  285. : (result.isLocalFile() ? result.getLocalFile().getFullPathName()
  286. : result.toString (true));
  287. // Android and iOS file choosers will create placeholder files for chosen
  288. // paths, so we may as well write into those files.
  289. #if JUCE_ANDROID || JUCE_IOS
  290. if (! result.isEmpty())
  291. {
  292. std::unique_ptr<InputStream> wi (fileToSave.createInputStream());
  293. std::unique_ptr<OutputStream> wo (result.createOutputStream());
  294. if (wi.get() != nullptr && wo.get() != nullptr)
  295. {
  296. auto numWritten = wo->writeFromInputStream (*wi, -1);
  297. jassert (numWritten > 0);
  298. ignoreUnused (numWritten);
  299. wo->flush();
  300. }
  301. }
  302. #endif
  303. AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
  304. "File Chooser...",
  305. "You picked: " + name);
  306. });
  307. }
  308. else if (type == directoryChooser)
  309. {
  310. fc.reset (new FileChooser ("Choose a directory...",
  311. File::getCurrentWorkingDirectory(),
  312. "*",
  313. useNativeVersion));
  314. fc->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories,
  315. [] (const FileChooser& chooser)
  316. {
  317. auto result = chooser.getURLResult();
  318. auto name = result.isLocalFile() ? result.getLocalFile().getFullPathName()
  319. : result.toString (true);
  320. AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
  321. "File Chooser...",
  322. "You picked: " + name);
  323. });
  324. }
  325. }
  326. else if (type == shareText)
  327. {
  328. ContentSharer::getInstance()->shareText ("I love JUCE!",
  329. [] (bool success, const String& error)
  330. {
  331. auto resultString = success ? String ("success") : ("failure\n (error: " + error + ")");
  332. AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, "Sharing Text Result",
  333. "Sharing text finished\nwith " + resultString);
  334. });
  335. }
  336. else if (type == shareFile)
  337. {
  338. File fileToSave = File::createTempFile ("DialogsDemoSharingTest");
  339. if (fileToSave.createDirectory().wasOk())
  340. {
  341. fileToSave = fileToSave.getChildFile ("SharingDemoFile.txt");
  342. fileToSave.replaceWithText ("Make it fast!");
  343. Array<URL> urls;
  344. urls.add (URL (fileToSave));
  345. ContentSharer::getInstance()->shareFiles (urls,
  346. [] (bool success, const String& error)
  347. {
  348. auto resultString = success ? String ("success") : ("failure\n (error: " + error + ")");
  349. AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
  350. "Sharing Files Result",
  351. "Sharing files finished\nwith " + resultString);
  352. });
  353. }
  354. }
  355. else if (type == shareImage)
  356. {
  357. auto myImage = getImageFromAssets ("juce_icon.png");
  358. Image myImage2 (Image::RGB, 500, 500, true);
  359. Graphics g (myImage2);
  360. g.setColour (Colours::green);
  361. ColourGradient gradient (Colours::yellow, 170, 170, Colours::cyan, 170, 20, true);
  362. g.setGradientFill (gradient);
  363. g.fillEllipse (20, 20, 300, 300);
  364. Array<Image> images { myImage, myImage2 };
  365. ContentSharer::getInstance()->shareImages (images,
  366. [] (bool success, const String& error)
  367. {
  368. String resultString = success ? String ("success")
  369. : ("failure\n (error: " + error + ")");
  370. AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, "Sharing Images Result",
  371. "Sharing images finished\nwith " + resultString);
  372. });
  373. }
  374. }
  375. ImagePreviewComponent imagePreview;
  376. std::unique_ptr<FileChooser> fc;
  377. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DialogsDemo)
  378. };