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.

330 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #pragma once
  19. #include "../../ProjectSaving/jucer_ProjectExporter.h"
  20. #include "../../Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h"
  21. #include "../../Utility/Helpers/jucer_ValueTreePropertyWithDefaultWrapper.h"
  22. #include "jucer_NewProjectWizard.h"
  23. //==============================================================================
  24. class ItemHeader final : public Component
  25. {
  26. public:
  27. ItemHeader (StringRef name, StringRef description, const char* iconSvgData)
  28. : nameLabel ({}, name),
  29. descriptionLabel ({}, description),
  30. icon (makeIcon (iconSvgData))
  31. {
  32. addAndMakeVisible (nameLabel);
  33. nameLabel.setFont (18.0f);
  34. nameLabel.setMinimumHorizontalScale (1.0f);
  35. addAndMakeVisible (descriptionLabel);
  36. descriptionLabel.setMinimumHorizontalScale (1.0f);
  37. }
  38. void resized() override
  39. {
  40. auto bounds = getLocalBounds();
  41. auto topSlice = bounds.removeFromTop (50);
  42. iconBounds = topSlice.removeFromRight (75);
  43. nameLabel.setBounds (topSlice);
  44. bounds.removeFromTop (10);
  45. descriptionLabel.setBounds (bounds.removeFromTop (50));
  46. bounds.removeFromTop (20);
  47. }
  48. void paint (Graphics& g) override
  49. {
  50. g.fillAll (findColour (secondaryBackgroundColourId));
  51. if (icon != nullptr)
  52. icon->drawWithin (g, iconBounds.toFloat(), RectanglePlacement::centred, 1.0f);
  53. }
  54. private:
  55. static std::unique_ptr<Drawable> makeIcon (const char* iconSvgData)
  56. {
  57. if (auto svg = XmlDocument::parse (iconSvgData))
  58. return Drawable::createFromSVG (*svg);
  59. return {};
  60. }
  61. Label nameLabel, descriptionLabel;
  62. Rectangle<int> iconBounds;
  63. std::unique_ptr<Drawable> icon;
  64. //==============================================================================
  65. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemHeader)
  66. };
  67. //==============================================================================
  68. class TemplateComponent final : public Component
  69. {
  70. public:
  71. TemplateComponent (const NewProjectTemplates::ProjectTemplate& temp,
  72. std::function<void (std::unique_ptr<Project>)>&& createdCallback)
  73. : projectTemplate (temp),
  74. projectCreatedCallback (std::move (createdCallback)),
  75. header (projectTemplate.displayName, projectTemplate.description, projectTemplate.icon)
  76. {
  77. createProjectButton.onClick = [this]
  78. {
  79. chooser = std::make_unique<FileChooser> ("Save Project", NewProjectWizard::getLastWizardFolder());
  80. auto browserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories;
  81. chooser->launchAsync (browserFlags, [this] (const FileChooser& fc)
  82. {
  83. auto dir = fc.getResult();
  84. if (dir == File{})
  85. return;
  86. SafePointer<TemplateComponent> safeThis { this };
  87. messageBox = NewProjectWizard::createNewProject (projectTemplate,
  88. dir.getChildFile (projectNameValue.get().toString()),
  89. projectNameValue.get(),
  90. modulesValue.get(),
  91. exportersValue.get(),
  92. fileOptionsValue.get(),
  93. modulePathValue.getCurrentValue(),
  94. modulePathValue.getWrappedValueTreePropertyWithDefault().isUsingDefault(),
  95. [safeThis, dir] (ScopedMessageBox mb, std::unique_ptr<Project> project)
  96. {
  97. if (safeThis == nullptr)
  98. return;
  99. safeThis->messageBox = std::move (mb);
  100. safeThis->projectCreatedCallback (std::move (project));
  101. getAppSettings().lastWizardFolder = dir;
  102. });
  103. });
  104. };
  105. addAndMakeVisible (createProjectButton);
  106. addAndMakeVisible (header);
  107. modulePathValue.init ({ settingsTree, Ids::defaultJuceModulePath, nullptr },
  108. getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()),
  109. TargetOS::getThisOS());
  110. panel.addProperties (buildPropertyList(), 2);
  111. addAndMakeVisible (panel);
  112. }
  113. void resized() override
  114. {
  115. auto bounds = getLocalBounds().reduced (10);
  116. header.setBounds (bounds.removeFromTop (150));
  117. createProjectButton.setBounds (bounds.removeFromBottom (30).removeFromRight (150));
  118. bounds.removeFromBottom (5);
  119. panel.setBounds (bounds);
  120. }
  121. void paint (Graphics& g) override
  122. {
  123. g.fillAll (findColour (secondaryBackgroundColourId));
  124. }
  125. private:
  126. NewProjectTemplates::ProjectTemplate projectTemplate;
  127. std::unique_ptr<FileChooser> chooser;
  128. std::function<void (std::unique_ptr<Project>)> projectCreatedCallback;
  129. ItemHeader header;
  130. TextButton createProjectButton { "Create Project..." };
  131. ValueTree settingsTree { "NewProjectSettings" };
  132. ValueTreePropertyWithDefault projectNameValue { settingsTree, Ids::name, nullptr, "NewProject" },
  133. modulesValue { settingsTree, Ids::dependencies_, nullptr, projectTemplate.requiredModules, "," },
  134. exportersValue { settingsTree, Ids::exporters, nullptr, StringArray (ProjectExporter::getCurrentPlatformExporterTypeInfo().identifier.toString()), "," },
  135. fileOptionsValue { settingsTree, Ids::file, nullptr, NewProjectTemplates::getVarForFileOption (projectTemplate.defaultFileOption) };
  136. ValueTreePropertyWithDefaultWrapper modulePathValue;
  137. PropertyPanel panel;
  138. //==============================================================================
  139. PropertyComponent* createProjectNamePropertyComponent()
  140. {
  141. return new TextPropertyComponent (projectNameValue, "Project Name", 1024, false);
  142. }
  143. PropertyComponent* createModulesPropertyComponent()
  144. {
  145. Array<var> moduleVars;
  146. var requiredModules;
  147. for (auto& m : getJUCEModules())
  148. {
  149. moduleVars.add (m);
  150. if (projectTemplate.requiredModules.contains (m))
  151. requiredModules.append (m);
  152. }
  153. modulesValue = requiredModules;
  154. return new MultiChoicePropertyComponent (modulesValue, "Modules",
  155. getJUCEModules(), moduleVars);
  156. }
  157. PropertyComponent* createModulePathPropertyComponent()
  158. {
  159. return new FilePathPropertyComponent (modulePathValue.getWrappedValueTreePropertyWithDefault(), "Path to Modules", true);
  160. }
  161. PropertyComponent* createExportersPropertyValue()
  162. {
  163. Array<var> exporterVars;
  164. StringArray exporterNames;
  165. for (auto& exporterTypeInfo : ProjectExporter::getExporterTypeInfos())
  166. {
  167. exporterVars.add (exporterTypeInfo.identifier.toString());
  168. exporterNames.add (exporterTypeInfo.displayName);
  169. }
  170. return new MultiChoicePropertyComponent (exportersValue, "Exporters", exporterNames, exporterVars);
  171. }
  172. PropertyComponent* createFileCreationOptionsPropertyComponent()
  173. {
  174. Array<var> optionVars;
  175. StringArray optionStrings;
  176. for (auto& opt : projectTemplate.fileOptionsAndFiles)
  177. {
  178. optionVars.add (NewProjectTemplates::getVarForFileOption (opt.first));
  179. optionStrings.add (NewProjectTemplates::getStringForFileOption (opt.first));
  180. }
  181. return new ChoicePropertyComponent (fileOptionsValue, "File Creation Options", optionStrings, optionVars);
  182. }
  183. Array<PropertyComponent*> buildPropertyList()
  184. {
  185. PropertyListBuilder builder;
  186. builder.add (createProjectNamePropertyComponent());
  187. builder.add (createModulesPropertyComponent());
  188. builder.add (createModulePathPropertyComponent());
  189. builder.add (createExportersPropertyValue());
  190. if (! projectTemplate.fileOptionsAndFiles.empty())
  191. builder.add (createFileCreationOptionsPropertyComponent());
  192. return builder.components;
  193. }
  194. ScopedMessageBox messageBox;
  195. //==============================================================================
  196. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemplateComponent)
  197. };
  198. //==============================================================================
  199. class ExampleComponent final : public Component
  200. {
  201. public:
  202. ExampleComponent (const File& f, std::function<void (const File&)> selectedCallback)
  203. : exampleFile (f),
  204. metadata (parseJUCEHeaderMetadata (exampleFile)),
  205. exampleSelectedCallback (std::move (selectedCallback)),
  206. header (metadata[Ids::name].toString(), metadata[Ids::description].toString(), BinaryData::background_logo_svg),
  207. codeViewer (doc, &cppTokeniser)
  208. {
  209. setTitle (exampleFile.getFileName());
  210. setFocusContainerType (FocusContainerType::focusContainer);
  211. addAndMakeVisible (header);
  212. openExampleButton.onClick = [this] { exampleSelectedCallback (exampleFile); };
  213. addAndMakeVisible (openExampleButton);
  214. setupCodeViewer();
  215. addAndMakeVisible (codeViewer);
  216. }
  217. void paint (Graphics& g) override
  218. {
  219. g.fillAll (findColour (secondaryBackgroundColourId));
  220. }
  221. void resized() override
  222. {
  223. auto bounds = getLocalBounds().reduced (10);
  224. header.setBounds (bounds.removeFromTop (125));
  225. openExampleButton.setBounds (bounds.removeFromBottom (30).removeFromRight (150));
  226. codeViewer.setBounds (bounds);
  227. }
  228. private:
  229. void setupCodeViewer()
  230. {
  231. auto fileString = exampleFile.loadFileAsString();
  232. doc.replaceAllContent (fileString);
  233. codeViewer.setScrollbarThickness (6);
  234. codeViewer.setReadOnly (true);
  235. codeViewer.setTitle ("Code");
  236. getAppSettings().appearance.applyToCodeEditor (codeViewer);
  237. codeViewer.scrollToLine (findBestLineToScrollToForClass (StringArray::fromLines (fileString),
  238. metadata[Ids::name].toString(),
  239. metadata[Ids::type] == "AudioProcessor"));
  240. }
  241. //==============================================================================
  242. File exampleFile;
  243. var metadata;
  244. std::function<void (const File&)> exampleSelectedCallback;
  245. ItemHeader header;
  246. CPlusPlusCodeTokeniser cppTokeniser;
  247. CodeDocument doc;
  248. CodeEditorComponent codeViewer;
  249. TextButton openExampleButton { "Open Example..." };
  250. //==============================================================================
  251. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExampleComponent)
  252. };