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.

303 lines
10.0KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE examples.
  4. Copyright (c) 2022 - 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: MDIDemo
  20. version: 1.0.0
  21. vendor: JUCE
  22. website: http://juce.com
  23. description: Displays and edits MDI files.
  24. dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
  25. juce_gui_basics, juce_gui_extra
  26. exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
  27. moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
  28. type: Component
  29. mainClass: MDIDemo
  30. useLocalCopy: 1
  31. END_JUCE_PIP_METADATA
  32. *******************************************************************************/
  33. #pragma once
  34. #include "../Assets/DemoUtilities.h"
  35. //==============================================================================
  36. /** The Note class contains text editor used to display and edit the note's contents and will
  37. also listen to changes in the text and mark the FileBasedDocument as 'dirty'. This 'dirty'
  38. flag is used to prompt the user to save the note when it is closed.
  39. */
  40. class Note final : public Component,
  41. public FileBasedDocument
  42. {
  43. public:
  44. Note (const String& name, const String& contents)
  45. : FileBasedDocument (".jnote", "*.jnote",
  46. "Browse for note to load",
  47. "Choose file to save note to"),
  48. textValueObject (contents)
  49. {
  50. // we need to use an separate Value object as our text source so it doesn't get marked
  51. // as changed immediately
  52. setName (name);
  53. editor.setMultiLine (true);
  54. editor.setReturnKeyStartsNewLine (true);
  55. editor.getTextValue().referTo (textValueObject);
  56. addAndMakeVisible (editor);
  57. editor.onTextChange = [this] { changed(); };
  58. }
  59. void resized() override
  60. {
  61. editor.setBounds (getLocalBounds());
  62. }
  63. String getDocumentTitle() override
  64. {
  65. return getName();
  66. }
  67. Result loadDocument (const File& file) override
  68. {
  69. editor.setText (file.loadFileAsString());
  70. return Result::ok();
  71. }
  72. Result saveDocument (const File& file) override
  73. {
  74. // attempt to save the contents into the given file
  75. if (file.replaceWithText (editor.getText()))
  76. return Result::ok();
  77. return Result::fail ("Can't write to file");
  78. }
  79. File getLastDocumentOpened() override
  80. {
  81. // not interested in this for now
  82. return {};
  83. }
  84. void setLastDocumentOpened (const File& /*file*/) override
  85. {
  86. // not interested in this for now
  87. }
  88. File getSuggestedSaveAsFile (const File&) override
  89. {
  90. return File::getSpecialLocation (File::userDesktopDirectory)
  91. .getChildFile (getName())
  92. .withFileExtension ("jnote");
  93. }
  94. private:
  95. Value textValueObject;
  96. TextEditor editor;
  97. void lookAndFeelChanged() override
  98. {
  99. editor.applyFontToAllText (editor.getFont());
  100. }
  101. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Note)
  102. };
  103. //==============================================================================
  104. /** Simple MultiDocumentPanel that just tries to save our notes when they are closed.
  105. */
  106. class DemoMultiDocumentPanel final : public MultiDocumentPanel
  107. {
  108. public:
  109. DemoMultiDocumentPanel() = default;
  110. void tryToCloseDocumentAsync (Component* component, std::function<void (bool)> callback) override
  111. {
  112. if (auto* note = dynamic_cast<Note*> (component))
  113. {
  114. SafePointer<DemoMultiDocumentPanel> parent { this };
  115. note->saveIfNeededAndUserAgreesAsync ([parent, callback] (FileBasedDocument::SaveResult result)
  116. {
  117. if (parent != nullptr)
  118. callback (result == FileBasedDocument::savedOk);
  119. });
  120. }
  121. }
  122. void activeDocumentChanged() override
  123. {
  124. if (auto* activeDoc = getActiveDocument())
  125. Logger::outputDebugString ("activeDocumentChanged() to " + activeDoc->getName());
  126. }
  127. private:
  128. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoMultiDocumentPanel)
  129. };
  130. //==============================================================================
  131. /** Simple multi-document panel that manages a number of notes that you can store to files.
  132. By default this will look for notes saved to the desktop and load them up.
  133. */
  134. class MDIDemo final : public Component,
  135. public FileDragAndDropTarget
  136. {
  137. public:
  138. MDIDemo()
  139. {
  140. setOpaque (true);
  141. showInTabsButton.setToggleState (false, dontSendNotification);
  142. showInTabsButton.onClick = [this] { updateLayoutMode(); };
  143. addAndMakeVisible (showInTabsButton);
  144. oneDocShouldBeFullscreenButton.onClick = [this]
  145. {
  146. multiDocumentPanel.useFullscreenWhenOneDocument (oneDocShouldBeFullscreenButton.getToggleState());
  147. };
  148. addAndMakeVisible (oneDocShouldBeFullscreenButton);
  149. oneDocShouldBeFullscreenButton.setToggleState (false, juce::sendNotification);
  150. addNoteButton.onClick = [this]
  151. {
  152. addNote ("Note " + String (noteCounter), "Hello World! " + String (noteCounter));
  153. ++noteCounter;
  154. };
  155. addAndMakeVisible (addNoteButton);
  156. closeActiveDocumentButton.onClick = [this]
  157. {
  158. multiDocumentPanel.closeDocumentAsync (multiDocumentPanel.getActiveDocument(), false, [] (auto) {});
  159. };
  160. addAndMakeVisible (closeActiveDocumentButton);
  161. closeApplicationButton.onClick = [this]
  162. {
  163. multiDocumentPanel.closeAllDocumentsAsync (true, [] (bool allSaved)
  164. {
  165. if (allSaved)
  166. JUCEApplicationBase::quit();
  167. });
  168. };
  169. addAndMakeVisible (closeApplicationButton);
  170. addAndMakeVisible (multiDocumentPanel);
  171. multiDocumentPanel.setBackgroundColour (Colours::transparentBlack);
  172. updateLayoutMode();
  173. addNote ("Notes Demo", "You can drag-and-drop text files onto this page to open them as notes..");
  174. addExistingNotes();
  175. setSize (650, 500);
  176. }
  177. void paint (Graphics& g) override
  178. {
  179. g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
  180. }
  181. void resized() override
  182. {
  183. auto area = getLocalBounds();
  184. auto topButtonRow = area.removeFromTop (28).reduced (2);
  185. showInTabsButton .setBounds (topButtonRow.removeFromLeft (150));
  186. closeApplicationButton .setBounds (topButtonRow.removeFromRight (150));
  187. addNoteButton .setBounds (topButtonRow.removeFromRight (150));
  188. closeActiveDocumentButton .setBounds (topButtonRow.removeFromRight (150));
  189. oneDocShouldBeFullscreenButton.setBounds (area.removeFromTop (28).reduced (2).removeFromLeft (240));
  190. multiDocumentPanel.setBounds (area);
  191. }
  192. bool isInterestedInFileDrag (const StringArray&) override
  193. {
  194. return true;
  195. }
  196. void filesDropped (const StringArray& filenames, int /*x*/, int /*y*/) override
  197. {
  198. Array<File> files;
  199. for (auto& f : filenames)
  200. files.add ({ f });
  201. createNotesForFiles (files);
  202. }
  203. void createNotesForFiles (const Array<File>& files)
  204. {
  205. for (auto& file : files)
  206. {
  207. auto content = file.loadFileAsString();
  208. if (content.length() > 20000)
  209. content = "Too long!";
  210. addNote (file.getFileName(), content);
  211. }
  212. }
  213. private:
  214. void updateLayoutMode()
  215. {
  216. multiDocumentPanel.setLayoutMode (showInTabsButton.getToggleState() ? MultiDocumentPanel::MaximisedWindowsWithTabs
  217. : MultiDocumentPanel::FloatingWindows);
  218. }
  219. void addNote (const String& name, const String& content)
  220. {
  221. auto* newNote = new Note (name, content);
  222. newNote->setSize (200, 200);
  223. multiDocumentPanel.addDocument (newNote, Colours::lightblue.withAlpha (0.6f), true);
  224. }
  225. void addExistingNotes()
  226. {
  227. Array<File> files;
  228. File::getSpecialLocation (File::userDesktopDirectory).findChildFiles (files, File::findFiles, false, "*.jnote");
  229. createNotesForFiles (files);
  230. }
  231. ToggleButton showInTabsButton { "Show with tabs" };
  232. ToggleButton oneDocShouldBeFullscreenButton { "Fill screen when only one note is open" };
  233. TextButton addNoteButton { "Create a new note" },
  234. closeApplicationButton { "Close app" },
  235. closeActiveDocumentButton { "Close active document" };
  236. DemoMultiDocumentPanel multiDocumentPanel;
  237. int noteCounter = 1;
  238. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MDIDemo)
  239. };