migrating to the latest JUCE version

This commit is contained in:
2022-11-04 23:11:33 +01:00
committed by Nikolai Rodionov
parent 4257a0f8ba
commit faf8f18333
2796 changed files with 888518 additions and 784244 deletions

View File

@ -1,215 +1,179 @@
/*
==============================================================================
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 "jucer_ModuleDescription.h"
//==============================================================================
class AvailableModulesList : private AsyncUpdater
{
public:
using ModuleIDAndFolder = std::pair<String, File>;
using ModuleIDAndFolderList = std::vector<ModuleIDAndFolder>;
AvailableModulesList() = default;
//==============================================================================
void scanPaths (const Array<File>& paths)
{
auto job = createScannerJob (paths);
auto& ref = *job;
removePendingAndAddJob (std::move (job));
scanPool.waitForJobToFinish (&ref, -1);
}
void scanPathsAsync (const Array<File>& paths)
{
removePendingAndAddJob (createScannerJob (paths));
}
//==============================================================================
ModuleIDAndFolderList getAllModules() const
{
const ScopedLock readLock (lock);
return modulesList;
}
ModuleIDAndFolder getModuleWithID (const String& id) const
{
const ScopedLock readLock (lock);
for (auto& mod : modulesList)
if (mod.first == id)
return mod;
return {};
}
//==============================================================================
void removeDuplicates (const ModuleIDAndFolderList& other)
{
const ScopedLock readLock (lock);
const auto predicate = [&] (const ModuleIDAndFolder& entry)
{
return std::find (other.begin(), other.end(), entry) != other.end();
};
modulesList.erase (std::remove_if (modulesList.begin(), modulesList.end(), predicate),
modulesList.end());
}
//==============================================================================
struct Listener
{
virtual ~Listener() = default;
virtual void availableModulesChanged (AvailableModulesList* listThatHasChanged) = 0;
};
void addListener (Listener* listenerToAdd) { listeners.add (listenerToAdd); }
void removeListener (Listener* listenerToRemove) { listeners.remove (listenerToRemove); }
private:
//==============================================================================
struct ModuleScannerJob : public ThreadPoolJob
{
ModuleScannerJob (const Array<File>& paths,
std::function<void (const ModuleIDAndFolderList&)>&& callback)
: ThreadPoolJob ("ModuleScannerJob"),
pathsToScan (paths),
completionCallback (std::move (callback))
{
}
JobStatus runJob() override
{
ModuleIDAndFolderList list;
for (auto& p : pathsToScan)
addAllModulesInFolder (p, list);
if (! shouldExit())
{
std::sort (list.begin(), list.end(), [] (const ModuleIDAndFolder& m1,
const ModuleIDAndFolder& m2)
{
return m1.first.compareIgnoreCase (m2.first) < 0;
});
completionCallback (list);
}
return jobHasFinished;
}
static bool tryToAddModuleFromFolder (const File& path, ModuleIDAndFolderList& list)
{
ModuleDescription m (path);
if (m.isValid()
&& std::find_if (list.begin(), list.end(),
[&m] (const ModuleIDAndFolder& element) { return element.first == m.getID(); }) == std::end (list))
{
list.push_back ({ m.getID(), path });
return true;
}
return false;
}
static void addAllModulesInFolder (const File& topLevelPath, ModuleIDAndFolderList& list)
{
struct FileAndDepth
{
File file;
int depth;
};
std::queue<FileAndDepth> pathsToCheck;
pathsToCheck.push ({ topLevelPath, 0 });
while (! pathsToCheck.empty())
{
const auto path = pathsToCheck.front();
pathsToCheck.pop();
if (tryToAddModuleFromFolder (path.file, list) || path.depth == 3)
continue;
for (const auto& iter : RangedDirectoryIterator (path.file, false, "*", File::findDirectories))
{
if (auto* job = ThreadPoolJob::getCurrentThreadPoolJob())
if (job->shouldExit())
return;
pathsToCheck.push({ iter.getFile(), path.depth + 1 });
}
}
}
Array<File> pathsToScan;
std::function<void (const ModuleIDAndFolderList&)> completionCallback;
};
//==============================================================================
void handleAsyncUpdate() override
{
listeners.call ([this] (Listener& l) { l.availableModulesChanged (this); });
}
std::unique_ptr<ThreadPoolJob> createScannerJob (const Array<File>& paths)
{
return std::make_unique<ModuleScannerJob> (paths, [this] (ModuleIDAndFolderList scannedModulesList)
{
if (scannedModulesList == modulesList)
return;
{
const ScopedLock swapLock (lock);
modulesList.swap (scannedModulesList);
}
triggerAsyncUpdate();
});
}
void removePendingAndAddJob (std::unique_ptr<ThreadPoolJob> jobToAdd)
{
scanPool.removeAllJobs (false, 100);
scanPool.addJob (jobToAdd.release(), true);
}
//==============================================================================
ThreadPool scanPool { 1 };
ModuleIDAndFolderList modulesList;
ListenerList<Listener> listeners;
CriticalSection lock;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AvailableModulesList)
};
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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 "jucer_ModuleDescription.h"
#include <future>
//==============================================================================
class AvailableModulesList : private AsyncUpdater
{
public:
using ModuleIDAndFolder = std::pair<String, File>;
using ModuleIDAndFolderList = std::vector<ModuleIDAndFolder>;
AvailableModulesList() = default;
//==============================================================================
void scanPaths (const Array<File>& paths)
{
scanPathsAsync (paths);
scanner = {};
}
void scanPathsAsync (const Array<File>& paths)
{
scanner = std::async (std::launch::async, [this, paths]
{
ModuleIDAndFolderList list;
for (auto& p : paths)
addAllModulesInFolder (p, list);
std::sort (list.begin(), list.end(), [] (const ModuleIDAndFolder& m1,
const ModuleIDAndFolder& m2)
{
return m1.first.compareIgnoreCase (m2.first) < 0;
});
{
const ScopedLock swapLock (lock);
if (list == modulesList)
return;
modulesList.swap (list);
}
triggerAsyncUpdate();
});
}
//==============================================================================
ModuleIDAndFolderList getAllModules() const
{
const ScopedLock readLock (lock);
return modulesList;
}
ModuleIDAndFolder getModuleWithID (const String& id) const
{
const ScopedLock readLock (lock);
for (auto& mod : modulesList)
if (mod.first == id)
return mod;
return {};
}
//==============================================================================
void removeDuplicates (const ModuleIDAndFolderList& other)
{
const ScopedLock readLock (lock);
const auto predicate = [&] (const ModuleIDAndFolder& entry)
{
return std::find (other.begin(), other.end(), entry) != other.end();
};
modulesList.erase (std::remove_if (modulesList.begin(), modulesList.end(), predicate),
modulesList.end());
}
//==============================================================================
struct Listener
{
virtual ~Listener() = default;
virtual void availableModulesChanged (AvailableModulesList* listThatHasChanged) = 0;
};
void addListener (Listener* listenerToAdd) { listeners.add (listenerToAdd); }
void removeListener (Listener* listenerToRemove) { listeners.remove (listenerToRemove); }
private:
//==============================================================================
static bool tryToAddModuleFromFolder (const File& path, ModuleIDAndFolderList& list)
{
ModuleDescription m (path);
if (m.isValid()
&& std::none_of (list.begin(), list.end(),
[&m] (const ModuleIDAndFolder& element) { return element.first == m.getID(); }))
{
list.push_back ({ m.getID(), path });
return true;
}
return false;
}
static void addAllModulesInFolder (const File& topLevelPath, ModuleIDAndFolderList& list)
{
struct FileAndDepth
{
File file;
int depth;
};
std::queue<FileAndDepth> pathsToCheck;
pathsToCheck.push ({ topLevelPath, 0 });
while (! pathsToCheck.empty())
{
const auto path = pathsToCheck.front();
pathsToCheck.pop();
if (tryToAddModuleFromFolder (path.file, list) || path.depth == 3)
continue;
for (const auto& iter : RangedDirectoryIterator (path.file, false, "*", File::findDirectories))
{
if (auto* job = ThreadPoolJob::getCurrentThreadPoolJob())
if (job->shouldExit())
return;
pathsToCheck.push({ iter.getFile(), path.depth + 1 });
}
}
}
//==============================================================================
void handleAsyncUpdate() override
{
listeners.call ([this] (Listener& l) { l.availableModulesChanged (this); });
}
//==============================================================================
ModuleIDAndFolderList modulesList;
ListenerList<Listener> listeners;
CriticalSection lock;
std::future<void> scanner;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AvailableModulesList)
};

View File

@ -1,93 +1,93 @@
/*
==============================================================================
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
//==============================================================================
class ModuleDescription
{
public:
ModuleDescription() = default;
ModuleDescription (const File& folder)
: moduleFolder (folder),
moduleInfo (parseJUCEHeaderMetadata (getHeader()))
{
}
bool isValid() const { return getID().isNotEmpty(); }
String getID() const { return moduleInfo [Ids::ID_uppercase].toString(); }
String getVendor() const { return moduleInfo [Ids::vendor].toString(); }
String getVersion() const { return moduleInfo [Ids::version].toString(); }
String getName() const { return moduleInfo [Ids::name].toString(); }
String getDescription() const { return moduleInfo [Ids::description].toString(); }
String getLicense() const { return moduleInfo [Ids::license].toString(); }
String getMinimumCppStandard() const { return moduleInfo [Ids::minimumCppStandard].toString(); }
String getPreprocessorDefs() const { return moduleInfo [Ids::defines].toString(); }
String getExtraSearchPaths() const { return moduleInfo [Ids::searchpaths].toString(); }
var getModuleInfo() const { return moduleInfo; }
File getModuleFolder() const { return moduleFolder; }
File getFolder() const
{
jassert (moduleFolder != File());
return moduleFolder;
}
File getHeader() const
{
if (moduleFolder != File())
{
static const char* extensions[] = { ".h", ".hpp", ".hxx" };
for (auto e : extensions)
{
auto header = moduleFolder.getChildFile (moduleFolder.getFileName() + e);
if (header.existsAsFile())
return header;
}
}
return {};
}
StringArray getDependencies() const
{
auto moduleDependencies = StringArray::fromTokens (moduleInfo ["dependencies"].toString(), " \t;,", "\"'");
moduleDependencies.trim();
moduleDependencies.removeEmptyStrings();
return moduleDependencies;
}
private:
File moduleFolder;
var moduleInfo;
URL url;
};
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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
//==============================================================================
class ModuleDescription
{
public:
ModuleDescription() = default;
ModuleDescription (const File& folder)
: moduleFolder (folder),
moduleInfo (parseJUCEHeaderMetadata (getHeader()))
{
}
bool isValid() const { return getID().isNotEmpty(); }
String getID() const { return moduleInfo [Ids::ID_uppercase].toString(); }
String getVendor() const { return moduleInfo [Ids::vendor].toString(); }
String getVersion() const { return moduleInfo [Ids::version].toString(); }
String getName() const { return moduleInfo [Ids::name].toString(); }
String getDescription() const { return moduleInfo [Ids::description].toString(); }
String getLicense() const { return moduleInfo [Ids::license].toString(); }
String getMinimumCppStandard() const { return moduleInfo [Ids::minimumCppStandard].toString(); }
String getPreprocessorDefs() const { return moduleInfo [Ids::defines].toString(); }
String getExtraSearchPaths() const { return moduleInfo [Ids::searchpaths].toString(); }
var getModuleInfo() const { return moduleInfo; }
File getModuleFolder() const { return moduleFolder; }
File getFolder() const
{
jassert (moduleFolder != File());
return moduleFolder;
}
File getHeader() const
{
if (moduleFolder != File())
{
static const char* extensions[] = { ".h", ".hpp", ".hxx" };
for (auto e : extensions)
{
auto header = moduleFolder.getChildFile (moduleFolder.getFileName() + e);
if (header.existsAsFile())
return header;
}
}
return {};
}
StringArray getDependencies() const
{
auto moduleDependencies = StringArray::fromTokens (moduleInfo ["dependencies"].toString(), " \t;,", "\"'");
moduleDependencies.trim();
moduleDependencies.removeEmptyStrings();
return moduleDependencies;
}
private:
File moduleFolder;
var moduleInfo;
URL url;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,148 +1,148 @@
/*
==============================================================================
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 "../jucer_Project.h"
class ProjectExporter;
class ProjectSaver;
//==============================================================================
class LibraryModule
{
public:
LibraryModule (const ModuleDescription&);
bool isValid() const { return moduleInfo.isValid(); }
String getID() const { return moduleInfo.getID(); }
String getVendor() const { return moduleInfo.getVendor(); }
String getVersion() const { return moduleInfo.getVersion(); }
String getName() const { return moduleInfo.getName(); }
String getDescription() const { return moduleInfo.getDescription(); }
String getLicense() const { return moduleInfo.getLicense(); }
String getMinimumCppStandard() const { return moduleInfo.getMinimumCppStandard(); }
File getFolder() const { return moduleInfo.getFolder(); }
void writeIncludes (ProjectSaver&, OutputStream&);
void addSettingsForModuleToExporter (ProjectExporter&, ProjectSaver&) const;
void getConfigFlags (Project&, OwnedArray<Project::ConfigFlag>& flags) const;
void findBrowseableFiles (const File& localModuleFolder, Array<File>& files) const;
struct CompileUnit
{
File file;
bool isCompiledForObjC = false, isCompiledForNonObjC = false;
bool isNeededForExporter (ProjectExporter&) const;
String getFilenameForProxyFile() const;
static bool hasSuffix (const File&, const char*);
};
Array<CompileUnit> getAllCompileUnits (build_tools::ProjectType::Target::Type forTarget =
build_tools::ProjectType::Target::unspecified) const;
void findAndAddCompiledUnits (ProjectExporter&, ProjectSaver*, Array<File>& result,
build_tools::ProjectType::Target::Type forTarget =
build_tools::ProjectType::Target::unspecified) const;
ModuleDescription moduleInfo;
private:
void addSearchPathsToExporter (ProjectExporter&) const;
void addDefinesToExporter (ProjectExporter&) const;
void addCompileUnitsToExporter (ProjectExporter&, ProjectSaver&) const;
void addLibsToExporter (ProjectExporter&) const;
void addBrowseableCode (ProjectExporter&, const Array<File>& compiled, const File& localModuleFolder) const;
mutable Array<File> sourceFiles;
OwnedArray<Project::ConfigFlag> configFlags;
};
//==============================================================================
class EnabledModulesList
{
public:
EnabledModulesList (Project&, const ValueTree&);
//==============================================================================
ValueTree getState() const { return state; }
StringArray getAllModules() const;
void createRequiredModules (OwnedArray<LibraryModule>& modules);
void sortAlphabetically();
File getDefaultModulesFolder() const;
int getNumModules() const { return state.getNumChildren(); }
String getModuleID (int index) const { return state.getChild (index) [Ids::ID].toString(); }
ModuleDescription getModuleInfo (const String& moduleID) const;
bool isModuleEnabled (const String& moduleID) const;
StringArray getExtraDependenciesNeeded (const String& moduleID) const;
bool tryToFixMissingDependencies (const String& moduleID);
bool doesModuleHaveHigherCppStandardThanProject (const String& moduleID) const;
bool shouldUseGlobalPath (const String& moduleID) const;
Value shouldUseGlobalPathValue (const String& moduleID) const;
bool shouldShowAllModuleFilesInProject (const String& moduleID) const;
Value shouldShowAllModuleFilesInProjectValue (const String& moduleID) const;
bool shouldCopyModuleFilesLocally (const String& moduleID) const;
Value shouldCopyModuleFilesLocallyValue (const String& moduleID) const;
bool areMostModulesUsingGlobalPath() const;
bool areMostModulesCopiedLocally() const;
StringArray getModulesWithHigherCppStandardThanProject() const;
StringArray getModulesWithMissingDependencies() const;
String getHighestModuleCppStandard() const;
//==============================================================================
void addModule (const File& moduleManifestFile, bool copyLocally, bool useGlobalPath);
void addModuleInteractive (const String& moduleID);
void addModuleFromUserSelectedFile();
void addModuleOfferingToCopy (const File&, bool isFromUserSpecifiedFolder);
void removeModule (String moduleID);
private:
UndoManager* getUndoManager() const { return project.getUndoManagerFor (state); }
Project& project;
CriticalSection stateLock;
ValueTree state;
std::unique_ptr<FileChooser> chooser;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModulesList)
};
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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 "../jucer_Project.h"
class ProjectExporter;
class ProjectSaver;
//==============================================================================
class LibraryModule
{
public:
LibraryModule (const ModuleDescription&);
bool isValid() const { return moduleDescription.isValid(); }
String getID() const { return moduleDescription.getID(); }
String getVendor() const { return moduleDescription.getVendor(); }
String getVersion() const { return moduleDescription.getVersion(); }
String getName() const { return moduleDescription.getName(); }
String getDescription() const { return moduleDescription.getDescription(); }
String getLicense() const { return moduleDescription.getLicense(); }
String getMinimumCppStandard() const { return moduleDescription.getMinimumCppStandard(); }
File getFolder() const { return moduleDescription.getFolder(); }
void writeIncludes (ProjectSaver&, OutputStream&);
void addSettingsForModuleToExporter (ProjectExporter&, ProjectSaver&) const;
void getConfigFlags (Project&, OwnedArray<Project::ConfigFlag>& flags) const;
void findBrowseableFiles (const File& localModuleFolder, Array<File>& files) const;
struct CompileUnit
{
File file;
bool isCompiledForObjC = false, isCompiledForNonObjC = false;
bool isNeededForExporter (ProjectExporter&) const;
String getFilenameForProxyFile() const;
static bool hasSuffix (const File&, const char*);
};
Array<CompileUnit> getAllCompileUnits (build_tools::ProjectType::Target::Type forTarget =
build_tools::ProjectType::Target::unspecified) const;
void findAndAddCompiledUnits (ProjectExporter&, ProjectSaver*, Array<File>& result,
build_tools::ProjectType::Target::Type forTarget =
build_tools::ProjectType::Target::unspecified) const;
ModuleDescription moduleDescription;
private:
void addSearchPathsToExporter (ProjectExporter&) const;
void addDefinesToExporter (ProjectExporter&) const;
void addCompileUnitsToExporter (ProjectExporter&, ProjectSaver&) const;
void addLibsToExporter (ProjectExporter&) const;
void addBrowseableCode (ProjectExporter&, const Array<File>& compiled, const File& localModuleFolder) const;
mutable Array<File> sourceFiles;
OwnedArray<Project::ConfigFlag> configFlags;
};
//==============================================================================
class EnabledModulesList
{
public:
EnabledModulesList (Project&, const ValueTree&);
//==============================================================================
ValueTree getState() const { return state; }
StringArray getAllModules() const;
void createRequiredModules (OwnedArray<LibraryModule>& modules);
void sortAlphabetically();
File getDefaultModulesFolder() const;
int getNumModules() const { return state.getNumChildren(); }
String getModuleID (int index) const { return state.getChild (index) [Ids::ID].toString(); }
ModuleDescription getModuleInfo (const String& moduleID) const;
bool isModuleEnabled (const String& moduleID) const;
StringArray getExtraDependenciesNeeded (const String& moduleID) const;
bool tryToFixMissingDependencies (const String& moduleID);
bool doesModuleHaveHigherCppStandardThanProject (const String& moduleID) const;
bool shouldUseGlobalPath (const String& moduleID) const;
Value shouldUseGlobalPathValue (const String& moduleID) const;
bool shouldShowAllModuleFilesInProject (const String& moduleID) const;
Value shouldShowAllModuleFilesInProjectValue (const String& moduleID) const;
bool shouldCopyModuleFilesLocally (const String& moduleID) const;
Value shouldCopyModuleFilesLocallyValue (const String& moduleID) const;
bool areMostModulesUsingGlobalPath() const;
bool areMostModulesCopiedLocally() const;
StringArray getModulesWithHigherCppStandardThanProject() const;
StringArray getModulesWithMissingDependencies() const;
String getHighestModuleCppStandard() const;
//==============================================================================
void addModule (const File& moduleManifestFile, bool copyLocally, bool useGlobalPath);
void addModuleInteractive (const String& moduleID);
void addModuleFromUserSelectedFile();
void addModuleOfferingToCopy (const File&, bool isFromUserSpecifiedFolder);
void removeModule (String moduleID);
private:
UndoManager* getUndoManager() const { return project.getUndoManagerFor (state); }
Project& project;
CriticalSection stateLock;
ValueTree state;
std::unique_ptr<FileChooser> chooser;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModulesList)
};

View File

@ -1,401 +1,400 @@
/*
==============================================================================
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
//==============================================================================
class ExporterItem : public ProjectTreeItemBase,
private Value::Listener
{
public:
ExporterItem (Project& p, ProjectExporter* e, int index)
: project (p), exporter (e), configListTree (exporter->getConfigurations()),
exporterIndex (index)
{
exporter->initialiseDependencyPathValues();
configListTree.addListener (this);
targetLocationValue.referTo (exporter->getTargetLocationValue());
targetLocationValue.addListener (this);
}
int getItemHeight() const override { return 25; }
bool canBeSelected() const override { return true; }
bool mightContainSubItems() override { return exporter->getNumConfigurations() > 0; }
String getUniqueName() const override { return "exporter_" + String (exporterIndex); }
String getRenamingName() const override { return getDisplayName(); }
String getDisplayName() const override { return exporter->getUniqueName(); }
void setName (const String&) override {}
bool isMissing() const override { return false; }
String getTooltip() override { return getDisplayName(); }
static Icon getIconForExporter (ProjectExporter* e)
{
if (e != nullptr)
{
if (e->isXcode()) return Icon (getIcons().xcode, Colours::transparentBlack);
else if (e->isVisualStudio()) return Icon (getIcons().visualStudio, Colours::transparentBlack);
else if (e->isAndroid()) return Icon (getIcons().android, Colours::transparentBlack);
else if (e->isCodeBlocks()) return Icon (getIcons().codeBlocks, Colours::transparentBlack);
else if (e->isMakefile()) return Icon (getIcons().linux, Colours::transparentBlack);
else if (e->isCLion()) return Icon (getIcons().clion, Colours::transparentBlack);
}
return Icon();
}
Icon getIcon() const override
{
return getIconForExporter (exporter.get()).withColour (getContentColour (true));
}
void showDocument() override
{
showSettingsPage (new SettingsComp (*exporter));
}
void deleteItem() override
{
auto resultCallback = [safeThis = WeakReference<ExporterItem> { this }] (int result)
{
if (safeThis == nullptr || result == 0)
return;
safeThis->closeSettingsPage();
auto parent = safeThis->exporter->settings.getParent();
parent.removeChild (safeThis->exporter->settings,
safeThis->project.getUndoManagerFor (parent));
};
AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
"Delete Exporter",
"Are you sure you want to delete this export target?",
"",
"",
nullptr,
ModalCallbackFunction::create (std::move (resultCallback)));
}
void addSubItems() override
{
for (ProjectExporter::ConfigIterator config (*exporter); config.next();)
addSubItem (new ConfigItem (config.config, *exporter));
}
void showPopupMenu (Point<int> p) override
{
PopupMenu menu;
menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations());
menu.addItem (2, "Save this exporter");
menu.addSeparator();
menu.addItem (3, "Delete this exporter");
launchPopupMenu (menu, p);
}
void showAddMenu (Point<int> p) override
{
PopupMenu menu;
menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations());
launchPopupMenu (menu, p);
}
void handlePopupMenuResult (int resultCode) override
{
if (resultCode == 1)
exporter->addNewConfiguration (false);
else if (resultCode == 2)
project.saveProject (Async::yes, exporter.get(), nullptr);
else if (resultCode == 3)
deleteAllSelectedItems();
}
var getDragSourceDescription() override
{
return getParentItem()->getUniqueName() + "/" + String (exporterIndex);
}
bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
{
return dragSourceDetails.description.toString().startsWith (getUniqueName());
}
void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
{
auto oldIndex = indexOfConfig (dragSourceDetails.description.toString().fromLastOccurrenceOf ("||", false, false));
if (oldIndex >= 0)
configListTree.moveChild (oldIndex, insertIndex, project.getUndoManagerFor (configListTree));
}
int indexOfConfig (const String& configName)
{
int i = 0;
for (ProjectExporter::ConfigIterator config (*exporter); config.next(); ++i)
if (config->getName() == configName)
return i;
return -1;
}
//==============================================================================
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { refreshIfNeeded (parentTree); }
void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { refreshIfNeeded (parentTree); }
void refreshIfNeeded (ValueTree& changedTree)
{
if (changedTree == configListTree)
refreshSubItems();
}
private:
Project& project;
std::unique_ptr<ProjectExporter> exporter;
ValueTree configListTree;
int exporterIndex;
Value targetLocationValue;
void valueChanged (Value& value) override
{
if (value == exporter->getTargetLocationValue())
refreshSubItems();
}
//==============================================================================
struct SettingsComp : public Component
{
SettingsComp (ProjectExporter& exp)
: group (exp.getUniqueName(),
ExporterItem::getIconForExporter (&exp),
exp.getDescription())
{
addAndMakeVisible (group);
PropertyListBuilder props;
exp.createPropertyEditors (props);
group.setProperties (props);
parentSizeChanged();
}
void parentSizeChanged() override { updateSize (*this, group); }
void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
PropertyGroupComponent group;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterItem)
JUCE_DECLARE_WEAK_REFERENCEABLE (ExporterItem)
};
//==============================================================================
class ConfigItem : public ProjectTreeItemBase
{
public:
ConfigItem (const ProjectExporter::BuildConfiguration::Ptr& conf, ProjectExporter& e)
: config (conf), exporter (e), configTree (config->config)
{
jassert (config != nullptr);
configTree.addListener (this);
}
bool isMissing() const override { return false; }
bool canBeSelected() const override { return true; }
bool mightContainSubItems() override { return false; }
String getUniqueName() const override { return "config_" + config->getName(); }
String getRenamingName() const override { return getDisplayName(); }
String getDisplayName() const override { return config->getName(); }
void setName (const String&) override {}
Icon getIcon() const override { return Icon (getIcons().config, getContentColour (true)); }
void itemOpennessChanged (bool) override {}
void showDocument() override
{
showSettingsPage (new SettingsComp (*config));
}
void deleteItem() override
{
AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
"Delete Configuration",
"Are you sure you want to delete this configuration?",
"",
"",
nullptr,
ModalCallbackFunction::create ([parent = WeakReference<ConfigItem> { this }] (int result)
{
if (parent == nullptr)
return;
if (result == 0)
return;
parent->closeSettingsPage();
parent->config->removeFromExporter();
}));
}
void showPopupMenu (Point<int> p) override
{
bool enabled = exporter.supportsUserDefinedConfigurations();
PopupMenu menu;
menu.addItem (1, "Create a copy of this configuration", enabled);
menu.addSeparator();
menu.addItem (2, "Delete this configuration", enabled);
launchPopupMenu (menu, p);
}
void handlePopupMenuResult (int resultCode) override
{
if (resultCode == 1)
exporter.addNewConfigurationFromExisting (*config);
else if (resultCode == 2)
deleteAllSelectedItems();
}
var getDragSourceDescription() override
{
return getParentItem()->getUniqueName() + "||" + config->getName();
}
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { repaintItem(); }
private:
ProjectExporter::BuildConfiguration::Ptr config;
ProjectExporter& exporter;
ValueTree configTree;
//==============================================================================
class SettingsComp : public Component
{
public:
SettingsComp (ProjectExporter::BuildConfiguration& conf)
: group (conf.exporter.getUniqueName() + " - " + conf.getName(), Icon (getIcons().config, Colours::transparentBlack))
{
addAndMakeVisible (group);
PropertyListBuilder props;
conf.createPropertyEditors (props);
group.setProperties (props);
parentSizeChanged();
}
void parentSizeChanged() override { updateSize (*this, group); }
void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
private:
PropertyGroupComponent group;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConfigItem)
JUCE_DECLARE_WEAK_REFERENCEABLE (ConfigItem)
};
//==============================================================================
class ExportersTreeRoot : public ProjectTreeItemBase
{
public:
ExportersTreeRoot (Project& p)
: project (p),
exportersTree (project.getExporters())
{
exportersTree.addListener (this);
}
bool isRoot() const override { return true; }
bool canBeSelected() const override { return true; }
bool isMissing() const override { return false; }
bool mightContainSubItems() override { return project.getNumExporters() > 0; }
String getUniqueName() const override { return "exporters"; }
String getRenamingName() const override { return getDisplayName(); }
String getDisplayName() const override { return "Exporters"; }
void setName (const String&) override {}
Icon getIcon() const override { return project.getMainGroup().getIcon (isOpen()).withColour (getContentColour (true)); }
void showPopupMenu (Point<int>) override
{
if (auto* pcc = getProjectContentComponent())
pcc->showNewExporterMenu();
}
void addSubItems() override
{
int i = 0;
for (Project::ExporterIterator exporter (project); exporter.next(); ++i)
addSubItem (new TreeItemTypes::ExporterItem (project, exporter.exporter.release(), i));
}
bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
{
return dragSourceDetails.description.toString().startsWith (getUniqueName());
}
void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
{
int oldIndex = dragSourceDetails.description.toString().getTrailingIntValue();
exportersTree.moveChild (oldIndex, jmax (0, insertIndex), project.getUndoManagerFor (exportersTree));
}
void itemOpennessChanged (bool isNowOpen) override
{
if (isNowOpen)
refreshSubItems();
}
void removeExporter (int index)
{
if (auto* exporter = dynamic_cast<TreeItemTypes::ExporterItem*> (getSubItem (index)))
exporter->deleteItem();
}
private:
Project& project;
ValueTree exportersTree;
//==============================================================================
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { refreshIfNeeded (parentTree); }
void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { refreshIfNeeded (parentTree); }
void refreshIfNeeded (ValueTree& changedTree)
{
if (changedTree == exportersTree)
refreshSubItems();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExportersTreeRoot)
};
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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
//==============================================================================
class ExporterItem : public ProjectTreeItemBase,
private Value::Listener
{
public:
ExporterItem (Project& p, ProjectExporter* e, int index)
: project (p), exporter (e), configListTree (exporter->getConfigurations()),
exporterIndex (index)
{
exporter->initialiseDependencyPathValues();
configListTree.addListener (this);
targetLocationValue.referTo (exporter->getTargetLocationValue());
targetLocationValue.addListener (this);
}
int getItemHeight() const override { return 25; }
bool canBeSelected() const override { return true; }
bool mightContainSubItems() override { return exporter->getNumConfigurations() > 0; }
String getUniqueName() const override { return "exporter_" + String (exporterIndex); }
String getRenamingName() const override { return getDisplayName(); }
String getDisplayName() const override { return exporter->getUniqueName(); }
void setName (const String&) override {}
bool isMissing() const override { return false; }
String getTooltip() override { return getDisplayName(); }
static Icon getIconForExporter (ProjectExporter* e)
{
if (e != nullptr)
{
if (e->isXcode()) return Icon (getIcons().xcode, Colours::transparentBlack);
else if (e->isVisualStudio()) return Icon (getIcons().visualStudio, Colours::transparentBlack);
else if (e->isAndroid()) return Icon (getIcons().android, Colours::transparentBlack);
else if (e->isCodeBlocks()) return Icon (getIcons().codeBlocks, Colours::transparentBlack);
else if (e->isMakefile()) return Icon (getIcons().linux, Colours::transparentBlack);
}
return Icon();
}
Icon getIcon() const override
{
return getIconForExporter (exporter.get()).withColour (getContentColour (true));
}
void showDocument() override
{
showSettingsPage (new SettingsComp (*exporter));
}
void deleteItem() override
{
auto resultCallback = [safeThis = WeakReference<ExporterItem> { this }] (int result)
{
if (safeThis == nullptr || result == 0)
return;
safeThis->closeSettingsPage();
auto parent = safeThis->exporter->settings.getParent();
parent.removeChild (safeThis->exporter->settings,
safeThis->project.getUndoManagerFor (parent));
};
AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
"Delete Exporter",
"Are you sure you want to delete this export target?",
"",
"",
nullptr,
ModalCallbackFunction::create (std::move (resultCallback)));
}
void addSubItems() override
{
for (ProjectExporter::ConfigIterator config (*exporter); config.next();)
addSubItem (new ConfigItem (config.config, *exporter));
}
void showPopupMenu (Point<int> p) override
{
PopupMenu menu;
menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations());
menu.addItem (2, "Save this exporter");
menu.addSeparator();
menu.addItem (3, "Delete this exporter");
launchPopupMenu (menu, p);
}
void showAddMenu (Point<int> p) override
{
PopupMenu menu;
menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations());
launchPopupMenu (menu, p);
}
void handlePopupMenuResult (int resultCode) override
{
if (resultCode == 1)
exporter->addNewConfiguration (false);
else if (resultCode == 2)
project.saveProject (Async::yes, exporter.get(), nullptr);
else if (resultCode == 3)
deleteAllSelectedItems();
}
var getDragSourceDescription() override
{
return getParentItem()->getUniqueName() + "/" + String (exporterIndex);
}
bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
{
return dragSourceDetails.description.toString().startsWith (getUniqueName());
}
void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
{
auto oldIndex = indexOfConfig (dragSourceDetails.description.toString().fromLastOccurrenceOf ("||", false, false));
if (oldIndex >= 0)
configListTree.moveChild (oldIndex, insertIndex, project.getUndoManagerFor (configListTree));
}
int indexOfConfig (const String& configName)
{
int i = 0;
for (ProjectExporter::ConfigIterator config (*exporter); config.next(); ++i)
if (config->getName() == configName)
return i;
return -1;
}
//==============================================================================
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { refreshIfNeeded (parentTree); }
void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { refreshIfNeeded (parentTree); }
void refreshIfNeeded (ValueTree& changedTree)
{
if (changedTree == configListTree)
refreshSubItems();
}
private:
Project& project;
std::unique_ptr<ProjectExporter> exporter;
ValueTree configListTree;
int exporterIndex;
Value targetLocationValue;
void valueChanged (Value& value) override
{
if (value == exporter->getTargetLocationValue())
refreshSubItems();
}
//==============================================================================
struct SettingsComp : public Component
{
SettingsComp (ProjectExporter& exp)
: group (exp.getUniqueName(),
ExporterItem::getIconForExporter (&exp),
exp.getDescription())
{
addAndMakeVisible (group);
PropertyListBuilder props;
exp.createPropertyEditors (props);
group.setProperties (props);
parentSizeChanged();
}
void parentSizeChanged() override { updateSize (*this, group); }
void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
PropertyGroupComponent group;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterItem)
JUCE_DECLARE_WEAK_REFERENCEABLE (ExporterItem)
};
//==============================================================================
class ConfigItem : public ProjectTreeItemBase
{
public:
ConfigItem (const ProjectExporter::BuildConfiguration::Ptr& conf, ProjectExporter& e)
: config (conf), exporter (e), configTree (config->config)
{
jassert (config != nullptr);
configTree.addListener (this);
}
bool isMissing() const override { return false; }
bool canBeSelected() const override { return true; }
bool mightContainSubItems() override { return false; }
String getUniqueName() const override { return "config_" + config->getName(); }
String getRenamingName() const override { return getDisplayName(); }
String getDisplayName() const override { return config->getName(); }
void setName (const String&) override {}
Icon getIcon() const override { return Icon (getIcons().config, getContentColour (true)); }
void itemOpennessChanged (bool) override {}
void showDocument() override
{
showSettingsPage (new SettingsComp (*config));
}
void deleteItem() override
{
AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
"Delete Configuration",
"Are you sure you want to delete this configuration?",
"",
"",
nullptr,
ModalCallbackFunction::create ([parent = WeakReference<ConfigItem> { this }] (int result)
{
if (parent == nullptr)
return;
if (result == 0)
return;
parent->closeSettingsPage();
parent->config->removeFromExporter();
}));
}
void showPopupMenu (Point<int> p) override
{
bool enabled = exporter.supportsUserDefinedConfigurations();
PopupMenu menu;
menu.addItem (1, "Create a copy of this configuration", enabled);
menu.addSeparator();
menu.addItem (2, "Delete this configuration", enabled);
launchPopupMenu (menu, p);
}
void handlePopupMenuResult (int resultCode) override
{
if (resultCode == 1)
exporter.addNewConfigurationFromExisting (*config);
else if (resultCode == 2)
deleteAllSelectedItems();
}
var getDragSourceDescription() override
{
return getParentItem()->getUniqueName() + "||" + config->getName();
}
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { repaintItem(); }
private:
ProjectExporter::BuildConfiguration::Ptr config;
ProjectExporter& exporter;
ValueTree configTree;
//==============================================================================
class SettingsComp : public Component
{
public:
SettingsComp (ProjectExporter::BuildConfiguration& conf)
: group (conf.exporter.getUniqueName() + " - " + conf.getName(), Icon (getIcons().config, Colours::transparentBlack))
{
addAndMakeVisible (group);
PropertyListBuilder props;
conf.createPropertyEditors (props);
group.setProperties (props);
parentSizeChanged();
}
void parentSizeChanged() override { updateSize (*this, group); }
void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
private:
PropertyGroupComponent group;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConfigItem)
JUCE_DECLARE_WEAK_REFERENCEABLE (ConfigItem)
};
//==============================================================================
class ExportersTreeRoot : public ProjectTreeItemBase
{
public:
ExportersTreeRoot (Project& p)
: project (p),
exportersTree (project.getExporters())
{
exportersTree.addListener (this);
}
bool isRoot() const override { return true; }
bool canBeSelected() const override { return true; }
bool isMissing() const override { return false; }
bool mightContainSubItems() override { return project.getNumExporters() > 0; }
String getUniqueName() const override { return "exporters"; }
String getRenamingName() const override { return getDisplayName(); }
String getDisplayName() const override { return "Exporters"; }
void setName (const String&) override {}
Icon getIcon() const override { return project.getMainGroup().getIcon (isOpen()).withColour (getContentColour (true)); }
void showPopupMenu (Point<int>) override
{
if (auto* pcc = getProjectContentComponent())
pcc->showNewExporterMenu();
}
void addSubItems() override
{
int i = 0;
for (Project::ExporterIterator exporter (project); exporter.next(); ++i)
addSubItem (new TreeItemTypes::ExporterItem (project, exporter.exporter.release(), i));
}
bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
{
return dragSourceDetails.description.toString().startsWith (getUniqueName());
}
void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
{
int oldIndex = dragSourceDetails.description.toString().getTrailingIntValue();
exportersTree.moveChild (oldIndex, jmax (0, insertIndex), project.getUndoManagerFor (exportersTree));
}
void itemOpennessChanged (bool isNowOpen) override
{
if (isNowOpen)
refreshSubItems();
}
void removeExporter (int index)
{
if (auto* exporter = dynamic_cast<TreeItemTypes::ExporterItem*> (getSubItem (index)))
exporter->deleteItem();
}
private:
Project& project;
ValueTree exportersTree;
//==============================================================================
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { refreshIfNeeded (parentTree); }
void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { refreshIfNeeded (parentTree); }
void refreshIfNeeded (ValueTree& changedTree)
{
if (changedTree == exportersTree)
refreshSubItems();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExportersTreeRoot)
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,81 +1,81 @@
/*
==============================================================================
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
//==============================================================================
struct ProjectTreeItemBase : public JucerTreeViewBase,
public ValueTree::Listener
{
ProjectTreeItemBase() {}
void showSettingsPage (Component* content)
{
content->setComponentID (getUniqueName());
std::unique_ptr<Component> comp (content);
if (auto* pcc = getProjectContentComponent())
pcc->setScrollableEditorComponent (std::move (comp));
}
void closeSettingsPage()
{
if (auto* pcc = getProjectContentComponent())
if (auto* content = pcc->getEditorComponent())
if (content->getComponentID() == getUniqueName())
pcc->hideEditor();
}
void deleteAllSelectedItems() override
{
auto* tree = getOwnerView();
jassert (tree->getNumSelectedItems() <= 1); // multi-select should be disabled
if (auto* s = dynamic_cast<ProjectTreeItemBase*> (tree->getSelectedItem (0)))
s->deleteItem();
}
void itemOpennessChanged (bool isNowOpen) override
{
if (isNowOpen)
refreshSubItems();
}
virtual bool isProjectSettings() const { return false; }
virtual bool isModulesList() const { return false; }
static void updateSize (Component& comp, PropertyGroupComponent& group)
{
auto width = jmax (550, comp.getParentWidth() - 12);
auto y = 0;
y += group.updateSize (12, y, width - 12);
y = jmax (comp.getParentHeight(), y);
comp.setSize (width, y);
}
};
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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
//==============================================================================
struct ProjectTreeItemBase : public JucerTreeViewBase,
public ValueTree::Listener
{
ProjectTreeItemBase() {}
void showSettingsPage (Component* content)
{
content->setComponentID (getUniqueName());
std::unique_ptr<Component> comp (content);
if (auto* pcc = getProjectContentComponent())
pcc->setScrollableEditorComponent (std::move (comp));
}
void closeSettingsPage()
{
if (auto* pcc = getProjectContentComponent())
if (auto* content = pcc->getEditorComponent())
if (content->getComponentID() == getUniqueName())
pcc->hideEditor();
}
void deleteAllSelectedItems() override
{
auto* tree = getOwnerView();
jassert (tree->getNumSelectedItems() <= 1); // multi-select should be disabled
if (auto* s = dynamic_cast<ProjectTreeItemBase*> (tree->getSelectedItem (0)))
s->deleteItem();
}
void itemOpennessChanged (bool isNowOpen) override
{
if (isNowOpen)
refreshSubItems();
}
virtual bool isProjectSettings() const { return false; }
virtual bool isModulesList() const { return false; }
static void updateSize (Component& comp, PropertyGroupComponent& group)
{
auto width = jmax (550, comp.getParentWidth() - 12);
auto y = 0;
y += group.updateSize (12, y, width - 12);
y = jmax (comp.getParentHeight(), y);
comp.setSize (width, y);
}
};

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +1,42 @@
/*
==============================================================================
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 "../../../Application/Windows/jucer_TranslationToolWindowComponent.h"
#include "../../../Utility/UI/jucer_JucerTreeViewBase.h"
#include "../../../Utility/Helpers/jucer_NewFileWizard.h"
#include "../jucer_ContentViewComponents.h"
#include "../jucer_FileGroupInformationComponent.h"
#include "../jucer_ModulesInformationComponent.h"
struct TreeItemTypes
{
#include "jucer_ProjectTreeItemBase.h"
#include "jucer_ExporterTreeItems.h"
#include "jucer_ModuleTreeItems.h"
#include "jucer_FileTreeItems.h"
};
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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 "../../../Application/Windows/jucer_TranslationToolWindowComponent.h"
#include "../../../Utility/UI/jucer_JucerTreeViewBase.h"
#include "../../../Utility/Helpers/jucer_NewFileWizard.h"
#include "../jucer_ContentViewComponents.h"
#include "../jucer_FileGroupInformationComponent.h"
#include "../jucer_ModulesInformationComponent.h"
struct TreeItemTypes
{
#include "jucer_ProjectTreeItemBase.h"
#include "jucer_ExporterTreeItems.h"
#include "jucer_ModuleTreeItems.h"
#include "jucer_FileTreeItems.h"
};

View File

@ -1,112 +1,112 @@
/*
==============================================================================
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
//==============================================================================
class ContentViewComponent : public Component
{
public:
ContentViewComponent()
{
setTitle ("Content");
setFocusContainerType (Component::FocusContainerType::focusContainer);
addAndMakeVisible (logoComponent);
addAndMakeVisible (fileNameLabel);
fileNameLabel.setJustificationType (Justification::centred);
}
void resized() override
{
auto bounds = getLocalBounds();
fileNameLabel.setBounds (bounds.removeFromTop (15));
if (content != nullptr)
content->setBounds (bounds);
else
logoComponent.setBounds (bounds);
}
Component* getCurrentComponent() noexcept
{
return content.get();
}
void setContent (std::unique_ptr<Component> newContent,
const String& labelText)
{
content = std::move (newContent);
addAndMakeVisible (content.get());
fileNameLabel.setVisible (labelText.isNotEmpty());
fileNameLabel.setText (labelText, dontSendNotification);
resized();
}
private:
class LogoComponent : public Component
{
public:
void paint (Graphics& g) override
{
g.setColour (findColour (defaultTextColourId));
auto bounds = getLocalBounds();
bounds.reduce (bounds.getWidth() / 6, bounds.getHeight() / 6);
g.setFont (15.0f);
g.drawFittedText (versionInfo, bounds.removeFromBottom (50), Justification::centredBottom, 3);
if (logo != nullptr)
logo->drawWithin (g, bounds.withTrimmedBottom (bounds.getHeight() / 4).toFloat(),
RectanglePlacement (RectanglePlacement::centred), 1.0f);
}
private:
std::unique_ptr<Drawable> logo = []() -> std::unique_ptr<Drawable>
{
if (auto svg = parseXML (BinaryData::background_logo_svg))
return Drawable::createFromSVG (*svg);
jassertfalse;
return {};
}();
String versionInfo = SystemStats::getJUCEVersion()
+ newLine
+ ProjucerApplication::getApp().getVersionDescription();
};
std::unique_ptr<Component> content;
LogoComponent logoComponent;
Label fileNameLabel;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentViewComponent)
};
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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
//==============================================================================
class ContentViewComponent : public Component
{
public:
ContentViewComponent()
{
setTitle ("Content");
setFocusContainerType (Component::FocusContainerType::focusContainer);
addAndMakeVisible (logoComponent);
addAndMakeVisible (fileNameLabel);
fileNameLabel.setJustificationType (Justification::centred);
}
void resized() override
{
auto bounds = getLocalBounds();
fileNameLabel.setBounds (bounds.removeFromTop (15));
if (content != nullptr)
content->setBounds (bounds);
else
logoComponent.setBounds (bounds);
}
Component* getCurrentComponent() noexcept
{
return content.get();
}
void setContent (std::unique_ptr<Component> newContent,
const String& labelText)
{
content = std::move (newContent);
addAndMakeVisible (content.get());
fileNameLabel.setVisible (labelText.isNotEmpty());
fileNameLabel.setText (labelText, dontSendNotification);
resized();
}
private:
class LogoComponent : public Component
{
public:
void paint (Graphics& g) override
{
g.setColour (findColour (defaultTextColourId));
auto bounds = getLocalBounds();
bounds.reduce (bounds.getWidth() / 6, bounds.getHeight() / 6);
g.setFont (15.0f);
g.drawFittedText (versionInfo, bounds.removeFromBottom (50), Justification::centredBottom, 3);
if (logo != nullptr)
logo->drawWithin (g, bounds.withTrimmedBottom (bounds.getHeight() / 4).toFloat(),
RectanglePlacement (RectanglePlacement::centred), 1.0f);
}
private:
std::unique_ptr<Drawable> logo = []() -> std::unique_ptr<Drawable>
{
if (auto svg = parseXML (BinaryData::background_logo_svg))
return Drawable::createFromSVG (*svg);
jassertfalse;
return {};
}();
String versionInfo = SystemStats::getJUCEVersion()
+ newLine
+ ProjucerApplication::getApp().getVersionDescription();
};
std::unique_ptr<Component> content;
LogoComponent logoComponent;
Label fileNameLabel;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentViewComponent)
};

View File

@ -1,446 +1,445 @@
/*
==============================================================================
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 "../../Utility/UI/PropertyComponents/jucer_LabelPropertyComponent.h"
//==============================================================================
struct ContentViewHeader : public Component
{
ContentViewHeader (String headerName, Icon headerIcon)
: name (headerName), icon (headerIcon)
{
setTitle (name);
}
void paint (Graphics& g) override
{
g.fillAll (findColour (contentHeaderBackgroundColourId));
auto bounds = getLocalBounds().reduced (20, 0);
icon.withColour (Colours::white).draw (g, bounds.toFloat().removeFromRight (30), false);
g.setColour (Colours::white);
g.setFont (Font (18.0f));
g.drawFittedText (name, bounds, Justification::centredLeft, 1);
}
String name;
Icon icon;
};
//==============================================================================
class ListBoxHeader : public Component
{
public:
ListBoxHeader (Array<String> columnHeaders)
{
for (auto s : columnHeaders)
{
addAndMakeVisible (headers.add (new Label (s, s)));
widths.add (1.0f / (float) columnHeaders.size());
}
setSize (200, 40);
}
ListBoxHeader (Array<String> columnHeaders, Array<float> columnWidths)
{
jassert (columnHeaders.size() == columnWidths.size());
auto index = 0;
for (auto s : columnHeaders)
{
addAndMakeVisible (headers.add (new Label (s, s)));
widths.add (columnWidths.getUnchecked (index++));
}
recalculateWidths();
setSize (200, 40);
}
void resized() override
{
auto bounds = getLocalBounds();
auto width = bounds.getWidth();
auto index = 0;
for (auto h : headers)
{
auto headerWidth = roundToInt ((float) width * widths.getUnchecked (index));
h->setBounds (bounds.removeFromLeft (headerWidth));
++index;
}
}
void setColumnHeaderWidth (int index, float proportionOfWidth)
{
if (! (isPositiveAndBelow (index, headers.size()) && isPositiveAndNotGreaterThan (proportionOfWidth, 1.0f)))
{
jassertfalse;
return;
}
widths.set (index, proportionOfWidth);
recalculateWidths (index);
}
int getColumnX (int index)
{
auto prop = 0.0f;
for (int i = 0; i < index; ++i)
prop += widths.getUnchecked (i);
return roundToInt (prop * (float) getWidth());
}
float getProportionAtIndex (int index)
{
jassert (isPositiveAndBelow (index, widths.size()));
return widths.getUnchecked (index);
}
private:
OwnedArray<Label> headers;
Array<float> widths;
void recalculateWidths (int indexToIgnore = -1)
{
auto total = 0.0f;
for (auto w : widths)
total += w;
if (total == 1.0f)
return;
auto diff = 1.0f - total;
auto amount = diff / static_cast<float> (indexToIgnore == -1 ? widths.size() : widths.size() - 1);
for (int i = 0; i < widths.size(); ++i)
{
if (i != indexToIgnore)
{
auto val = widths.getUnchecked (i);
widths.set (i, val + amount);
}
}
}
};
//==============================================================================
class InfoButton : public Button
{
public:
InfoButton (const String& infoToDisplay = {})
: Button ({})
{
setTitle ("Info");
if (infoToDisplay.isNotEmpty())
setInfoToDisplay (infoToDisplay);
setSize (20, 20);
}
void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override
{
auto bounds = getLocalBounds().toFloat().reduced (2);
auto& icon = getIcons().info;
g.setColour (findColour (treeIconColourId).withMultipliedAlpha (isMouseOverButton || isButtonDown ? 1.0f : 0.5f));
if (isButtonDown)
g.fillEllipse (bounds);
else
g.fillPath (icon, RectanglePlacement (RectanglePlacement::centred)
.getTransformToFit (icon.getBounds(), bounds));
}
void clicked() override
{
auto w = std::make_unique<InfoWindow> (info);
w->setSize (width, w->getHeight() * numLines + 10);
CallOutBox::launchAsynchronously (std::move (w), getScreenBounds(), nullptr);
}
using Button::clicked;
void setInfoToDisplay (const String& infoToDisplay)
{
if (infoToDisplay.isNotEmpty())
{
info = infoToDisplay;
auto stringWidth = roundToInt (Font (14.0f).getStringWidthFloat (info));
width = jmin (300, stringWidth);
numLines += static_cast<int> (stringWidth / width);
setHelpText (info);
}
}
void setAssociatedComponent (Component* comp) { associatedComponent = comp; }
Component* getAssociatedComponent() { return associatedComponent; }
private:
String info;
Component* associatedComponent = nullptr;
int width;
int numLines = 1;
//==============================================================================
struct InfoWindow : public Component
{
InfoWindow (const String& s)
: stringToDisplay (s)
{
setSize (150, 14);
}
void paint (Graphics& g) override
{
g.fillAll (findColour (secondaryBackgroundColourId));
g.setColour (findColour (defaultTextColourId));
g.setFont (Font (14.0f));
g.drawFittedText (stringToDisplay, getLocalBounds(), Justification::centred, 15, 0.75f);
}
String stringToDisplay;
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InfoButton)
};
//==============================================================================
class PropertyGroupComponent : public Component,
private TextPropertyComponent::Listener
{
public:
PropertyGroupComponent (String name, Icon icon, String desc = {})
: header (name, icon),
description (desc)
{
addAndMakeVisible (header);
}
void setProperties (const PropertyListBuilder& newProps)
{
clearProperties();
if (description.isNotEmpty())
properties.push_back (std::make_unique<LabelPropertyComponent> (description, 16, Font (16.0f),
Justification::centredLeft));
for (auto* comp : newProps.components)
properties.push_back (std::unique_ptr<PropertyComponent> (comp));
for (auto& prop : properties)
{
const auto propertyTooltip = prop->getTooltip();
if (propertyTooltip.isNotEmpty())
{
// set the tooltip to empty so it only displays when its button is clicked
prop->setTooltip ({});
auto infoButton = std::make_unique<InfoButton> (propertyTooltip);
infoButton->setAssociatedComponent (prop.get());
auto propertyAndInfoWrapper = std::make_unique<PropertyAndInfoWrapper> (*prop, *infoButton.get());
addAndMakeVisible (propertyAndInfoWrapper.get());
propertyComponentsWithInfo.push_back (std::move (propertyAndInfoWrapper));
infoButtons.push_back (std::move (infoButton));
}
else
{
addAndMakeVisible (prop.get());
}
if (auto* multiChoice = dynamic_cast<MultiChoicePropertyComponent*> (prop.get()))
multiChoice->onHeightChange = [this] { updateSize(); };
if (auto* text = dynamic_cast<TextPropertyComponent*> (prop.get()))
if (text->isTextEditorMultiLine())
text->addListener (this);
}
}
int updateSize (int x, int y, int width)
{
header.setBounds (0, 0, width, headerSize);
auto height = header.getBottom() + 10;
for (auto& pp : properties)
{
const auto propertyHeight = pp->getPreferredHeight()
+ (getHeightMultiplier (pp.get()) * pp->getPreferredHeight());
auto iter = std::find_if (propertyComponentsWithInfo.begin(), propertyComponentsWithInfo.end(),
[&pp] (const std::unique_ptr<PropertyAndInfoWrapper>& w) { return &w->propertyComponent == pp.get(); });
if (iter != propertyComponentsWithInfo.end())
(*iter)->setBounds (0, height, width - 10, propertyHeight);
else
pp->setBounds (40, height, width - 50, propertyHeight);
if (shouldResizePropertyComponent (pp.get()))
resizePropertyComponent (pp.get());
height += pp->getHeight() + 10;
}
height += 16;
setBounds (x, y, width, jmax (height, getParentHeight()));
return height;
}
void paint (Graphics& g) override
{
g.fillAll (findColour (secondaryBackgroundColourId));
}
const std::vector<std::unique_ptr<PropertyComponent>>& getProperties() const noexcept
{
return properties;
}
void clearProperties()
{
propertyComponentsWithInfo.clear();
infoButtons.clear();
properties.clear();
}
private:
//==============================================================================
struct PropertyAndInfoWrapper : public Component
{
PropertyAndInfoWrapper (PropertyComponent& c, InfoButton& i)
: propertyComponent (c),
infoButton (i)
{
setFocusContainerType (FocusContainerType::focusContainer);
setTitle (propertyComponent.getName());
addAndMakeVisible (propertyComponent);
addAndMakeVisible (infoButton);
}
void resized() override
{
auto bounds = getLocalBounds();
bounds.removeFromLeft (40);
bounds.removeFromRight (10);
propertyComponent.setBounds (bounds);
infoButton.setCentrePosition (20, bounds.getHeight() / 2);
}
PropertyComponent& propertyComponent;
InfoButton& infoButton;
};
//==============================================================================
void textPropertyComponentChanged (TextPropertyComponent* comp) override
{
auto fontHeight = [comp]
{
Label tmpLabel;
return comp->getLookAndFeel().getLabelFont (tmpLabel).getHeight();
}();
auto lines = StringArray::fromLines (comp->getText());
comp->setPreferredHeight (jmax (100, 10 + roundToInt (fontHeight * (float) lines.size())));
updateSize();
}
void updateSize()
{
updateSize (getX(), getY(), getWidth());
if (auto* parent = getParentComponent())
parent->parentSizeChanged();
}
bool shouldResizePropertyComponent (PropertyComponent* p)
{
if (auto* textComp = dynamic_cast<TextPropertyComponent*> (p))
return ! textComp->isTextEditorMultiLine();
return (dynamic_cast<ChoicePropertyComponent*> (p) != nullptr
|| dynamic_cast<ButtonPropertyComponent*> (p) != nullptr
|| dynamic_cast<BooleanPropertyComponent*> (p) != nullptr);
}
void resizePropertyComponent (PropertyComponent* pp)
{
for (auto i = pp->getNumChildComponents() - 1; i >= 0; --i)
{
auto* child = pp->getChildComponent (i);
auto bounds = child->getBounds();
child->setBounds (bounds.withSizeKeepingCentre (child->getWidth(), pp->getPreferredHeight()));
}
}
int getHeightMultiplier (PropertyComponent* pp)
{
auto availableTextWidth = ProjucerLookAndFeel::getTextWidthForPropertyComponent (pp);
auto font = ProjucerLookAndFeel::getPropertyComponentFont();
auto nameWidth = font.getStringWidthFloat (pp->getName());
if (availableTextWidth == 0)
return 0;
return static_cast<int> (nameWidth / (float) availableTextWidth);
}
//==============================================================================
static constexpr int headerSize = 40;
std::vector<std::unique_ptr<PropertyComponent>> properties;
std::vector<std::unique_ptr<InfoButton>> infoButtons;
std::vector<std::unique_ptr<PropertyAndInfoWrapper>> propertyComponentsWithInfo;
ContentViewHeader header;
String description;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyGroupComponent)
};
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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 "../../Utility/UI/PropertyComponents/jucer_LabelPropertyComponent.h"
//==============================================================================
struct ContentViewHeader : public Component
{
ContentViewHeader (String headerName, Icon headerIcon)
: name (headerName), icon (headerIcon)
{
setTitle (name);
}
void paint (Graphics& g) override
{
g.fillAll (findColour (contentHeaderBackgroundColourId));
auto bounds = getLocalBounds().reduced (20, 0);
icon.withColour (Colours::white).draw (g, bounds.toFloat().removeFromRight (30), false);
g.setColour (Colours::white);
g.setFont (Font (18.0f));
g.drawFittedText (name, bounds, Justification::centredLeft, 1);
}
String name;
Icon icon;
};
//==============================================================================
class ListBoxHeader : public Component
{
public:
ListBoxHeader (Array<String> columnHeaders)
{
for (auto s : columnHeaders)
{
addAndMakeVisible (headers.add (new Label (s, s)));
widths.add (1.0f / (float) columnHeaders.size());
}
setSize (200, 40);
}
ListBoxHeader (Array<String> columnHeaders, Array<float> columnWidths)
{
jassert (columnHeaders.size() == columnWidths.size());
auto index = 0;
for (auto s : columnHeaders)
{
addAndMakeVisible (headers.add (new Label (s, s)));
widths.add (columnWidths.getUnchecked (index++));
}
recalculateWidths();
setSize (200, 40);
}
void resized() override
{
auto bounds = getLocalBounds();
auto width = bounds.getWidth();
auto index = 0;
for (auto h : headers)
{
auto headerWidth = roundToInt ((float) width * widths.getUnchecked (index));
h->setBounds (bounds.removeFromLeft (headerWidth));
++index;
}
}
void setColumnHeaderWidth (int index, float proportionOfWidth)
{
if (! (isPositiveAndBelow (index, headers.size()) && isPositiveAndNotGreaterThan (proportionOfWidth, 1.0f)))
{
jassertfalse;
return;
}
widths.set (index, proportionOfWidth);
recalculateWidths (index);
}
int getColumnX (int index)
{
auto prop = 0.0f;
for (int i = 0; i < index; ++i)
prop += widths.getUnchecked (i);
return roundToInt (prop * (float) getWidth());
}
float getProportionAtIndex (int index)
{
jassert (isPositiveAndBelow (index, widths.size()));
return widths.getUnchecked (index);
}
private:
OwnedArray<Label> headers;
Array<float> widths;
void recalculateWidths (int indexToIgnore = -1)
{
auto total = 0.0f;
for (auto w : widths)
total += w;
if (total == 1.0f)
return;
auto diff = 1.0f - total;
auto amount = diff / static_cast<float> (indexToIgnore == -1 ? widths.size() : widths.size() - 1);
for (int i = 0; i < widths.size(); ++i)
{
if (i != indexToIgnore)
{
auto val = widths.getUnchecked (i);
widths.set (i, val + amount);
}
}
}
};
//==============================================================================
class InfoButton : public Button
{
public:
InfoButton (const String& infoToDisplay = {})
: Button ({})
{
setTitle ("Info");
if (infoToDisplay.isNotEmpty())
setInfoToDisplay (infoToDisplay);
setSize (20, 20);
}
void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override
{
auto bounds = getLocalBounds().toFloat().reduced (2);
auto& icon = getIcons().info;
g.setColour (findColour (treeIconColourId).withMultipliedAlpha (isMouseOverButton || isButtonDown ? 1.0f : 0.5f));
if (isButtonDown)
g.fillEllipse (bounds);
else
g.fillPath (icon, RectanglePlacement (RectanglePlacement::centred)
.getTransformToFit (icon.getBounds(), bounds));
}
void clicked() override
{
auto w = std::make_unique<InfoWindow> (info);
w->setSize (width, w->getHeight() * numLines + 10);
CallOutBox::launchAsynchronously (std::move (w), getScreenBounds(), nullptr);
}
using Button::clicked;
void setInfoToDisplay (const String& infoToDisplay)
{
if (infoToDisplay.isNotEmpty())
{
info = infoToDisplay;
auto stringWidth = roundToInt (Font (14.0f).getStringWidthFloat (info));
width = jmin (300, stringWidth);
numLines += static_cast<int> (stringWidth / width);
setHelpText (info);
}
}
void setAssociatedComponent (Component* comp) { associatedComponent = comp; }
Component* getAssociatedComponent() { return associatedComponent; }
private:
String info;
Component* associatedComponent = nullptr;
int width;
int numLines = 1;
//==============================================================================
struct InfoWindow : public Component
{
InfoWindow (const String& s)
: stringToDisplay (s)
{
setSize (150, 14);
}
void paint (Graphics& g) override
{
g.fillAll (findColour (secondaryBackgroundColourId));
g.setColour (findColour (defaultTextColourId));
g.setFont (Font (14.0f));
g.drawFittedText (stringToDisplay, getLocalBounds(), Justification::centred, 15, 0.75f);
}
String stringToDisplay;
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InfoButton)
};
//==============================================================================
class PropertyGroupComponent : public Component,
private TextPropertyComponent::Listener
{
public:
PropertyGroupComponent (String name, Icon icon, String desc = {})
: header (name, icon),
description (desc)
{
addAndMakeVisible (header);
}
void setProperties (const PropertyListBuilder& newProps)
{
clearProperties();
if (description.isNotEmpty())
properties.push_back (std::make_unique<LabelPropertyComponent> (description, 16, Font (16.0f),
Justification::centredLeft));
for (auto* comp : newProps.components)
properties.push_back (std::unique_ptr<PropertyComponent> (comp));
for (auto& prop : properties)
{
const auto propertyTooltip = prop->getTooltip();
if (propertyTooltip.isNotEmpty())
{
// set the tooltip to empty so it only displays when its button is clicked
prop->setTooltip ({});
auto infoButton = std::make_unique<InfoButton> (propertyTooltip);
infoButton->setAssociatedComponent (prop.get());
auto propertyAndInfoWrapper = std::make_unique<PropertyAndInfoWrapper> (*prop, *infoButton.get());
addAndMakeVisible (propertyAndInfoWrapper.get());
propertyComponentsWithInfo.push_back (std::move (propertyAndInfoWrapper));
infoButtons.push_back (std::move (infoButton));
}
else
{
addAndMakeVisible (prop.get());
}
if (auto* multiChoice = dynamic_cast<MultiChoicePropertyComponent*> (prop.get()))
multiChoice->onHeightChange = [this] { updateSize(); };
if (auto* text = dynamic_cast<TextPropertyComponent*> (prop.get()))
if (text->isTextEditorMultiLine())
text->addListener (this);
}
}
int updateSize (int x, int y, int width)
{
header.setBounds (0, 0, width, headerSize);
auto height = header.getBottom() + 10;
for (auto& pp : properties)
{
const auto propertyHeight = jmax (pp->getPreferredHeight(), getApproximateLabelHeight (*pp));
auto iter = std::find_if (propertyComponentsWithInfo.begin(), propertyComponentsWithInfo.end(),
[&pp] (const std::unique_ptr<PropertyAndInfoWrapper>& w) { return &w->propertyComponent == pp.get(); });
if (iter != propertyComponentsWithInfo.end())
(*iter)->setBounds (0, height, width - 10, propertyHeight);
else
pp->setBounds (40, height, width - 50, propertyHeight);
if (shouldResizePropertyComponent (pp.get()))
resizePropertyComponent (pp.get());
height += pp->getHeight() + 10;
}
height += 16;
setBounds (x, y, width, jmax (height, getParentHeight()));
return height;
}
void paint (Graphics& g) override
{
g.fillAll (findColour (secondaryBackgroundColourId));
}
const std::vector<std::unique_ptr<PropertyComponent>>& getProperties() const noexcept
{
return properties;
}
void clearProperties()
{
propertyComponentsWithInfo.clear();
infoButtons.clear();
properties.clear();
}
private:
//==============================================================================
struct PropertyAndInfoWrapper : public Component
{
PropertyAndInfoWrapper (PropertyComponent& c, InfoButton& i)
: propertyComponent (c),
infoButton (i)
{
setFocusContainerType (FocusContainerType::focusContainer);
setTitle (propertyComponent.getName());
addAndMakeVisible (propertyComponent);
addAndMakeVisible (infoButton);
}
void resized() override
{
auto bounds = getLocalBounds();
bounds.removeFromLeft (40);
bounds.removeFromRight (10);
propertyComponent.setBounds (bounds);
infoButton.setCentrePosition (20, bounds.getHeight() / 2);
}
PropertyComponent& propertyComponent;
InfoButton& infoButton;
};
//==============================================================================
void textPropertyComponentChanged (TextPropertyComponent* comp) override
{
auto fontHeight = [comp]
{
Label tmpLabel;
return comp->getLookAndFeel().getLabelFont (tmpLabel).getHeight();
}();
auto lines = StringArray::fromLines (comp->getText());
comp->setPreferredHeight (jmax (100, 10 + roundToInt (fontHeight * (float) lines.size())));
updateSize();
}
void updateSize()
{
updateSize (getX(), getY(), getWidth());
if (auto* parent = getParentComponent())
parent->parentSizeChanged();
}
bool shouldResizePropertyComponent (PropertyComponent* p)
{
if (auto* textComp = dynamic_cast<TextPropertyComponent*> (p))
return ! textComp->isTextEditorMultiLine();
return (dynamic_cast<ChoicePropertyComponent*> (p) != nullptr
|| dynamic_cast<ButtonPropertyComponent*> (p) != nullptr
|| dynamic_cast<BooleanPropertyComponent*> (p) != nullptr);
}
void resizePropertyComponent (PropertyComponent* pp)
{
for (auto i = pp->getNumChildComponents() - 1; i >= 0; --i)
{
auto* child = pp->getChildComponent (i);
auto bounds = child->getBounds();
child->setBounds (bounds.withSizeKeepingCentre (child->getWidth(), pp->getPreferredHeight()));
}
}
static int getApproximateLabelHeight (const PropertyComponent& pp)
{
auto availableTextWidth = ProjucerLookAndFeel::getTextWidthForPropertyComponent (pp);
if (availableTextWidth == 0)
return 0;
const auto font = ProjucerLookAndFeel::getPropertyComponentFont();
const auto labelWidth = font.getStringWidthFloat (pp.getName());
const auto numLines = (int) (labelWidth / (float) availableTextWidth) + 1;
return (int) std::round ((float) numLines * font.getHeight() * 1.1f);
}
//==============================================================================
static constexpr int headerSize = 40;
std::vector<std::unique_ptr<PropertyComponent>> properties;
std::vector<std::unique_ptr<InfoButton>> infoButtons;
std::vector<std::unique_ptr<PropertyAndInfoWrapper>> propertyComponentsWithInfo;
ContentViewHeader header;
String description;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyGroupComponent)
};

View File

@ -1,363 +1,363 @@
/*
==============================================================================
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
//==============================================================================
class FileGroupInformationComponent : public Component,
private ListBoxModel,
private ValueTree::Listener
{
public:
FileGroupInformationComponent (const Project::Item& group)
: item (group),
header (item.getName(), { getIcons().openFolder, Colours::transparentBlack })
{
list.setHeaderComponent (std::make_unique<ListBoxHeader> (Array<String> { "File", "Binary Resource", "Xcode Resource", "Compile", "Skip PCH", "Compiler Flag Scheme" },
Array<float> { 0.25f, 0.125f, 0.125f, 0.125f, 0.125f, 0.25f }));
list.setModel (this);
list.setColour (ListBox::backgroundColourId, Colours::transparentBlack);
addAndMakeVisible (list);
list.updateContent();
list.setRowHeight (30);
item.state.addListener (this);
lookAndFeelChanged();
addAndMakeVisible (header);
}
~FileGroupInformationComponent() override
{
item.state.removeListener (this);
}
//==============================================================================
void paint (Graphics& g) override
{
g.setColour (findColour (secondaryBackgroundColourId));
g.fillRect (getLocalBounds().reduced (12, 0));
}
void resized() override
{
auto bounds = getLocalBounds().reduced (12, 0);
header.setBounds (bounds.removeFromTop (40));
list.setBounds (bounds.reduced (10, 4));
}
void parentSizeChanged() override
{
setSize (jmax (550, getParentWidth()), getParentHeight());
}
int getNumRows() override
{
return item.getNumChildren();
}
void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool /*rowIsSelected*/) override
{
g.setColour (findColour (rowNumber % 2 == 0 ? widgetBackgroundColourId
: secondaryWidgetBackgroundColourId));
g.fillRect (0, 0, width, height - 1);
}
Component* refreshComponentForRow (int rowNumber, bool /*isRowSelected*/, Component* existingComponentToUpdate) override
{
std::unique_ptr<Component> existing (existingComponentToUpdate);
if (rowNumber < getNumRows())
{
auto child = item.getChild (rowNumber);
if (existingComponentToUpdate == nullptr
|| dynamic_cast<FileOptionComponent*> (existing.get())->item != child)
{
existing.reset();
existing.reset (new FileOptionComponent (child, dynamic_cast<ListBoxHeader*> (list.getHeaderComponent())));
}
}
return existing.release();
}
String getGroupPath() const { return item.getFile().getFullPathName(); }
//==============================================================================
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); }
void valueTreeChildAdded (ValueTree&, ValueTree&) override { itemChanged(); }
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { itemChanged(); }
void valueTreeChildOrderChanged (ValueTree&, int, int) override { itemChanged(); }
void valueTreeParentChanged (ValueTree&) override { itemChanged(); }
private:
Project::Item item;
ListBox list;
ContentViewHeader header;
void itemChanged()
{
list.updateContent();
repaint();
}
//==============================================================================
class FileOptionComponent : public Component
{
public:
FileOptionComponent (const Project::Item& fileItem, ListBoxHeader* listBoxHeader)
: item (fileItem),
header (listBoxHeader),
compilerFlagSchemeSelector (item)
{
if (item.isFile())
{
auto isSourceFile = item.isSourceFile();
if (isSourceFile)
{
addAndMakeVisible (compileButton);
compileButton.getToggleStateValue().referTo (item.getShouldCompileValue());
compileButton.onStateChange = [this] { compileEnablementChanged(); };
}
addAndMakeVisible (binaryResourceButton);
binaryResourceButton.getToggleStateValue().referTo (item.getShouldAddToBinaryResourcesValue());
addAndMakeVisible (xcodeResourceButton);
xcodeResourceButton.getToggleStateValue().referTo (item.getShouldAddToXcodeResourcesValue());
if (isSourceFile)
{
addChildComponent (skipPCHButton);
skipPCHButton.getToggleStateValue().referTo (item.getShouldSkipPCHValue());
addChildComponent (compilerFlagSchemeSelector);
compileEnablementChanged();
}
}
}
void paint (Graphics& g) override
{
if (header != nullptr)
{
auto textBounds = getLocalBounds().removeFromLeft (roundToInt (header->getProportionAtIndex (0) * (float) getWidth()));
auto iconBounds = textBounds.removeFromLeft (25);
if (item.isImageFile())
iconBounds.reduce (5, 5);
item.getIcon().withColour (findColour (treeIconColourId)).draw (g, iconBounds.toFloat(), item.isIconCrossedOut());
g.setColour (findColour (widgetTextColourId));
g.drawText (item.getName(), textBounds, Justification::centredLeft);
}
}
void resized() override
{
if (header != nullptr)
{
auto bounds = getLocalBounds();
auto width = (float) getWidth();
bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (0) * width));
binaryResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (1) * width)));
xcodeResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (2) * width)));
compileButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (3) * width)));
skipPCHButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (4) * width)));
compilerFlagSchemeSelector.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (5) * width)));
}
}
Project::Item item;
private:
//==============================================================================
class CompilerFlagSchemeSelector : public Component,
private Value::Listener
{
public:
CompilerFlagSchemeSelector (Project::Item& it)
: item (it)
{
schemeBox.setTextWhenNothingSelected ("None");
updateCompilerFlagSchemeComboBox();
schemeBox.onChange = [this] { handleComboBoxSelection(); };
addAndMakeVisible (schemeBox);
addChildComponent (newSchemeLabel);
newSchemeLabel.setEditable (true);
newSchemeLabel.setJustificationType (Justification::centredLeft);
newSchemeLabel.onEditorHide = [this]
{
newSchemeLabel.setVisible (false);
schemeBox.setVisible (true);
auto newScheme = newSchemeLabel.getText();
item.project.addCompilerFlagScheme (newScheme);
if (item.getCompilerFlagSchemeString().isEmpty())
item.setCompilerFlagScheme (newScheme);
updateCompilerFlagSchemeComboBox();
};
selectScheme (item.getCompilerFlagSchemeString());
projectCompilerFlagSchemesValue = item.project.getProjectValue (Ids::compilerFlagSchemes);
projectCompilerFlagSchemesValue.addListener (this);
lookAndFeelChanged();
}
void resized() override
{
auto b = getLocalBounds();
schemeBox.setBounds (b);
newSchemeLabel.setBounds (b);
}
private:
void valueChanged (Value&) override { updateCompilerFlagSchemeComboBox(); }
void lookAndFeelChanged() override
{
schemeBox.setColour (ComboBox::outlineColourId, Colours::transparentBlack);
schemeBox.setColour (ComboBox::textColourId, findColour (defaultTextColourId));
}
void updateCompilerFlagSchemeComboBox()
{
auto itemScheme = item.getCompilerFlagSchemeString();
auto allSchemes = item.project.getCompilerFlagSchemes();
if (itemScheme.isNotEmpty() && ! allSchemes.contains (itemScheme))
{
item.clearCurrentCompilerFlagScheme();
itemScheme = {};
}
schemeBox.clear();
schemeBox.addItemList (allSchemes, 1);
schemeBox.addSeparator();
schemeBox.addItem ("Add a new scheme...", -1);
schemeBox.addItem ("Delete selected scheme", -2);
schemeBox.addItem ("Clear", -3);
selectScheme (itemScheme);
}
void handleComboBoxSelection()
{
auto selectedID = schemeBox.getSelectedId();
if (selectedID > 0)
{
item.setCompilerFlagScheme (schemeBox.getItemText (selectedID - 1));
}
else if (selectedID == -1)
{
newSchemeLabel.setText ("NewScheme", dontSendNotification);
schemeBox.setVisible (false);
newSchemeLabel.setVisible (true);
newSchemeLabel.showEditor();
if (auto* ed = newSchemeLabel.getCurrentTextEditor())
ed->setInputRestrictions (64, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_");
}
else if (selectedID == -2)
{
auto currentScheme = item.getCompilerFlagSchemeString();
if (currentScheme.isNotEmpty())
{
item.project.removeCompilerFlagScheme (currentScheme);
item.clearCurrentCompilerFlagScheme();
}
updateCompilerFlagSchemeComboBox();
}
else if (selectedID == -3)
{
schemeBox.setSelectedId (0);
item.clearCurrentCompilerFlagScheme();
}
}
void selectScheme (const String& schemeToSelect)
{
if (schemeToSelect.isNotEmpty())
{
for (int i = 0; i < schemeBox.getNumItems(); ++i)
{
if (schemeBox.getItemText (i) == schemeToSelect)
{
schemeBox.setSelectedItemIndex (i);
return;
}
}
}
schemeBox.setSelectedId (0);
}
Project::Item& item;
Value projectCompilerFlagSchemesValue;
ComboBox schemeBox;
Label newSchemeLabel;
};
void compileEnablementChanged()
{
auto shouldBeCompiled = compileButton.getToggleState();
skipPCHButton.setVisible (shouldBeCompiled);
compilerFlagSchemeSelector.setVisible (shouldBeCompiled);
}
//==============================================================================
ListBoxHeader* header;
ToggleButton compileButton, binaryResourceButton, xcodeResourceButton, skipPCHButton;
CompilerFlagSchemeSelector compilerFlagSchemeSelector;
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileGroupInformationComponent)
};
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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
//==============================================================================
class FileGroupInformationComponent : public Component,
private ListBoxModel,
private ValueTree::Listener
{
public:
FileGroupInformationComponent (const Project::Item& group)
: item (group),
header (item.getName(), { getIcons().openFolder, Colours::transparentBlack })
{
list.setHeaderComponent (std::make_unique<ListBoxHeader> (Array<String> { "File", "Binary Resource", "Xcode Resource", "Compile", "Skip PCH", "Compiler Flag Scheme" },
Array<float> { 0.25f, 0.125f, 0.125f, 0.125f, 0.125f, 0.25f }));
list.setModel (this);
list.setColour (ListBox::backgroundColourId, Colours::transparentBlack);
addAndMakeVisible (list);
list.updateContent();
list.setRowHeight (30);
item.state.addListener (this);
lookAndFeelChanged();
addAndMakeVisible (header);
}
~FileGroupInformationComponent() override
{
item.state.removeListener (this);
}
//==============================================================================
void paint (Graphics& g) override
{
g.setColour (findColour (secondaryBackgroundColourId));
g.fillRect (getLocalBounds().reduced (12, 0));
}
void resized() override
{
auto bounds = getLocalBounds().reduced (12, 0);
header.setBounds (bounds.removeFromTop (40));
list.setBounds (bounds.reduced (10, 4));
}
void parentSizeChanged() override
{
setSize (jmax (550, getParentWidth()), getParentHeight());
}
int getNumRows() override
{
return item.getNumChildren();
}
void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool /*rowIsSelected*/) override
{
g.setColour (findColour (rowNumber % 2 == 0 ? widgetBackgroundColourId
: secondaryWidgetBackgroundColourId));
g.fillRect (0, 0, width, height - 1);
}
Component* refreshComponentForRow (int rowNumber, bool /*isRowSelected*/, Component* existingComponentToUpdate) override
{
std::unique_ptr<Component> existing (existingComponentToUpdate);
if (rowNumber < getNumRows())
{
auto child = item.getChild (rowNumber);
if (existingComponentToUpdate == nullptr
|| dynamic_cast<FileOptionComponent*> (existing.get())->item != child)
{
existing.reset();
existing.reset (new FileOptionComponent (child, dynamic_cast<ListBoxHeader*> (list.getHeaderComponent())));
}
}
return existing.release();
}
String getGroupPath() const { return item.getFile().getFullPathName(); }
//==============================================================================
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); }
void valueTreeChildAdded (ValueTree&, ValueTree&) override { itemChanged(); }
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { itemChanged(); }
void valueTreeChildOrderChanged (ValueTree&, int, int) override { itemChanged(); }
void valueTreeParentChanged (ValueTree&) override { itemChanged(); }
private:
Project::Item item;
ListBox list;
ContentViewHeader header;
void itemChanged()
{
list.updateContent();
repaint();
}
//==============================================================================
class FileOptionComponent : public Component
{
public:
FileOptionComponent (const Project::Item& fileItem, ListBoxHeader* listBoxHeader)
: item (fileItem),
header (listBoxHeader),
compilerFlagSchemeSelector (item)
{
if (item.isFile())
{
auto isSourceFile = item.isSourceFile();
if (isSourceFile)
{
addAndMakeVisible (compileButton);
compileButton.getToggleStateValue().referTo (item.getShouldCompileValue());
compileButton.onStateChange = [this] { compileEnablementChanged(); };
}
addAndMakeVisible (binaryResourceButton);
binaryResourceButton.getToggleStateValue().referTo (item.getShouldAddToBinaryResourcesValue());
addAndMakeVisible (xcodeResourceButton);
xcodeResourceButton.getToggleStateValue().referTo (item.getShouldAddToXcodeResourcesValue());
if (isSourceFile)
{
addChildComponent (skipPCHButton);
skipPCHButton.getToggleStateValue().referTo (item.getShouldSkipPCHValue());
addChildComponent (compilerFlagSchemeSelector);
compileEnablementChanged();
}
}
}
void paint (Graphics& g) override
{
if (header != nullptr)
{
auto textBounds = getLocalBounds().removeFromLeft (roundToInt (header->getProportionAtIndex (0) * (float) getWidth()));
auto iconBounds = textBounds.removeFromLeft (25);
if (item.isImageFile())
iconBounds.reduce (5, 5);
item.getIcon().withColour (findColour (treeIconColourId)).draw (g, iconBounds.toFloat(), item.isIconCrossedOut());
g.setColour (findColour (widgetTextColourId));
g.drawText (item.getName(), textBounds, Justification::centredLeft);
}
}
void resized() override
{
if (header != nullptr)
{
auto bounds = getLocalBounds();
auto width = (float) getWidth();
bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (0) * width));
binaryResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (1) * width)));
xcodeResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (2) * width)));
compileButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (3) * width)));
skipPCHButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (4) * width)));
compilerFlagSchemeSelector.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (5) * width)));
}
}
Project::Item item;
private:
//==============================================================================
class CompilerFlagSchemeSelector : public Component,
private Value::Listener
{
public:
CompilerFlagSchemeSelector (Project::Item& it)
: item (it)
{
schemeBox.setTextWhenNothingSelected ("None");
updateCompilerFlagSchemeComboBox();
schemeBox.onChange = [this] { handleComboBoxSelection(); };
addAndMakeVisible (schemeBox);
addChildComponent (newSchemeLabel);
newSchemeLabel.setEditable (true);
newSchemeLabel.setJustificationType (Justification::centredLeft);
newSchemeLabel.onEditorHide = [this]
{
newSchemeLabel.setVisible (false);
schemeBox.setVisible (true);
auto newScheme = newSchemeLabel.getText();
item.project.addCompilerFlagScheme (newScheme);
if (item.getCompilerFlagSchemeString().isEmpty())
item.setCompilerFlagScheme (newScheme);
updateCompilerFlagSchemeComboBox();
};
selectScheme (item.getCompilerFlagSchemeString());
projectCompilerFlagSchemesValue = item.project.getProjectValue (Ids::compilerFlagSchemes);
projectCompilerFlagSchemesValue.addListener (this);
lookAndFeelChanged();
}
void resized() override
{
auto b = getLocalBounds();
schemeBox.setBounds (b);
newSchemeLabel.setBounds (b);
}
private:
void valueChanged (Value&) override { updateCompilerFlagSchemeComboBox(); }
void lookAndFeelChanged() override
{
schemeBox.setColour (ComboBox::outlineColourId, Colours::transparentBlack);
schemeBox.setColour (ComboBox::textColourId, findColour (defaultTextColourId));
}
void updateCompilerFlagSchemeComboBox()
{
auto itemScheme = item.getCompilerFlagSchemeString();
auto allSchemes = item.project.getCompilerFlagSchemes();
if (itemScheme.isNotEmpty() && ! allSchemes.contains (itemScheme))
{
item.clearCurrentCompilerFlagScheme();
itemScheme = {};
}
schemeBox.clear();
schemeBox.addItemList (allSchemes, 1);
schemeBox.addSeparator();
schemeBox.addItem ("Add a new scheme...", -1);
schemeBox.addItem ("Delete selected scheme", -2);
schemeBox.addItem ("Clear", -3);
selectScheme (itemScheme);
}
void handleComboBoxSelection()
{
auto selectedID = schemeBox.getSelectedId();
if (selectedID > 0)
{
item.setCompilerFlagScheme (schemeBox.getItemText (selectedID - 1));
}
else if (selectedID == -1)
{
newSchemeLabel.setText ("NewScheme", dontSendNotification);
schemeBox.setVisible (false);
newSchemeLabel.setVisible (true);
newSchemeLabel.showEditor();
if (auto* ed = newSchemeLabel.getCurrentTextEditor())
ed->setInputRestrictions (64, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_");
}
else if (selectedID == -2)
{
auto currentScheme = item.getCompilerFlagSchemeString();
if (currentScheme.isNotEmpty())
{
item.project.removeCompilerFlagScheme (currentScheme);
item.clearCurrentCompilerFlagScheme();
}
updateCompilerFlagSchemeComboBox();
}
else if (selectedID == -3)
{
schemeBox.setSelectedId (0);
item.clearCurrentCompilerFlagScheme();
}
}
void selectScheme (const String& schemeToSelect)
{
if (schemeToSelect.isNotEmpty())
{
for (int i = 0; i < schemeBox.getNumItems(); ++i)
{
if (schemeBox.getItemText (i) == schemeToSelect)
{
schemeBox.setSelectedItemIndex (i);
return;
}
}
}
schemeBox.setSelectedId (0);
}
Project::Item& item;
Value projectCompilerFlagSchemesValue;
ComboBox schemeBox;
Label newSchemeLabel;
};
void compileEnablementChanged()
{
auto shouldBeCompiled = compileButton.getToggleState();
skipPCHButton.setVisible (shouldBeCompiled);
compilerFlagSchemeSelector.setVisible (shouldBeCompiled);
}
//==============================================================================
ListBoxHeader* header;
ToggleButton compileButton, binaryResourceButton, xcodeResourceButton, skipPCHButton;
CompilerFlagSchemeSelector compilerFlagSchemeSelector;
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileGroupInformationComponent)
};

