1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-14 00:14:18 +00:00
JUCE/extras/Projucer/Source/Wizards/jucer_NewProjectWizardComponent.h

505 lines
18 KiB
C++

/*
==============================================================================
This file is part of the JUCE 6 technical preview.
Copyright (c) 2017 - ROLI Ltd.
You may use this code under the terms of the GPL v3
(see www.gnu.org/licenses).
For this technical preview, this file is not subject to commercial licensing.
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
//==============================================================================
class ModulesFolderPathBox : public Component
{
public:
ModulesFolderPathBox (String initialFileOrDirectory)
: currentPathBox ("currentPathBox"),
openFolderButton (TRANS("...")),
modulesLabel (String(), TRANS("Modules Folder") + ":"),
useGlobalPathsToggle ("Use global module path")
{
if (initialFileOrDirectory.isEmpty())
initialFileOrDirectory = getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get().toString();
setModulesFolder (initialFileOrDirectory);
addAndMakeVisible (currentPathBox);
currentPathBox.setEditableText (true);
currentPathBox.onChange = [this] { setModulesFolder (File::getCurrentWorkingDirectory()
.getChildFile (currentPathBox.getText())); };
addAndMakeVisible (openFolderButton);
openFolderButton.setTooltip (TRANS ("Select JUCE modules folder"));
openFolderButton.onClick = [this] { selectJuceFolder(); };
addAndMakeVisible (modulesLabel);
modulesLabel.attachToComponent (&currentPathBox, true);
auto updateEnablement = [this]
{
isUsingGlobalPaths = useGlobalPathsToggle.getToggleState();
currentPathBox.setEnabled (! isUsingGlobalPaths);
openFolderButton.setEnabled (! isUsingGlobalPaths);
modulesLabel.setEnabled (! isUsingGlobalPaths);
};
addAndMakeVisible (useGlobalPathsToggle);
useGlobalPathsToggle.setToggleState (true, sendNotification);
useGlobalPathsToggle.onClick = [updateEnablement] { updateEnablement(); };
updateEnablement();
}
void resized() override
{
auto b = getLocalBounds();
auto topSlice = b.removeFromTop (b.getHeight() / 2);
openFolderButton.setBounds (topSlice.removeFromRight (30));
modulesLabel.setBounds (topSlice.removeFromLeft (110));
currentPathBox.setBounds (topSlice);
b.removeFromTop (5);
useGlobalPathsToggle.setBounds (b.translated (20, 0));
}
static bool selectJuceFolder (File& result)
{
for (;;)
{
FileChooser fc ("Select your JUCE modules folder...",
{ getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get().toString() },
"*");
if (! fc.browseForDirectory())
return false;
if (isJUCEModulesFolder (fc.getResult()))
{
result = fc.getResult();
return true;
}
AlertWindow::showMessageBox (AlertWindow::WarningIcon,
"Not a valid JUCE modules folder!",
"Please select the folder containing your juce_* modules!\n\n"
"This is required so that the new project can be given some essential core modules.");
}
}
void selectJuceFolder()
{
File result;
if (selectJuceFolder (result))
setModulesFolder (result);
}
void setModulesFolder (const File& newFolder)
{
if (modulesFolder != newFolder)
{
modulesFolder = newFolder;
currentPathBox.setText (modulesFolder.getFullPathName(), dontSendNotification);
}
}
File modulesFolder;
bool isUsingGlobalPaths = true;
private:
ComboBox currentPathBox;
TextButton openFolderButton;
Label modulesLabel;
ToggleButton useGlobalPathsToggle;
};
/** The target platforms chooser for the chosen template. */
class PlatformTargetsComp : public Component,
private ListBoxModel
{
public:
PlatformTargetsComp()
{
setOpaque (false);
const Array<ProjectExporter::ExporterTypeInfo> types (ProjectExporter::getExporterTypes());
for (auto& type : types)
{
platforms.add (new PlatformType { type.getIcon(), type.name });
addAndMakeVisible (toggles.add (new ToggleButton (String())));
}
listBox.setRowHeight (30);
listBox.setModel (this);
listBox.setOpaque (false);
listBox.setMultipleSelectionEnabled (true);
listBox.setClickingTogglesRowSelection (true);
listBox.setColour (ListBox::backgroundColourId, Colours::transparentBlack);
addAndMakeVisible (listBox);
selectDefaultExporterIfNoneSelected();
}
StringArray getSelectedPlatforms() const
{
StringArray list;
for (int i = 0; i < platforms.size(); ++i)
if (listBox.isRowSelected (i))
list.add (platforms.getUnchecked(i)->name);
return list;
}
void selectDefaultExporterIfNoneSelected()
{
if (listBox.getNumSelectedRows() == 0)
{
for (int i = platforms.size(); --i >= 0;)
{
if (platforms.getUnchecked(i)->name == ProjectExporter::getCurrentPlatformExporterName())
{
listBox.selectRow (i);
break;
}
}
}
}
void resized() override
{
listBox.setBounds (getLocalBounds());
}
int getNumRows() override
{
return platforms.size();
}
void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override
{
ignoreUnused (width);
if (auto* platform = platforms[rowNumber])
{
auto bounds = getLocalBounds().withHeight (height).withTrimmedBottom (1);
g.setColour (findColour (rowNumber % 2 == 0 ? widgetBackgroundColourId
: secondaryWidgetBackgroundColourId));
g.fillRect (bounds);
bounds.removeFromLeft (10);
auto toggleBounds = bounds.removeFromLeft (height);
drawToggle (g, toggleBounds, rowIsSelected);
auto iconBounds = bounds.removeFromLeft (height).reduced (5);
g.drawImageWithin (platform->icon, iconBounds.getX(), iconBounds.getY(), iconBounds.getWidth(),
iconBounds.getHeight(), RectanglePlacement::fillDestination);
bounds.removeFromLeft (10);
g.setColour (findColour (widgetTextColourId));
g.drawFittedText (platform->name, bounds, Justification::centredLeft, 1);
}
}
void selectedRowsChanged (int) override
{
selectDefaultExporterIfNoneSelected();
}
private:
struct PlatformType
{
Image icon;
String name;
};
void drawToggle (Graphics& g, Rectangle<int> bounds, bool isToggled)
{
auto sideLength = jmin (bounds.getWidth(), bounds.getHeight());
bounds = bounds.withSizeKeepingCentre (sideLength, sideLength).reduced (4);
g.setColour (findColour (ToggleButton::tickDisabledColourId));
g.drawRoundedRectangle (bounds.toFloat(), 2.0f, 1.0f);
if (isToggled)
{
g.setColour (findColour (ToggleButton::tickColourId));
const auto tick = getTickShape (0.75f);
g.fillPath (tick, tick.getTransformToScaleToFit (bounds.reduced (4, 5).toFloat(), false));
}
}
Path getTickShape (float height)
{
static const unsigned char pathData[] = { 110,109,32,210,202,64,126,183,148,64,108,39,244,247,64,245,76,124,64,108,178,131,27,65,246,76,252,64,108,175,242,4,65,246,76,252,
64,108,236,5,68,65,0,0,160,180,108,240,150,90,65,21,136,52,63,108,48,59,16,65,0,0,32,65,108,32,210,202,64,126,183,148,64, 99,101,0,0 };
Path path;
path.loadPathFromData (pathData, sizeof (pathData));
path.scaleToFit (0, 0, height * 2.0f, height, true);
return path;
}
ListBox listBox;
OwnedArray<PlatformType> platforms;
OwnedArray<ToggleButton> toggles;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PlatformTargetsComp)
};
//==============================================================================
/**
The Component for project creation.
Features a file browser to select project destination and
a list box of platform targets to generate.
*/
class WizardComp : public Component,
private FileBrowserListener
{
public:
WizardComp()
: platformTargets(),
projectName (TRANS("Project name")),
modulesPathBox (getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get().toString())
{
setOpaque (false);
addChildAndSetID (&projectName, "projectName");
projectName.setText ("NewProject");
nameLabel.attachToComponent (&projectName, true);
projectName.onTextChange = [this]
{
updateCreateButton();
fileBrowser.setFileName (File::createLegalFileName (projectName.getText()));
};
addChildAndSetID (&projectType, "projectType");
projectType.addItemList (getWizardNames(), 1);
projectType.setSelectedId (1, dontSendNotification);
typeLabel.attachToComponent (&projectType, true);
projectType.onChange = [this] { updateFileCreationTypes(); };
addChildAndSetID (&fileOutline, "fileOutline");
fileOutline.setColour (GroupComponent::outlineColourId, Colours::black.withAlpha (0.2f));
fileOutline.setTextLabelPosition (Justification::centred);
addChildAndSetID (&targetsOutline, "targetsOutline");
targetsOutline.setColour (GroupComponent::outlineColourId, Colours::black.withAlpha (0.2f));
targetsOutline.setTextLabelPosition (Justification::centred);
addChildAndSetID (&platformTargets, "platformTargets");
addChildAndSetID (&fileBrowser, "fileBrowser");
fileBrowser.setFilenameBoxLabel ("Folder:");
fileBrowser.setFileName (File::createLegalFileName (projectName.getText()));
fileBrowser.addListener (this);
addChildAndSetID (&createButton, "createButton");
createButton.onClick = [this] { createProject(); };
addChildAndSetID (&cancelButton, "cancelButton");
cancelButton.addShortcut (KeyPress (KeyPress::escapeKey));
cancelButton.onClick = [this] { returnToTemplatesPage(); };
addChildAndSetID (&modulesPathBox, "modulesPathBox");
addChildAndSetID (&filesToCreate, "filesToCreate");
filesToCreateLabel.attachToComponent (&filesToCreate, true);
updateFileCreationTypes();
updateCreateButton();
lookAndFeelChanged();
}
void paint (Graphics& g) override
{
g.fillAll (findColour (backgroundColourId));
}
void resized() override
{
auto r = getLocalBounds();
auto left = r.removeFromLeft (getWidth() / 2).reduced (15);
auto right = r.reduced (15);
projectName.setBounds (left.removeFromTop (22).withTrimmedLeft (120));
left.removeFromTop (20);
projectType.setBounds (left.removeFromTop (22).withTrimmedLeft (120));
left.removeFromTop (20);
fileOutline.setBounds (left);
fileBrowser.setBounds (left.reduced (25));
auto buttons = right.removeFromBottom (30);
right.removeFromBottom (10);
createButton.setBounds (buttons.removeFromRight (130));
buttons.removeFromRight (10);
cancelButton.setBounds (buttons.removeFromRight (130));
filesToCreate.setBounds (right.removeFromTop (22).withTrimmedLeft (150));
right.removeFromTop (20);
modulesPathBox.setBounds (right.removeFromTop (50));
right.removeFromTop (20);
targetsOutline.setBounds (right);
platformTargets.setBounds (right.reduced (25));
}
void returnToTemplatesPage()
{
if (auto* parent = findParentComponentOfClass<SlidingPanelComponent>())
{
if (parent->getNumTabs() > 0)
parent->goToTab (parent->getCurrentTabIndex() - 1);
}
else
{
jassertfalse;
}
}
void createProject()
{
auto* mw = Component::findParentComponentOfClass<MainWindow>();
jassert (mw != nullptr);
std::unique_ptr<NewProjectWizardClasses::NewProjectWizard> wizard = createWizard();
if (wizard != nullptr)
{
Result result (wizard->processResultsFromSetupItems (*this));
if (result.failed())
{
AlertWindow::showMessageBox (AlertWindow::WarningIcon,
TRANS("Create Project"),
result.getErrorMessage());
return;
}
wizard->modulesFolder = modulesPathBox.isUsingGlobalPaths ? File (getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get().toString())
: modulesPathBox.modulesFolder;
if (! isJUCEModulesFolder (wizard->modulesFolder))
{
if (modulesPathBox.isUsingGlobalPaths)
AlertWindow::showMessageBox (AlertWindow::AlertIconType::WarningIcon, "Invalid Global Path",
"Your global JUCE module search path is invalid. Please select the folder containing your JUCE modules "
"to set as the default path.");
if (! wizard->selectJuceFolder())
return;
if (modulesPathBox.isUsingGlobalPaths)
{
getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).setValue (wizard->modulesFolder.getFullPathName(), nullptr);
ProjucerApplication::getApp().rescanJUCEPathModules();
}
}
auto projectDir = fileBrowser.getSelectedFile (0);
std::unique_ptr<Project> project (wizard->runWizard (*this, projectName.getText(), projectDir, modulesPathBox.isUsingGlobalPaths));
if (project != nullptr)
{
mw->openFile (project->getFile());
getAppSettings().lastWizardFolder = projectDir.getParentDirectory();
}
}
}
void updateFileCreationTypes()
{
StringArray items;
std::unique_ptr<NewProjectWizardClasses::NewProjectWizard> wizard = createWizard();
if (wizard != nullptr)
items = wizard->getFileCreationOptions();
filesToCreate.clear();
filesToCreate.addItemList (items, 1);
filesToCreate.setSelectedId (1, dontSendNotification);
}
void selectionChanged() override {}
void fileClicked (const File&, const MouseEvent&) override {}
void fileDoubleClicked (const File&) override {}
void browserRootChanged (const File&) override
{
fileBrowser.setFileName (File::createLegalFileName (projectName.getText()));
}
int getFileCreationComboID() const
{
return filesToCreate.getSelectedItemIndex();
}
ComboBox projectType, filesToCreate;
PlatformTargetsComp platformTargets;
private:
TextEditor projectName;
Label nameLabel { {}, TRANS("Project Name") + ":" };
Label typeLabel { {}, TRANS("Project Type") + ":" };
Label filesToCreateLabel { {}, TRANS("Files to Auto-Generate") + ":" };
FileBrowserComponent fileBrowser { FileBrowserComponent::saveMode
| FileBrowserComponent::canSelectDirectories
| FileBrowserComponent::doNotClearFileNameOnRootChange,
NewProjectWizardClasses::getLastWizardFolder(), nullptr, nullptr };
GroupComponent fileOutline { {}, TRANS("Project Folder") + ":" };
GroupComponent targetsOutline { {}, TRANS("Target Platforms") + ":" };
TextButton createButton { TRANS("Create") + "..." };
TextButton cancelButton { TRANS("Cancel") };
ModulesFolderPathBox modulesPathBox;
std::unique_ptr<NewProjectWizardClasses::NewProjectWizard> createWizard()
{
return createWizardType (projectType.getSelectedItemIndex());
}
void updateCreateButton()
{
createButton.setEnabled (projectName.getText().trim().isNotEmpty());
}
void lookAndFeelChanged() override
{
projectName.setColour (TextEditor::backgroundColourId, findColour (backgroundColourId));
projectName.setColour (TextEditor::textColourId, findColour (defaultTextColourId));
projectName.setColour (TextEditor::outlineColourId, findColour (defaultTextColourId));
projectName.applyFontToAllText (projectName.getFont());
fileBrowser.resized();
}
};