/* ============================================================================== 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. ============================================================================== */ #pragma once #include "../../ProjectSaving/jucer_ProjectExporter.h" #include "../../Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h" #include "../../Utility/Helpers/jucer_ValueWithDefaultWrapper.h" #include "jucer_NewProjectWizard.h" //============================================================================== class ItemHeader : public Component { public: ItemHeader (StringRef name, StringRef description, const char* iconSvgData) : nameLabel ({}, name), descriptionLabel ({}, description), icon (makeIcon (iconSvgData)) { addAndMakeVisible (nameLabel); nameLabel.setFont (18.0f); nameLabel.setMinimumHorizontalScale (1.0f); addAndMakeVisible (descriptionLabel); descriptionLabel.setMinimumHorizontalScale (1.0f); } void resized() override { auto bounds = getLocalBounds(); auto topSlice = bounds.removeFromTop (50); iconBounds = topSlice.removeFromRight (75); nameLabel.setBounds (topSlice); bounds.removeFromTop (10); descriptionLabel.setBounds (bounds.removeFromTop (50)); bounds.removeFromTop (20); } void paint (Graphics& g) override { g.fillAll (findColour (secondaryBackgroundColourId)); if (icon != nullptr) icon->drawWithin (g, iconBounds.toFloat(), RectanglePlacement::centred, 1.0f); } private: static std::unique_ptr makeIcon (const char* iconSvgData) { if (auto svg = XmlDocument::parse (iconSvgData)) return Drawable::createFromSVG (*svg); return {}; } Label nameLabel, descriptionLabel; Rectangle iconBounds; std::unique_ptr icon; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemHeader) }; //============================================================================== class TemplateComponent : public Component { public: TemplateComponent (const NewProjectTemplates::ProjectTemplate& temp, std::function)>&& createdCallback) : projectTemplate (temp), projectCreatedCallback (std::move (createdCallback)), header (projectTemplate.displayName, projectTemplate.description, projectTemplate.icon) { createProjectButton.onClick = [this] { chooser = std::make_unique ("Save Project", NewProjectWizard::getLastWizardFolder()); auto browserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories; chooser->launchAsync (browserFlags, [this] (const FileChooser& fc) { auto dir = fc.getResult(); if (dir == File{}) return; SafePointer safeThis { this }; NewProjectWizard::createNewProject (projectTemplate, dir.getChildFile (projectNameValue.get().toString()), projectNameValue.get(), modulesValue.get(), exportersValue.get(), fileOptionsValue.get(), modulePathValue.getCurrentValue(), modulePathValue.getWrappedValueWithDefault().isUsingDefault(), [safeThis, dir] (std::unique_ptr project) { if (safeThis == nullptr) return; safeThis->projectCreatedCallback (std::move (project)); getAppSettings().lastWizardFolder = dir; }); }); }; addAndMakeVisible (createProjectButton); addAndMakeVisible (header); modulePathValue.init ({ settingsTree, Ids::defaultJuceModulePath, nullptr }, getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()), TargetOS::getThisOS()); panel.addProperties (buildPropertyList(), 2); addAndMakeVisible (panel); } void resized() override { auto bounds = getLocalBounds().reduced (10); header.setBounds (bounds.removeFromTop (150)); createProjectButton.setBounds (bounds.removeFromBottom (30).removeFromRight (150)); bounds.removeFromBottom (5); panel.setBounds (bounds); } void paint (Graphics& g) override { g.fillAll (findColour (secondaryBackgroundColourId)); } private: NewProjectTemplates::ProjectTemplate projectTemplate; std::unique_ptr chooser; std::function)> projectCreatedCallback; ItemHeader header; TextButton createProjectButton { "Create Project..." }; ValueTree settingsTree { "NewProjectSettings" }; ValueWithDefault projectNameValue { settingsTree, Ids::name, nullptr, "NewProject" }, modulesValue { settingsTree, Ids::dependencies_, nullptr, projectTemplate.requiredModules, "," }, exportersValue { settingsTree, Ids::exporters, nullptr, StringArray (ProjectExporter::getCurrentPlatformExporterTypeInfo().identifier.toString()), "," }, fileOptionsValue { settingsTree, Ids::file, nullptr, NewProjectTemplates::getVarForFileOption (projectTemplate.defaultFileOption) }; ValueWithDefaultWrapper modulePathValue; PropertyPanel panel; //============================================================================== PropertyComponent* createProjectNamePropertyComponent() { return new TextPropertyComponent (projectNameValue, "Project Name", 1024, false); } PropertyComponent* createModulesPropertyComponent() { Array moduleVars; var requiredModules; for (auto& m : getJUCEModules()) { moduleVars.add (m); if (projectTemplate.requiredModules.contains (m)) requiredModules.append (m); } modulesValue = requiredModules; return new MultiChoicePropertyComponent (modulesValue, "Modules", getJUCEModules(), moduleVars); } PropertyComponent* createModulePathPropertyComponent() { return new FilePathPropertyComponent (modulePathValue.getWrappedValueWithDefault(), "Path to Modules", true); } PropertyComponent* createExportersPropertyValue() { Array exporterVars; StringArray exporterNames; for (auto& exporterTypeInfo : ProjectExporter::getExporterTypeInfos()) { exporterVars.add (exporterTypeInfo.identifier.toString()); exporterNames.add (exporterTypeInfo.displayName); } return new MultiChoicePropertyComponent (exportersValue, "Exporters", exporterNames, exporterVars); } PropertyComponent* createFileCreationOptionsPropertyComponent() { Array optionVars; StringArray optionStrings; for (auto& opt : projectTemplate.fileOptionsAndFiles) { optionVars.add (NewProjectTemplates::getVarForFileOption (opt.first)); optionStrings.add (NewProjectTemplates::getStringForFileOption (opt.first)); } return new ChoicePropertyComponent (fileOptionsValue, "File Creation Options", optionStrings, optionVars); } Array buildPropertyList() { PropertyListBuilder builder; builder.add (createProjectNamePropertyComponent()); builder.add (createModulesPropertyComponent()); builder.add (createModulePathPropertyComponent()); builder.add (createExportersPropertyValue()); if (! projectTemplate.fileOptionsAndFiles.empty()) builder.add (createFileCreationOptionsPropertyComponent()); return builder.components; } //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemplateComponent) }; //============================================================================== class ExampleComponent : public Component { public: ExampleComponent (const File& f, std::function selectedCallback) : exampleFile (f), metadata (parseJUCEHeaderMetadata (exampleFile)), exampleSelectedCallback (std::move (selectedCallback)), header (metadata[Ids::name].toString(), metadata[Ids::description].toString(), BinaryData::background_logo_svg), codeViewer (doc, &cppTokeniser) { setTitle (exampleFile.getFileName()); setFocusContainerType (FocusContainerType::focusContainer); addAndMakeVisible (header); openExampleButton.onClick = [this] { exampleSelectedCallback (exampleFile); }; addAndMakeVisible (openExampleButton); setupCodeViewer(); addAndMakeVisible (codeViewer); } void paint (Graphics& g) override { g.fillAll (findColour (secondaryBackgroundColourId)); } void resized() override { auto bounds = getLocalBounds().reduced (10); header.setBounds (bounds.removeFromTop (125)); openExampleButton.setBounds (bounds.removeFromBottom (30).removeFromRight (150)); codeViewer.setBounds (bounds); } private: void setupCodeViewer() { auto fileString = exampleFile.loadFileAsString(); doc.replaceAllContent (fileString); codeViewer.setScrollbarThickness (6); codeViewer.setReadOnly (true); codeViewer.setTitle ("Code"); getAppSettings().appearance.applyToCodeEditor (codeViewer); codeViewer.scrollToLine (findBestLineToScrollToForClass (StringArray::fromLines (fileString), metadata[Ids::name].toString(), metadata[Ids::type] == "AudioProcessor")); } //============================================================================== File exampleFile; var metadata; std::function exampleSelectedCallback; ItemHeader header; CPlusPlusCodeTokeniser cppTokeniser; CodeDocument doc; CodeEditorComponent codeViewer; TextButton openExampleButton { "Open Example..." }; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExampleComponent) };