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.

453 lines
19KB

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