View File

@ -1,279 +1,279 @@
/*
==============================================================================
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.
==============================================================================
*/
#include "jucer_HeaderComponent.h"
#include "../../Application/jucer_Application.h"
#include "../../ProjectSaving/jucer_ProjectExporter.h"
#include "../../Project/UI/jucer_ProjectContentComponent.h"
//==============================================================================
HeaderComponent::HeaderComponent (ProjectContentComponent* pcc)
: projectContentComponent (pcc)
{
setTitle ("Header");
setFocusContainerType (FocusContainerType::focusContainer);
addAndMakeVisible (configLabel);
addAndMakeVisible (exporterBox);
exporterBox.onChange = [this] { updateExporterButton(); };
juceIcon.setImage (ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize), RectanglePlacement::centred);
addAndMakeVisible (juceIcon);
addAndMakeVisible (userAvatar);
userAvatar.addChangeListener (this);
projectNameLabel.setText ({}, dontSendNotification);
addAndMakeVisible (projectNameLabel);
initialiseButtons();
}
//==============================================================================
void HeaderComponent::resized()
{
auto bounds = getLocalBounds();
configLabel.setFont ({ (float) bounds.getHeight() / 3.0f });
{
auto headerBounds = bounds.removeFromLeft (tabsWidth);
const int buttonSize = 25;
auto buttonBounds = headerBounds.removeFromRight (buttonSize);
projectSettingsButton.setBounds (buttonBounds.removeFromBottom (buttonSize).reduced (2));
juceIcon.setBounds (headerBounds.removeFromLeft (headerBounds.getHeight()).reduced (2));
headerBounds.removeFromRight (5);
projectNameLabel.setBounds (headerBounds);
}
{
auto exporterWidth = jmin (400, bounds.getWidth() / 2);
Rectangle<int> exporterBounds (0, 0, exporterWidth, bounds.getHeight());
exporterBounds.setCentre (bounds.getCentre());
saveAndOpenInIDEButton.setBounds (exporterBounds.removeFromRight (exporterBounds.getHeight()).reduced (2));
exporterBounds.removeFromRight (5);
exporterBox.setBounds (exporterBounds.removeFromBottom (roundToInt ((float) exporterBounds.getHeight() / 1.8f)));
configLabel.setBounds (exporterBounds);
}
userAvatar.setBounds (bounds.removeFromRight (userAvatar.isDisplaingGPLLogo() ? roundToInt ((float) bounds.getHeight() * 1.9f)
: bounds.getHeight()).reduced (2));
}
void HeaderComponent::paint (Graphics& g)
{
g.fillAll (findColour (backgroundColourId));
}
//==============================================================================
void HeaderComponent::setCurrentProject (Project* newProject)
{
stopTimer();
repaint();
projectNameLabel.setText ({}, dontSendNotification);
project = newProject;
if (project != nullptr)
{
exportersTree = project->getExporters();
exportersTree.addListener (this);
updateExporters();
projectNameValue.referTo (project->getProjectValue (Ids::name));
projectNameValue.addListener (this);
updateName();
}
}
//==============================================================================
void HeaderComponent::updateExporters()
{
auto selectedExporter = getSelectedExporter();
exporterBox.clear();
auto preferredExporterIndex = -1;
int i = 0;
for (Project::ExporterIterator exporter (*project); exporter.next(); ++i)
{
auto exporterName = exporter->getUniqueName();
exporterBox.addItem (exporterName, i + 1);
if (selectedExporter != nullptr && exporterName == selectedExporter->getUniqueName())
exporterBox.setSelectedId (i + 1);
if (exporterName.contains (ProjectExporter::getCurrentPlatformExporterTypeInfo().displayName) && preferredExporterIndex == -1)
preferredExporterIndex = i;
}
if (exporterBox.getSelectedItemIndex() == -1)
{
if (preferredExporterIndex == -1)
{
i = 0;
for (Project::ExporterIterator exporter (*project); exporter.next(); ++i)
{
if (exporter->canLaunchProject())
{
preferredExporterIndex = i;
break;
}
}
}
exporterBox.setSelectedItemIndex (preferredExporterIndex != -1 ? preferredExporterIndex : 0);
}
updateExporterButton();
}
std::unique_ptr<ProjectExporter> HeaderComponent::getSelectedExporter() const
{
if (project != nullptr)
{
int i = 0;
auto selectedIndex = exporterBox.getSelectedItemIndex();
for (Project::ExporterIterator exporter (*project); exporter.next();)
if (i++ == selectedIndex)
return std::move (exporter.exporter);
}
return nullptr;
}
bool HeaderComponent::canCurrentExporterLaunchProject() const
{
if (project != nullptr)
{
if (auto selectedExporter = getSelectedExporter())
{
for (Project::ExporterIterator exporter (*project); exporter.next();)
if (exporter->canLaunchProject() && exporter->getUniqueName() == selectedExporter->getUniqueName())
return true;
}
}
return false;
}
//==============================================================================
void HeaderComponent::sidebarTabsWidthChanged (int newWidth)
{
tabsWidth = newWidth;
resized();
}
//==============================================================================
void HeaderComponent::changeListenerCallback (ChangeBroadcaster* source)
{
if (source == &userAvatar)
resized();
}
void HeaderComponent::valueChanged (Value&)
{
updateName();
}
void HeaderComponent::timerCallback()
{
repaint();
}
//==============================================================================
void HeaderComponent::initialiseButtons()
{
addAndMakeVisible (projectSettingsButton);
projectSettingsButton.onClick = [this] { projectContentComponent->showProjectSettings(); };
addAndMakeVisible (saveAndOpenInIDEButton);
saveAndOpenInIDEButton.setBackgroundColour (Colours::white);
saveAndOpenInIDEButton.setIconInset (7);
saveAndOpenInIDEButton.onClick = [this]
{
if (project == nullptr)
return;
if (! project->isSaveAndExportDisabled())
{
projectContentComponent->openInSelectedIDE (true);
return;
}
auto setWarningVisible = [this] (const Identifier& identifier)
{
auto child = project->getProjectMessages().getChildWithName (ProjectMessages::Ids::warning)
.getChildWithName (identifier);
if (child.isValid())
child.setProperty (ProjectMessages::Ids::isVisible, true, nullptr);
};
if (project->hasIncompatibleLicenseTypeAndSplashScreenSetting())
setWarningVisible (ProjectMessages::Ids::incompatibleLicense);
if (project->isFileModificationCheckPending())
setWarningVisible (ProjectMessages::Ids::jucerFileModified);
};
updateExporterButton();
}
void HeaderComponent::updateName()
{
if (project != nullptr)
projectNameLabel.setText (project->getDocumentTitle(), dontSendNotification);
}
void HeaderComponent::updateExporterButton()
{
if (auto selectedExporter = getSelectedExporter())
{
auto selectedName = selectedExporter->getUniqueName();
for (auto info : ProjectExporter::getExporterTypeInfos())
{
if (selectedName.contains (info.displayName))
{
saveAndOpenInIDEButton.setImage (info.icon);
saveAndOpenInIDEButton.repaint();
saveAndOpenInIDEButton.setEnabled (canCurrentExporterLaunchProject());
}
}
}
}
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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.
==============================================================================
*/
#include "jucer_HeaderComponent.h"
#include "../../Application/jucer_Application.h"
#include "../../ProjectSaving/jucer_ProjectExporter.h"
#include "../../Project/UI/jucer_ProjectContentComponent.h"
//==============================================================================
HeaderComponent::HeaderComponent (ProjectContentComponent* pcc)
: projectContentComponent (pcc)
{
setTitle ("Header");
setFocusContainerType (FocusContainerType::focusContainer);
addAndMakeVisible (configLabel);
addAndMakeVisible (exporterBox);
exporterBox.onChange = [this] { updateExporterButton(); };
juceIcon.setImage (ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize), RectanglePlacement::centred);
addAndMakeVisible (juceIcon);
addAndMakeVisible (userAvatar);
userAvatar.addChangeListener (this);
projectNameLabel.setText ({}, dontSendNotification);
addAndMakeVisible (projectNameLabel);
initialiseButtons();
}
//==============================================================================
void HeaderComponent::resized()
{
auto bounds = getLocalBounds();
configLabel.setFont ({ (float) bounds.getHeight() / 3.0f });
{
auto headerBounds = bounds.removeFromLeft (tabsWidth);
const int buttonSize = 25;
auto buttonBounds = headerBounds.removeFromRight (buttonSize);
projectSettingsButton.setBounds (buttonBounds.removeFromBottom (buttonSize).reduced (2));
juceIcon.setBounds (headerBounds.removeFromLeft (headerBounds.getHeight()).reduced (2));
headerBounds.removeFromRight (5);
projectNameLabel.setBounds (headerBounds);
}
{
auto exporterWidth = jmin (400, bounds.getWidth() / 2);
Rectangle<int> exporterBounds (0, 0, exporterWidth, bounds.getHeight());
exporterBounds.setCentre (bounds.getCentre());
saveAndOpenInIDEButton.setBounds (exporterBounds.removeFromRight (exporterBounds.getHeight()).reduced (2));
exporterBounds.removeFromRight (5);
exporterBox.setBounds (exporterBounds.removeFromBottom (roundToInt ((float) exporterBounds.getHeight() / 1.8f)));
configLabel.setBounds (exporterBounds);
}
userAvatar.setBounds (bounds.removeFromRight (userAvatar.isDisplaingGPLLogo() ? roundToInt ((float) bounds.getHeight() * 1.9f)
: bounds.getHeight()).reduced (2));
}
void HeaderComponent::paint (Graphics& g)
{
g.fillAll (findColour (backgroundColourId));
}
//==============================================================================
void HeaderComponent::setCurrentProject (Project* newProject)
{
stopTimer();
repaint();
projectNameLabel.setText ({}, dontSendNotification);
project = newProject;
if (project != nullptr)
{
exportersTree = project->getExporters();
exportersTree.addListener (this);
updateExporters();
projectNameValue.referTo (project->getProjectValue (Ids::name));
projectNameValue.addListener (this);
updateName();
}
}
//==============================================================================
void HeaderComponent::updateExporters()
{
auto selectedExporter = getSelectedExporter();
exporterBox.clear();
auto preferredExporterIndex = -1;
int i = 0;
for (Project::ExporterIterator exporter (*project); exporter.next(); ++i)
{
auto exporterName = exporter->getUniqueName();
exporterBox.addItem (exporterName, i + 1);
if (selectedExporter != nullptr && exporterName == selectedExporter->getUniqueName())
exporterBox.setSelectedId (i + 1);
if (exporterName.contains (ProjectExporter::getCurrentPlatformExporterTypeInfo().displayName) && preferredExporterIndex == -1)
preferredExporterIndex = i;
}
if (exporterBox.getSelectedItemIndex() == -1)
{
if (preferredExporterIndex == -1)
{
i = 0;
for (Project::ExporterIterator exporter (*project); exporter.next(); ++i)
{
if (exporter->canLaunchProject())
{
preferredExporterIndex = i;
break;
}
}
}
exporterBox.setSelectedItemIndex (preferredExporterIndex != -1 ? preferredExporterIndex : 0);
}
updateExporterButton();
}
std::unique_ptr<ProjectExporter> HeaderComponent::getSelectedExporter() const
{
if (project != nullptr)
{
int i = 0;
auto selectedIndex = exporterBox.getSelectedItemIndex();
for (Project::ExporterIterator exporter (*project); exporter.next();)
if (i++ == selectedIndex)
return std::move (exporter.exporter);
}
return nullptr;
}
bool HeaderComponent::canCurrentExporterLaunchProject() const
{
if (project != nullptr)
{
if (auto selectedExporter = getSelectedExporter())
{
for (Project::ExporterIterator exporter (*project); exporter.next();)
if (exporter->canLaunchProject() && exporter->getUniqueName() == selectedExporter->getUniqueName())
return true;
}
}
return false;
}
//==============================================================================
void HeaderComponent::sidebarTabsWidthChanged (int newWidth)
{
tabsWidth = newWidth;
resized();
}
//==============================================================================
void HeaderComponent::changeListenerCallback (ChangeBroadcaster* source)
{
if (source == &userAvatar)
resized();
}
void HeaderComponent::valueChanged (Value&)
{
updateName();
}
void HeaderComponent::timerCallback()
{
repaint();
}
//==============================================================================
void HeaderComponent::initialiseButtons()
{
addAndMakeVisible (projectSettingsButton);
projectSettingsButton.onClick = [this] { projectContentComponent->showProjectSettings(); };
addAndMakeVisible (saveAndOpenInIDEButton);
saveAndOpenInIDEButton.setBackgroundColour (Colours::white);
saveAndOpenInIDEButton.setIconInset (7);
saveAndOpenInIDEButton.onClick = [this]
{
if (project == nullptr)
return;
if (! project->isSaveAndExportDisabled())
{
projectContentComponent->openInSelectedIDE (true);
return;
}
auto setWarningVisible = [this] (const Identifier& identifier)
{
auto child = project->getProjectMessages().getChildWithName (ProjectMessages::Ids::warning)
.getChildWithName (identifier);
if (child.isValid())
child.setProperty (ProjectMessages::Ids::isVisible, true, nullptr);
};
if (project->hasIncompatibleLicenseTypeAndSplashScreenSetting())
setWarningVisible (ProjectMessages::Ids::incompatibleLicense);
if (project->isFileModificationCheckPending())
setWarningVisible (ProjectMessages::Ids::jucerFileModified);
};
updateExporterButton();
}
void HeaderComponent::updateName()
{
if (project != nullptr)
projectNameLabel.setText (project->getDocumentTitle(), dontSendNotification);
}
void HeaderComponent::updateExporterButton()
{
if (auto selectedExporter = getSelectedExporter())
{
auto selectedName = selectedExporter->getUniqueName();
for (auto info : ProjectExporter::getExporterTypeInfos())
{
if (selectedName.contains (info.displayName))
{
saveAndOpenInIDEButton.setImage (info.icon);
saveAndOpenInIDEButton.repaint();
saveAndOpenInIDEButton.setEnabled (canCurrentExporterLaunchProject());
}
}
}
}

View File

@ -1,101 +1,101 @@
/*
==============================================================================
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 "../../Application/jucer_Headers.h"
#include "../../Utility/UI/jucer_IconButton.h"
#include "jucer_UserAvatarComponent.h"
class Project;
class ProjectContentComponent;
class ProjectExporter;
//==============================================================================
class HeaderComponent : public Component,
private ValueTree::Listener,
private ChangeListener,
private Value::Listener,
private Timer
{
public:
HeaderComponent (ProjectContentComponent* projectContentComponent);
//==============================================================================
void resized() override;
void paint (Graphics&) override;
//==============================================================================
void setCurrentProject (Project*);
void updateExporters();
std::unique_ptr<ProjectExporter> getSelectedExporter() const;
bool canCurrentExporterLaunchProject() const;
void sidebarTabsWidthChanged (int newWidth);
private:
//==============================================================================
void changeListenerCallback (ChangeBroadcaster* source) override;
void valueChanged (Value&) override;
void timerCallback() override;
//==============================================================================
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { updateIfNeeded (parentTree); }
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { updateIfNeeded (parentTree); }
void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { updateIfNeeded (parentTree); }
void updateIfNeeded (ValueTree tree)
{
if (tree == exportersTree)
updateExporters();
}
//==============================================================================
void initialiseButtons();
void updateName();
void updateExporterButton();
//==============================================================================
int tabsWidth = 200;
ProjectContentComponent* projectContentComponent = nullptr;
Project* project = nullptr;
ValueTree exportersTree;
Value projectNameValue;
ComboBox exporterBox;
Label configLabel { "Config Label", "Selected exporter" }, projectNameLabel;
ImageComponent juceIcon;
UserAvatarComponent userAvatar { true };
IconButton projectSettingsButton { "Project Settings", getIcons().settings },
saveAndOpenInIDEButton { "Save and Open in IDE", Image() };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HeaderComponent)
};
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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 "../../Application/jucer_Headers.h"
#include "../../Utility/UI/jucer_IconButton.h"
#include "jucer_UserAvatarComponent.h"
class Project;
class ProjectContentComponent;
class ProjectExporter;
//==============================================================================
class HeaderComponent : public Component,
private ValueTree::Listener,
private ChangeListener,
private Value::Listener,
private Timer
{
public:
HeaderComponent (ProjectContentComponent* projectContentComponent);
//==============================================================================
void resized() override;
void paint (Graphics&) override;
//==============================================================================
void setCurrentProject (Project*);
void updateExporters();
std::unique_ptr<ProjectExporter> getSelectedExporter() const;
bool canCurrentExporterLaunchProject() const;
void sidebarTabsWidthChanged (int newWidth);
private:
//==============================================================================
void changeListenerCallback (ChangeBroadcaster* source) override;
void valueChanged (Value&) override;
void timerCallback() override;
//==============================================================================
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { updateIfNeeded (parentTree); }
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { updateIfNeeded (parentTree); }
void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { updateIfNeeded (parentTree); }
void updateIfNeeded (ValueTree tree)
{
if (tree == exportersTree)
updateExporters();
}
//==============================================================================
void initialiseButtons();
void updateName();
void updateExporterButton();
//==============================================================================
int tabsWidth = 200;
ProjectContentComponent* projectContentComponent = nullptr;
Project* project = nullptr;
ValueTree exportersTree;
Value projectNameValue;
ComboBox exporterBox;
Label configLabel { "Config Label", "Selected exporter" }, projectNameLabel;
ImageComponent juceIcon;
UserAvatarComponent userAvatar { true };
IconButton projectSettingsButton { "Project Settings", getIcons().settings },
saveAndOpenInIDEButton { "Save and Open in IDE", Image() };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HeaderComponent)
};

View File

@ -1,341 +1,344 @@
/*
==============================================================================
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
//==============================================================================
class ModulesInformationComponent : public Component,
private ListBoxModel,
private ValueTree::Listener
{
public:
ModulesInformationComponent (Project& p)
: project (p),
modulesValueTree (project.getEnabledModules().getState())
{
auto tempHeader = std::make_unique<ListBoxHeader> (Array<String> { "Module", "Version", "Make Local Copy", "Paths" },
Array<float> { 0.25f, 0.2f, 0.2f, 0.35f });
listHeader = tempHeader.get();
list.setHeaderComponent (std::move (tempHeader));
list.setModel (this);
list.setColour (ListBox::backgroundColourId, Colours::transparentBlack);
addAndMakeVisible (list);
list.updateContent();
list.setRowHeight (30);
list.setMultipleSelectionEnabled (true);
addAndMakeVisible (header);
addAndMakeVisible (setCopyModeButton);
setCopyModeButton.setTriggeredOnMouseDown (true);
setCopyModeButton.onClick = [this] { showCopyModeMenu(); };
addAndMakeVisible (copyPathButton);
copyPathButton.setTriggeredOnMouseDown (true);
copyPathButton.onClick = [this] { showSetPathsMenu(); };
addAndMakeVisible (globalPathsButton);
globalPathsButton.onClick = [this] { showGlobalPathsMenu(); };
modulesValueTree.addListener (this);
lookAndFeelChanged();
}
void paint (Graphics& g) override
{
g.setColour (findColour (secondaryBackgroundColourId));
g.fillRect (getLocalBounds().reduced (12, 0));
}
void resized() override
{
auto bounds = getLocalBounds().reduced (12, 0);
header.setBounds (bounds.removeFromTop (40));
bounds.reduce (10, 0);
list.setBounds (bounds.removeFromTop (list.getRowPosition (getNumRows() - 1, true).getBottom() + 20));
if (bounds.getHeight() < 35)
{
parentSizeChanged();
}
else
{
auto buttonRow = bounds.removeFromTop (35);
setCopyModeButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3)));
buttonRow.removeFromLeft (8);
copyPathButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3)));
buttonRow.removeFromLeft (8);
globalPathsButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3)));
}
}
void parentSizeChanged() override
{
auto width = jmax (550, getParentWidth());
auto y = list.getRowPosition (getNumRows() - 1, true).getBottom() + 200;
y = jmax (getParentHeight(), y);
setSize (width, y);
}
int getNumRows() override
{
return project.getEnabledModules().getNumModules();
}
void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override
{
ignoreUnused (height);
Rectangle<int> bounds (0, 0, width, height);
g.setColour (rowIsSelected ? findColour (defaultHighlightColourId) : findColour (rowNumber % 2 == 0 ? widgetBackgroundColourId
: secondaryWidgetBackgroundColourId));
g.fillRect (bounds.withTrimmedBottom (1));
bounds.removeFromLeft (5);
g.setColour (rowIsSelected ? findColour (defaultHighlightedTextColourId) : findColour (widgetTextColourId));
//==============================================================================
auto moduleID = project.getEnabledModules().getModuleID (rowNumber);
g.drawFittedText (moduleID, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (0) * (float) width)), Justification::centredLeft, 1);
//==============================================================================
auto version = project.getEnabledModules().getModuleInfo (moduleID).getVersion();
if (version.isEmpty())
version = "?";
g.drawFittedText (version, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (1) * (float) width)), Justification::centredLeft, 1);
//==============================================================================
g.drawFittedText (String (project.getEnabledModules().shouldCopyModuleFilesLocally (moduleID) ? "Yes" : "No"),
bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (2) * (float) width)), Justification::centredLeft, 1);
//==============================================================================
String pathText;
if (project.getEnabledModules().shouldUseGlobalPath (moduleID))
{
pathText = "Global";
}
else
{
StringArray paths;
for (Project::ExporterIterator exporter (project); exporter.next();)
paths.addIfNotAlreadyThere (exporter->getPathForModuleString (moduleID).trim());
pathText = paths.joinIntoString (", ");
}
g.drawFittedText (pathText, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (3) * (float) width)), Justification::centredLeft, 1);
}
void listBoxItemDoubleClicked (int row, const MouseEvent&) override
{
auto moduleID = project.getEnabledModules().getModuleID (row);
if (moduleID.isNotEmpty())
if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>())
pcc->showModule (moduleID);
}
void deleteKeyPressed (int row) override
{
project.getEnabledModules().removeModule (project.getEnabledModules().getModuleID (row));
}
void lookAndFeelChanged() override
{
setCopyModeButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
copyPathButton.setColour (TextButton::buttonColourId, findColour (defaultButtonBackgroundColourId));
globalPathsButton.setColour (TextButton::buttonColourId, findColour (defaultButtonBackgroundColourId));
}
private:
enum
{
nameCol = 1,
versionCol,
copyCol,
pathCol
};
Project& project;
ValueTree modulesValueTree;
ContentViewHeader header { "Modules", { getIcons().modules, Colours::transparentBlack } };
ListBox list;
ListBoxHeader* listHeader;
TextButton setCopyModeButton { "Set copy-mode for all modules..." };
TextButton copyPathButton { "Set paths for all modules..." };
TextButton globalPathsButton { "Enable/disable global path for modules..." };
std::map<String, var> modulePathClipboard;
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); }
void valueTreeChildAdded (ValueTree&, ValueTree&) override { itemChanged(); }
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { itemChanged(); }
void valueTreeChildOrderChanged (ValueTree&, int, int) override { itemChanged(); }
void valueTreeParentChanged (ValueTree&) override { itemChanged(); }
void itemChanged()
{
list.updateContent();
resized();
repaint();
}
static void setLocalCopyModeForAllModules (Project& project, bool copyLocally)
{
auto& modules = project.getEnabledModules();
for (auto i = modules.getNumModules(); --i >= 0;)
modules.shouldCopyModuleFilesLocallyValue (modules.getModuleID (i)) = copyLocally;
}
void showCopyModeMenu()
{
PopupMenu m;
m.addItem (PopupMenu::Item ("Set all modules to copy locally")
.setAction ([&] { setLocalCopyModeForAllModules (project, true); }));
m.addItem (PopupMenu::Item ("Set all modules to not copy locally")
.setAction ([&] { setLocalCopyModeForAllModules (project, false); }));
m.showMenuAsync (PopupMenu::Options().withTargetComponent (setCopyModeButton));
}
static void setAllModulesToUseGlobalPaths (Project& project, bool useGlobal)
{
auto& modules = project.getEnabledModules();
for (auto moduleID : modules.getAllModules())
modules.shouldUseGlobalPathValue (moduleID) = useGlobal;
}
static void setSelectedModulesToUseGlobalPaths (Project& project, SparseSet<int> selected, bool useGlobal)
{
auto& modules = project.getEnabledModules();
for (int i = 0; i < selected.size(); ++i)
modules.shouldUseGlobalPathValue (modules.getModuleID (selected[i])) = useGlobal;
}
void showGlobalPathsMenu()
{
PopupMenu m;
m.addItem (PopupMenu::Item ("Set all modules to use global paths")
.setAction ([&] { setAllModulesToUseGlobalPaths (project, true); }));
m.addItem (PopupMenu::Item ("Set all modules to not use global paths")
.setAction ([&] { setAllModulesToUseGlobalPaths (project, false); }));
m.addItem (PopupMenu::Item ("Set selected modules to use global paths")
.setEnabled (list.getNumSelectedRows() > 0)
.setAction ([&] { setSelectedModulesToUseGlobalPaths (project, list.getSelectedRows(), true); }));
m.addItem (PopupMenu::Item ("Set selected modules to not use global paths")
.setEnabled (list.getNumSelectedRows() > 0)
.setAction ([&] { setSelectedModulesToUseGlobalPaths (project, list.getSelectedRows(), false); }));
m.showMenuAsync (PopupMenu::Options().withTargetComponent (globalPathsButton));
}
void showSetPathsMenu()
{
PopupMenu m;
auto moduleToCopy = project.getEnabledModules().getModuleID (list.getSelectedRow());
if (moduleToCopy.isNotEmpty())
{
m.addItem (PopupMenu::Item ("Copy the paths from the module '" + moduleToCopy + "' to all other modules")
.setAction ([this, moduleToCopy]
{
auto& modulesList = project.getEnabledModules();
for (Project::ExporterIterator exporter (project); exporter.next();)
{
for (int i = 0; i < modulesList.getNumModules(); ++i)
{
auto modID = modulesList.getModuleID (i);
if (modID != moduleToCopy)
exporter->getPathForModuleValue (modID) = exporter->getPathForModuleValue (moduleToCopy).get();
}
}
list.repaint();
}));
m.addItem (PopupMenu::Item ("Copy paths from selected module")
.setEnabled (list.getNumSelectedRows() == 1)
.setAction ([this, moduleToCopy]
{
modulePathClipboard.clear();
for (Project::ExporterIterator exporter (project); exporter.next();)
modulePathClipboard[exporter->getUniqueName()] = exporter->getPathForModuleValue (moduleToCopy).get();
list.repaint();
}));
m.addItem (PopupMenu::Item ("Paste paths to selected modules")
.setEnabled (! modulePathClipboard.empty())
.setAction ([this]
{
for (int selectionId = 0; selectionId < list.getNumSelectedRows(); ++selectionId)
{
auto rowNumber = list.getSelectedRow (selectionId);
auto modID = project.getEnabledModules().getModuleID (rowNumber);
for (Project::ExporterIterator exporter (project); exporter.next();)
exporter->getPathForModuleValue (modID) = modulePathClipboard[exporter->getUniqueName()];
}
list.repaint();
}));
}
else
{
m.addItem (PopupMenu::Item ("(Select a module in the list above to use this option)")
.setEnabled (false));
}
m.showMenuAsync (PopupMenu::Options()
.withDeletionCheck (*this)
.withTargetComponent (copyPathButton));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModulesInformationComponent)
};
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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
//==============================================================================
class ModulesInformationComponent : public Component,
private ListBoxModel,
private ValueTree::Listener
{
public:
ModulesInformationComponent (Project& p)
: project (p),
modulesValueTree (project.getEnabledModules().getState())
{
auto tempHeader = std::make_unique<ListBoxHeader> (Array<String> { "Module", "Version", "Make Local Copy", "Paths" },
Array<float> { 0.25f, 0.2f, 0.2f, 0.35f });
listHeader = tempHeader.get();
list.setHeaderComponent (std::move (tempHeader));
list.setModel (this);
list.setColour (ListBox::backgroundColourId, Colours::transparentBlack);
addAndMakeVisible (list);
list.updateContent();
list.setRowHeight (30);
list.setMultipleSelectionEnabled (true);
addAndMakeVisible (header);
addAndMakeVisible (setCopyModeButton);
setCopyModeButton.setTriggeredOnMouseDown (true);
setCopyModeButton.onClick = [this] { showCopyModeMenu(); };
addAndMakeVisible (copyPathButton);
copyPathButton.setTriggeredOnMouseDown (true);
copyPathButton.onClick = [this] { showSetPathsMenu(); };
addAndMakeVisible (globalPathsButton);
globalPathsButton.onClick = [this] { showGlobalPathsMenu(); };
modulesValueTree.addListener (this);
lookAndFeelChanged();
}
void paint (Graphics& g) override
{
g.setColour (findColour (secondaryBackgroundColourId));
g.fillRect (getLocalBounds().reduced (12, 0));
}
void resized() override
{
auto bounds = getLocalBounds().reduced (12, 0);
header.setBounds (bounds.removeFromTop (40));
bounds.reduce (10, 0);
list.setBounds (bounds.removeFromTop (list.getRowPosition (getNumRows() - 1, true).getBottom() + 20));
if (bounds.getHeight() < 35)
{
parentSizeChanged();
}
else
{
auto buttonRow = bounds.removeFromTop (35);
setCopyModeButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3)));
buttonRow.removeFromLeft (8);
copyPathButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3)));
buttonRow.removeFromLeft (8);
globalPathsButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3)));
}
}
void parentSizeChanged() override
{
auto width = jmax (550, getParentWidth());
auto y = list.getRowPosition (getNumRows() - 1, true).getBottom() + 200;
y = jmax (getParentHeight(), y);
setSize (width, y);
}
int getNumRows() override
{
return project.getEnabledModules().getNumModules();
}
void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override
{
ignoreUnused (height);
Rectangle<int> bounds (0, 0, width, height);
g.setColour (rowIsSelected ? findColour (defaultHighlightColourId) : findColour (rowNumber % 2 == 0 ? widgetBackgroundColourId
: secondaryWidgetBackgroundColourId));
g.fillRect (bounds.withTrimmedBottom (1));
bounds.removeFromLeft (5);
g.setColour (rowIsSelected ? findColour (defaultHighlightedTextColourId) : findColour (widgetTextColourId));
//==============================================================================
auto moduleID = project.getEnabledModules().getModuleID (rowNumber);
g.drawFittedText (moduleID, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (0) * (float) width)), Justification::centredLeft, 1);
//==============================================================================
auto version = project.getEnabledModules().getModuleInfo (moduleID).getVersion();
if (version.isEmpty())
version = "?";
g.drawFittedText (version, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (1) * (float) width)), Justification::centredLeft, 1);
//==============================================================================
g.drawFittedText (String (project.getEnabledModules().shouldCopyModuleFilesLocally (moduleID) ? "Yes" : "No"),
bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (2) * (float) width)), Justification::centredLeft, 1);
//==============================================================================
String pathText;
if (project.getEnabledModules().shouldUseGlobalPath (moduleID))
{
pathText = "Global";
}
else
{
StringArray paths;
for (Project::ExporterIterator exporter (project); exporter.next();)
paths.addIfNotAlreadyThere (exporter->getPathForModuleString (moduleID).trim());
paths.removeEmptyStrings();
paths.removeDuplicates (true);
pathText = paths.joinIntoString (", ");
}
g.drawFittedText (pathText, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (3) * (float) width)), Justification::centredLeft, 1);
}
void listBoxItemDoubleClicked (int row, const MouseEvent&) override
{
auto moduleID = project.getEnabledModules().getModuleID (row);
if (moduleID.isNotEmpty())
if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>())
pcc->showModule (moduleID);
}
void deleteKeyPressed (int row) override
{
project.getEnabledModules().removeModule (project.getEnabledModules().getModuleID (row));
}
void lookAndFeelChanged() override
{
setCopyModeButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
copyPathButton.setColour (TextButton::buttonColourId, findColour (defaultButtonBackgroundColourId));
globalPathsButton.setColour (TextButton::buttonColourId, findColour (defaultButtonBackgroundColourId));
}
private:
enum
{
nameCol = 1,
versionCol,
copyCol,
pathCol
};
Project& project;
ValueTree modulesValueTree;
ContentViewHeader header { "Modules", { getIcons().modules, Colours::transparentBlack } };
ListBox list;
ListBoxHeader* listHeader;
TextButton setCopyModeButton { "Set copy-mode for all modules..." };
TextButton copyPathButton { "Set paths for all modules..." };
TextButton globalPathsButton { "Enable/disable global path for modules..." };
std::map<String, var> modulePathClipboard;
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); }
void valueTreeChildAdded (ValueTree&, ValueTree&) override { itemChanged(); }
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { itemChanged(); }
void valueTreeChildOrderChanged (ValueTree&, int, int) override { itemChanged(); }
void valueTreeParentChanged (ValueTree&) override { itemChanged(); }
void itemChanged()
{
list.updateContent();
resized();
repaint();
}
static void setLocalCopyModeForAllModules (Project& project, bool copyLocally)
{
auto& modules = project.getEnabledModules();
for (auto i = modules.getNumModules(); --i >= 0;)
modules.shouldCopyModuleFilesLocallyValue (modules.getModuleID (i)) = copyLocally;
}
void showCopyModeMenu()
{
PopupMenu m;
m.addItem (PopupMenu::Item ("Set all modules to copy locally")
.setAction ([&] { setLocalCopyModeForAllModules (project, true); }));
m.addItem (PopupMenu::Item ("Set all modules to not copy locally")
.setAction ([&] { setLocalCopyModeForAllModules (project, false); }));
m.showMenuAsync (PopupMenu::Options().withTargetComponent (setCopyModeButton));
}
static void setAllModulesToUseGlobalPaths (Project& project, bool useGlobal)
{
auto& modules = project.getEnabledModules();
for (auto moduleID : modules.getAllModules())
modules.shouldUseGlobalPathValue (moduleID) = useGlobal;
}
static void setSelectedModulesToUseGlobalPaths (Project& project, SparseSet<int> selected, bool useGlobal)
{
auto& modules = project.getEnabledModules();
for (int i = 0; i < selected.size(); ++i)
modules.shouldUseGlobalPathValue (modules.getModuleID (selected[i])) = useGlobal;
}
void showGlobalPathsMenu()
{
PopupMenu m;
m.addItem (PopupMenu::Item ("Set all modules to use global paths")
.setAction ([&] { setAllModulesToUseGlobalPaths (project, true); }));
m.addItem (PopupMenu::Item ("Set all modules to not use global paths")
.setAction ([&] { setAllModulesToUseGlobalPaths (project, false); }));
m.addItem (PopupMenu::Item ("Set selected modules to use global paths")
.setEnabled (list.getNumSelectedRows() > 0)
.setAction ([&] { setSelectedModulesToUseGlobalPaths (project, list.getSelectedRows(), true); }));
m.addItem (PopupMenu::Item ("Set selected modules to not use global paths")
.setEnabled (list.getNumSelectedRows() > 0)
.setAction ([&] { setSelectedModulesToUseGlobalPaths (project, list.getSelectedRows(), false); }));
m.showMenuAsync (PopupMenu::Options().withTargetComponent (globalPathsButton));
}
void showSetPathsMenu()
{
PopupMenu m;
auto moduleToCopy = project.getEnabledModules().getModuleID (list.getSelectedRow());
if (moduleToCopy.isNotEmpty())
{
m.addItem (PopupMenu::Item ("Copy the paths from the module '" + moduleToCopy + "' to all other modules")
.setAction ([this, moduleToCopy]
{
auto& modulesList = project.getEnabledModules();
for (Project::ExporterIterator exporter (project); exporter.next();)
{
for (int i = 0; i < modulesList.getNumModules(); ++i)
{
auto modID = modulesList.getModuleID (i);
if (modID != moduleToCopy)
exporter->getPathForModuleValue (modID) = exporter->getPathForModuleValue (moduleToCopy).get();
}
}
list.repaint();
}));
m.addItem (PopupMenu::Item ("Copy paths from selected module")
.setEnabled (list.getNumSelectedRows() == 1)
.setAction ([this, moduleToCopy]
{
modulePathClipboard.clear();
for (Project::ExporterIterator exporter (project); exporter.next();)
modulePathClipboard[exporter->getUniqueName()] = exporter->getPathForModuleValue (moduleToCopy).get();
list.repaint();
}));
m.addItem (PopupMenu::Item ("Paste paths to selected modules")
.setEnabled (! modulePathClipboard.empty())
.setAction ([this]
{
for (int selectionId = 0; selectionId < list.getNumSelectedRows(); ++selectionId)
{
auto rowNumber = list.getSelectedRow (selectionId);
auto modID = project.getEnabledModules().getModuleID (rowNumber);
for (Project::ExporterIterator exporter (project); exporter.next();)
exporter->getPathForModuleValue (modID) = modulePathClipboard[exporter->getUniqueName()];
}
list.repaint();
}));
}
else
{
m.addItem (PopupMenu::Item ("(Select a module in the list above to use this option)")
.setEnabled (false));
}
m.showMenuAsync (PopupMenu::Options()
.withDeletionCheck (*this)
.withTargetComponent (copyPathButton));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModulesInformationComponent)
};

View File

@ -1,153 +1,153 @@
/*
==============================================================================
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 "../../CodeEditor/jucer_OpenDocumentManager.h"
#include "jucer_HeaderComponent.h"
#include "jucer_ProjectMessagesComponent.h"
#include "jucer_ContentViewComponent.h"
class Sidebar;
struct WizardHolder;
//==============================================================================
class ProjectContentComponent : public Component,
public ApplicationCommandTarget,
private ChangeListener,
private OpenDocumentManager::DocumentCloseListener
{
public:
//==============================================================================
ProjectContentComponent();
~ProjectContentComponent() override;
Project* getProject() const noexcept { return project; }
void setProject (Project*);
void saveOpenDocumentList();
void reloadLastOpenDocuments();
bool showEditorForFile (const File&, bool grabFocus);
bool hasFileInRecentList (const File&) const;
File getCurrentFile() const;
bool showDocument (OpenDocumentManager::Document*, bool grabFocus);
void hideDocument (OpenDocumentManager::Document*);
OpenDocumentManager::Document* getCurrentDocument() const { return currentDocument; }
void closeDocument();
void saveDocumentAsync();
void saveAsAsync();
void hideEditor();
void setScrollableEditorComponent (std::unique_ptr<Component> component);
void setEditorDocument (std::unique_ptr<Component> component, OpenDocumentManager::Document* doc);
Component* getEditorComponent();
Component& getSidebarComponent();
bool goToPreviousFile();
bool goToNextFile();
bool canGoToCounterpart() const;
bool goToCounterpart();
void saveProjectAsync();
void closeProject();
void openInSelectedIDE (bool saveFirst);
void showNewExporterMenu();
void showFilesPanel() { showProjectPanel (0); }
void showModulesPanel() { showProjectPanel (1); }
void showExportersPanel() { showProjectPanel (2); }
void showProjectSettings();
void showCurrentExporterSettings();
void showExporterSettings (const String& exporterName);
void showModule (const String& moduleID);
void showUserSettings();
void deleteSelectedTreeItems();
void refreshProjectTreeFileStatuses();
void updateMissingFileStatuses();
void addNewGUIFile();
void showBubbleMessage (Rectangle<int>, const String&);
StringArray getExportersWhichCanLaunch() const;
static void getSelectedProjectItemsBeingDragged (const DragAndDropTarget::SourceDetails&,
OwnedArray<Project::Item>& selectedNodes);
//==============================================================================
ApplicationCommandTarget* getNextCommandTarget() override;
void getAllCommands (Array<CommandID>&) override;
void getCommandInfo (CommandID, ApplicationCommandInfo&) override;
bool perform (const InvocationInfo&) override;
bool isSaveCommand (const CommandID id);
void paint (Graphics&) override;
void resized() override;
void childBoundsChanged (Component*) override;
void lookAndFeelChanged() override;
ProjectMessagesComponent& getProjectMessagesComponent() { return projectMessagesComponent; }
static String getProjectTabName() { return "Project"; }
private:
//==============================================================================
bool documentAboutToClose (OpenDocumentManager::Document*) override;
void changeListenerCallback (ChangeBroadcaster*) override;
void showTranslationTool();
//==============================================================================
void showProjectPanel (const int index);
bool canSelectedProjectBeLaunch();
//==============================================================================
Project* project = nullptr;
OpenDocumentManager::Document* currentDocument = nullptr;
RecentDocumentList recentDocumentList;
HeaderComponent headerComponent { this };
std::unique_ptr<Sidebar> sidebar;
ProjectMessagesComponent projectMessagesComponent;
ContentViewComponent contentViewComponent;
std::unique_ptr<ResizableEdgeComponent> resizerBar;
ComponentBoundsConstrainer sidebarSizeConstrainer;
std::unique_ptr<Component> translationTool;
BubbleMessageComponent bubbleMessage;
bool isForeground = false;
int lastViewedTab = 0;
std::unique_ptr<WizardHolder> wizardHolder;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectContentComponent)
};
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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 "../../CodeEditor/jucer_OpenDocumentManager.h"
#include "jucer_HeaderComponent.h"
#include "jucer_ProjectMessagesComponent.h"
#include "jucer_ContentViewComponent.h"
class Sidebar;
struct WizardHolder;
//==============================================================================
class ProjectContentComponent : public Component,
public ApplicationCommandTarget,
private ChangeListener,
private OpenDocumentManager::DocumentCloseListener
{
public:
//==============================================================================
ProjectContentComponent();
~ProjectContentComponent() override;
Project* getProject() const noexcept { return project; }
void setProject (Project*);
void saveOpenDocumentList();
void reloadLastOpenDocuments();
bool showEditorForFile (const File&, bool grabFocus);
bool hasFileInRecentList (const File&) const;
File getCurrentFile() const;
bool showDocument (OpenDocumentManager::Document*, bool grabFocus);
void hideDocument (OpenDocumentManager::Document*);
OpenDocumentManager::Document* getCurrentDocument() const { return currentDocument; }
void closeDocument();
void saveDocumentAsync();
void saveAsAsync();
void hideEditor();
void setScrollableEditorComponent (std::unique_ptr<Component> component);
void setEditorDocument (std::unique_ptr<Component> component, OpenDocumentManager::Document* doc);
Component* getEditorComponent();
Component& getSidebarComponent();
bool goToPreviousFile();
bool goToNextFile();
bool canGoToCounterpart() const;
bool goToCounterpart();
void saveProjectAsync();
void closeProject();
void openInSelectedIDE (bool saveFirst);
void showNewExporterMenu();
void showFilesPanel() { showProjectPanel (0); }
void showModulesPanel() { showProjectPanel (1); }
void showExportersPanel() { showProjectPanel (2); }
void showProjectSettings();
void showCurrentExporterSettings();
void showExporterSettings (const String& exporterName);
void showModule (const String& moduleID);
void showUserSettings();
void deleteSelectedTreeItems();
void refreshProjectTreeFileStatuses();
void updateMissingFileStatuses();
void addNewGUIFile();
void showBubbleMessage (Rectangle<int>, const String&);
StringArray getExportersWhichCanLaunch() const;
static void getSelectedProjectItemsBeingDragged (const DragAndDropTarget::SourceDetails&,
OwnedArray<Project::Item>& selectedNodes);
//==============================================================================
ApplicationCommandTarget* getNextCommandTarget() override;
void getAllCommands (Array<CommandID>&) override;
void getCommandInfo (CommandID, ApplicationCommandInfo&) override;
bool perform (const InvocationInfo&) override;
bool isSaveCommand (const CommandID id);
void paint (Graphics&) override;
void resized() override;
void childBoundsChanged (Component*) override;
void lookAndFeelChanged() override;
ProjectMessagesComponent& getProjectMessagesComponent() { return projectMessagesComponent; }
static String getProjectTabName() { return "Project"; }
private:
//==============================================================================
bool documentAboutToClose (OpenDocumentManager::Document*) override;
void changeListenerCallback (ChangeBroadcaster*) override;
void showTranslationTool();
//==============================================================================
void showProjectPanel (const int index);
bool canSelectedProjectBeLaunch();
//==============================================================================
Project* project = nullptr;
OpenDocumentManager::Document* currentDocument = nullptr;
RecentDocumentList recentDocumentList;
HeaderComponent headerComponent { this };
std::unique_ptr<Sidebar> sidebar;
ProjectMessagesComponent projectMessagesComponent;
ContentViewComponent contentViewComponent;
std::unique_ptr<ResizableEdgeComponent> resizerBar;
ComponentBoundsConstrainer sidebarSizeConstrainer;
std::unique_ptr<Component> translationTool;
BubbleMessageComponent bubbleMessage;
bool isForeground = false;
int lastViewedTab = 0;
std::unique_ptr<WizardHolder> wizardHolder;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectContentComponent)
};

File diff suppressed because it is too large Load Diff

View File

@ -1,171 +1,171 @@
/*
==============================================================================
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 "../../Application/jucer_Application.h"
//==============================================================================
class UserAvatarComponent : public Component,
public SettableTooltipClient,
public ChangeBroadcaster,
private LicenseController::LicenseStateListener
{
public:
UserAvatarComponent (bool isInteractive)
: interactive (isInteractive)
{
ProjucerApplication::getApp().getLicenseController().addListener (this);
lookAndFeelChanged();
}
~UserAvatarComponent() override
{
ProjucerApplication::getApp().getLicenseController().removeListener (this);
}
void paint (Graphics& g) override
{
auto bounds = getLocalBounds();
if (! isGPL)
{
bounds = bounds.removeFromRight (bounds.getHeight());
Path ellipse;
ellipse.addEllipse (bounds.toFloat());
g.reduceClipRegion (ellipse);
}
g.drawImage (currentAvatar, bounds.toFloat(), RectanglePlacement::fillDestination);
}
void mouseUp (const MouseEvent&) override
{
triggerClick();
}
void triggerClick()
{
if (interactive)
{
PopupMenu menu;
menu.addCommandItem (ProjucerApplication::getApp().commandManager.get(), CommandIDs::loginLogout);
menu.showMenuAsync (PopupMenu::Options().withTargetComponent (this));
}
}
bool isDisplaingGPLLogo() const noexcept { return isGPL; }
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
{
return interactive ? std::make_unique<AccessibilityHandler> (*this,
AccessibilityRole::button,
AccessibilityActions().addAction (AccessibilityActionType::press,
[this] { triggerClick(); }))
: nullptr;
}
private:
//==============================================================================
static Image createGPLAvatarImage()
{
if (auto logo = Drawable::createFromImageData (BinaryData::gpl_logo_svg, BinaryData::gpl_logo_svgSize))
{
auto bounds = logo->getDrawableBounds();
Image image (Image::ARGB, roundToInt (bounds.getWidth()), roundToInt (bounds.getHeight()), true);
Graphics g (image);
logo->draw (g, 1.0f);
return image;
}
jassertfalse;
return {};
}
Image createStandardAvatarImage()
{
Image image (Image::ARGB, 250, 250, true);
Graphics g (image);
g.setColour (findColour (defaultButtonBackgroundColourId));
g.fillAll();
g.setColour (findColour (defaultIconColourId));
auto path = getIcons().user;
g.fillPath (path, RectanglePlacement (RectanglePlacement::centred)
.getTransformToFit (path.getBounds(), image.getBounds().reduced (image.getHeight() / 5).toFloat()));
return image;
}
//==============================================================================
void licenseStateChanged() override
{
auto state = ProjucerApplication::getApp().getLicenseController().getCurrentState();
isGPL = ProjucerApplication::getApp().getLicenseController().getCurrentState().isGPL();
if (interactive)
{
auto formattedUserString = [state]() -> String
{
if (state.isSignedIn())
return (state.isGPL() ? "" : (state.username + " - ")) + state.getLicenseTypeString();
return "Not logged in";
}();
setTooltip (formattedUserString);
}
currentAvatar = isGPL ? gplAvatarImage
: state.isSignedIn() ? standardAvatarImage : signedOutAvatarImage;
repaint();
sendChangeMessage();
}
void lookAndFeelChanged() override
{
standardAvatarImage = createStandardAvatarImage();
signedOutAvatarImage = createStandardAvatarImage();
if (interactive)
signedOutAvatarImage.multiplyAllAlphas (0.4f);
licenseStateChanged();
repaint();
}
//==============================================================================
Image standardAvatarImage, signedOutAvatarImage, gplAvatarImage { createGPLAvatarImage() }, currentAvatar;
bool isGPL = false, interactive = false;
};
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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 "../../Application/jucer_Application.h"
//==============================================================================
class UserAvatarComponent : public Component,
public SettableTooltipClient,
public ChangeBroadcaster,
private LicenseController::LicenseStateListener
{
public:
UserAvatarComponent (bool isInteractive)
: interactive (isInteractive)
{
ProjucerApplication::getApp().getLicenseController().addListener (this);
lookAndFeelChanged();
}
~UserAvatarComponent() override
{
ProjucerApplication::getApp().getLicenseController().removeListener (this);
}
void paint (Graphics& g) override
{
auto bounds = getLocalBounds();
if (! isGPL)
{
bounds = bounds.removeFromRight (bounds.getHeight());
Path ellipse;
ellipse.addEllipse (bounds.toFloat());
g.reduceClipRegion (ellipse);
}
g.drawImage (currentAvatar, bounds.toFloat(), RectanglePlacement::fillDestination);
}
void mouseUp (const MouseEvent&) override
{
triggerClick();
}
void triggerClick()
{
if (interactive)
{
PopupMenu menu;
menu.addCommandItem (ProjucerApplication::getApp().commandManager.get(), CommandIDs::loginLogout);
menu.showMenuAsync (PopupMenu::Options().withTargetComponent (this));
}
}
bool isDisplaingGPLLogo() const noexcept { return isGPL; }
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
{
return interactive ? std::make_unique<AccessibilityHandler> (*this,
AccessibilityRole::button,
AccessibilityActions().addAction (AccessibilityActionType::press,
[this] { triggerClick(); }))
: nullptr;
}
private:
//==============================================================================
static Image createGPLAvatarImage()
{
if (auto logo = Drawable::createFromImageData (BinaryData::gpl_logo_svg, BinaryData::gpl_logo_svgSize))
{
auto bounds = logo->getDrawableBounds();
Image image (Image::ARGB, roundToInt (bounds.getWidth()), roundToInt (bounds.getHeight()), true);
Graphics g (image);
logo->draw (g, 1.0f);
return image;
}
jassertfalse;
return {};
}
Image createStandardAvatarImage()
{
Image image (Image::ARGB, 250, 250, true);
Graphics g (image);
g.setColour (findColour (defaultButtonBackgroundColourId));
g.fillAll();
g.setColour (findColour (defaultIconColourId));
auto path = getIcons().user;
g.fillPath (path, RectanglePlacement (RectanglePlacement::centred)
.getTransformToFit (path.getBounds(), image.getBounds().reduced (image.getHeight() / 5).toFloat()));
return image;
}
//==============================================================================
void licenseStateChanged() override
{
auto state = ProjucerApplication::getApp().getLicenseController().getCurrentState();
isGPL = ProjucerApplication::getApp().getLicenseController().getCurrentState().isGPL();
if (interactive)
{
auto formattedUserString = [state]() -> String
{
if (state.isSignedIn())
return (state.isGPL() ? "" : (state.username + " - ")) + state.getLicenseTypeString();
return "Not logged in";
}();
setTooltip (formattedUserString);
}
currentAvatar = isGPL ? gplAvatarImage
: state.isSignedIn() ? standardAvatarImage : signedOutAvatarImage;
repaint();
sendChangeMessage();
}
void lookAndFeelChanged() override
{
standardAvatarImage = createStandardAvatarImage();
signedOutAvatarImage = createStandardAvatarImage();
if (interactive)
signedOutAvatarImage.multiplyAllAlphas (0.4f);
licenseStateChanged();
repaint();
}
//==============================================================================
Image standardAvatarImage, signedOutAvatarImage, gplAvatarImage { createGPLAvatarImage() }, currentAvatar;
bool isGPL = false, interactive = false;
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff