Refactor BKE project interface & implementation

Mostly this is about more cleanly separating between `BlenderProject`
and `ProjectSettings`. Overall this is a nice improvement I think, helps
readability of interfaces and implementation a lot.
This commit is contained in:
Julian Eisel 2022-10-18 15:24:56 +02:00
parent 30b08fbec5
commit eafc9cb446
6 changed files with 313 additions and 239 deletions

View File

@ -20,7 +20,7 @@ typedef struct BlenderProject BlenderProject;
/** See #bke::ProjectSettings::create_settings_directory(). */
bool BKE_project_create_settings_directory(const char *project_root_path) ATTR_NONNULL();
/** See #bke::ProjectSettings::delete_settings_directory(). */
bool BKE_project_delete_settings_directory(const BlenderProject *project) ATTR_NONNULL();
bool BKE_project_delete_settings_directory(BlenderProject *project) ATTR_NONNULL();
BlenderProject *BKE_project_active_get(void) ATTR_WARN_UNUSED_RESULT;
/**
@ -38,18 +38,6 @@ bool BKE_project_is_path_project_root(const char *path) ATTR_WARN_UNUSED_RESULT
* referenced file/directory is a project root directory).
*/
bool BKE_project_contains_path(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
/**
* Attempt to load project based on the given path and return it. This should never become the
* active project, which should be loaded with #BKE_project_active_load_from_path() instead
* (because the active project uses unique_ptr without the guarded allocator, unlike this C-API
* function). The returned project pointer is owning and needs freeing with #BKE_project_free().
*/
BlenderProject *BKE_project_load_from_path(const char *path) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
/**
* Free a project allocated by #BKE_project_load_from_path() and null the pointer to it.
*/
void BKE_project_free(BlenderProject **project) ATTR_NONNULL();
/**
* Attempt to load and activate a project based on the given path. If the path doesn't lead

View File

@ -24,24 +24,67 @@ class ProjectSettings;
struct CustomAssetLibraries;
/**
* The active project (if there is one) is stored in static memory here too, and can be queried
* using #BlenderProject::get_active().
* Entry point / API for core Blender project management.
*
* Responsibilities:
* - Own and give access to the active project.
* - Manage the .blender_project/ directory.
* - Store and manage (including reading & writing) of the .blender_project/settings.json file. The
* implementation of this can be found in the internal #ProjectSettings class.
* - Tag for unsaved changes as needed.
*/
class BlenderProject {
friend class ProjectSettings;
/* Path to the project root using native slashes plus a trailing slash. */
std::string root_path_;
std::unique_ptr<ProjectSettings> settings_;
public:
inline static const StringRefNull SETTINGS_DIRNAME = ".blender_project";
inline static const StringRefNull SETTINGS_FILENAME = "settings.json";
public:
static auto get_active [[nodiscard]] () -> BlenderProject *;
static auto set_active_from_settings(std::unique_ptr<ProjectSettings> settings)
-> BlenderProject *;
static auto set_active(std::unique_ptr<BlenderProject> settings) -> BlenderProject *;
/**
* Read project settings from the given \a path, which may point to some directory or file inside
* of the project directory. Both Unix and Windows style slashes are allowed. Path is expected to
* be normalized.
*
* Attempt to read project data from the given \a project_path, which may be either a project
* root directory or the .blender_project directory, and load it into runtime data. Letting the
* returned #unique_pointer run out of scope cleanly destructs the runtime project data.
*
* \note Does NOT set the loaded project active.
*
* \return The loaded project or null on failure.
*/
static auto load_from_path(StringRef project_path) -> std::unique_ptr<BlenderProject>;
/**
* Initializes a blender project by creating a .blender_project directory at the given \a
* project_root_path.
* Both Unix and Windows style slashes are allowed.
*
* \return True if the settings directory was created, or already existed. False on failure.
*/
static auto create_settings_directory(StringRef project_root_path) -> bool;
/**
* Remove the .blender_project directory with all of its contents at the given \a
* project_root_path. If this is the path of the active project, it is marked as having changed
* but it is not unloaded. Runtime project data is still valid at this point.
*
* \return True on success.
*/
static auto delete_settings_directory(StringRef project_root_path) -> bool;
/**
* Check if the directory given by \a path contains a .blender_project directory and should thus
* be considered a project root directory.
*/
static bool path_is_project_root(StringRef path);
static auto path_is_project_root(StringRef path) -> bool;
/**
* Check if \a path points into a project and return the root directory path of that project (the
@ -49,51 +92,60 @@ class BlenderProject {
* the first project found, so if a project is nested inside another one, the nested project is
* used.
* Both Unix and Windows style slashes are allowed.
* \return the project root path or an empty path if not found.
*
* \return The project root path or an empty path if not found. The referenced string points into
* the input \a path, so slashes are not converted in the returned value.
*/
static auto project_root_path_find_from_path [[nodiscard]] (StringRef path) -> StringRef;
explicit BlenderProject(std::unique_ptr<ProjectSettings> settings);
/* --- Non-static member functions. --- */
BlenderProject(StringRef project_root_path, std::unique_ptr<ProjectSettings> settings);
/**
* Version of the static #delete_settings_directory() that deletes the settings directory of this
* project. Always tags as having unsaved changes after successful deletion.
*/
auto delete_settings_directory() -> bool;
auto root_path [[nodiscard]] () const -> StringRefNull;
auto get_settings [[nodiscard]] () const -> ProjectSettings &;
private:
static std::unique_ptr<BlenderProject> &active_project_ptr();
};
struct CustomAssetLibraries : NonCopyable {
ListBase asset_libraries = {nullptr, nullptr}; /* CustomAssetLibraryDefinition */
CustomAssetLibraries() = default;
CustomAssetLibraries(ListBase asset_libraries);
CustomAssetLibraries(CustomAssetLibraries &&);
~CustomAssetLibraries();
auto operator=(CustomAssetLibraries &&) -> CustomAssetLibraries &;
static auto active_project_ptr() -> std::unique_ptr<BlenderProject> &;
/**
* Get the project root path from a path that is either already the project root, or the
* .blender_project directory. Returns the path with native slashes plus a trailing slash.
*/
static auto project_path_to_native_project_root_path(StringRef project_path) -> std::string;
/**
* Get the .blender_project directory path from a project root path. Returns the path with native
* slashes plus a trailing slash. Assumes the path already ends with a native trailing slash.
*/
static auto project_root_path_to_settings_path(StringRef project_root_path) -> std::string;
/**
* Returns the path with native slashes.
* Assumes the path already ends with a native trailing slash.
*/
static auto project_root_path_to_settings_filepath(StringRef project_root_path) -> std::string;
};
/**
* Runtime representation of the project settings (`.blender_project/settings.json`) with IO
* functionality.
*/
class ProjectSettings {
/* Path to the project root using slashes in the OS native format. */
std::string project_root_path_;
std::string project_name_;
CustomAssetLibraries asset_libraries_ = {};
std::unique_ptr<CustomAssetLibraries> asset_libraries_;
bool has_unsaved_changes_ = false;
public:
inline static const StringRefNull SETTINGS_DIRNAME = ".blender_project";
inline static const StringRefNull SETTINGS_FILENAME = "settings.json";
/**
* Initializes a blender project by creating a .blender_project directory at the given \a
* project_root_path.
* Both Unix and Windows style slashes are allowed.
* \return True if the settings directory was created, or already existed. False on failure.
*/
static auto create_settings_directory(StringRef project_root_path) -> bool;
/**
* Read project settings from the given \a project_path, which may be either a project root
* directory or the .blender_project directory.
* Both Unix and Windows style slashes are allowed. Path is expected to be normalized.
*
* \return The read project settings or null in case of failure.
*/
static auto load_from_disk [[nodiscard]] (StringRef project_path)
@ -102,27 +154,26 @@ class ProjectSettings {
* Read project settings from the given \a path, which may point to some directory or file inside
* of the project directory. Both Unix and Windows style slashes are allowed. Path is expected to
* be normalized.
*
* \return The read project settings or null in case of failure.
*/
static auto load_from_path [[nodiscard]] (StringRef path) -> std::unique_ptr<ProjectSettings>;
/** Explicit constructor and destructor needed to manage the CustomAssetLibraries unique_ptr. */
ProjectSettings();
/* Implementation defaulted. */
~ProjectSettings();
/**
* Write project settings to the given \a project_path, which may be either a project root
* directory or the .blender_project directory. The .blender_project directory must exist.
* Both Unix and Windows style slashes are allowed. Path is expected to be normalized.
* \return True on success. If the .blender_project directory doesn't exist, that's treated as
* failure.
*
* \return True on success. If the .blender_project directory doesn't exist, that's treated
* as failure.
*/
auto save_to_disk(StringRef project_path) -> bool;
/**
* Remove the .blender_project directory with all of its contents. Does not unload the active
* project but marks it as having unsaved changes. Runtime project data is still valid.
* \return True on success.
*/
auto delete_settings_directory() -> bool;
explicit ProjectSettings(StringRef project_root_path);
auto project_root_path [[nodiscard]] () const -> StringRefNull;
void project_name(StringRef new_name);
auto project_name [[nodiscard]] () const -> StringRefNull;
auto asset_library_definitions() const -> const ListBase &;
@ -146,3 +197,8 @@ class ProjectSettings {
};
} // namespace blender::bke
inline ::BlenderProject *BKE_project_c_handle(blender::bke::BlenderProject *project)
{
return reinterpret_cast<::BlenderProject *>(project);
}

View File

@ -18,11 +18,18 @@
namespace blender::bke {
BlenderProject::BlenderProject(std::unique_ptr<ProjectSettings> settings)
BlenderProject::BlenderProject(const StringRef project_root_path,
std::unique_ptr<ProjectSettings> settings)
: settings_(std::move(settings))
{
root_path_ = BlenderProject::project_path_to_native_project_root_path(project_root_path);
BLI_assert(root_path_.back() == SEP);
}
/* ---------------------------------------------------------------------- */
/** \name Active project management (static storage)
* \{ */
/* Construct on First Use idiom. */
std::unique_ptr<BlenderProject> &BlenderProject::active_project_ptr()
{
@ -30,11 +37,11 @@ std::unique_ptr<BlenderProject> &BlenderProject::active_project_ptr()
return active_;
}
BlenderProject *BlenderProject::set_active_from_settings(std::unique_ptr<ProjectSettings> settings)
BlenderProject *BlenderProject::set_active(std::unique_ptr<BlenderProject> project)
{
std::unique_ptr<BlenderProject> &active = active_project_ptr();
if (settings) {
active = std::make_unique<BlenderProject>(BlenderProject(std::move(settings)));
if (project) {
active = std::move(project);
}
else {
active = nullptr;
@ -49,28 +56,70 @@ BlenderProject *BlenderProject::get_active()
return active.get();
}
StringRef BlenderProject::project_root_path_find_from_path(StringRef path)
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Project and project settings management.
* \{ */
std::unique_ptr<BlenderProject> BlenderProject::load_from_path(StringRef project_path)
{
std::string path_native = path;
BLI_path_slash_native(path_native.data());
const StringRef project_root_path = project_root_path_find_from_path(project_path);
StringRef cur_path = path;
while (cur_path.size()) {
std::string pa = StringRef(path_native.c_str(), cur_path.size());
if (path_is_project_root(pa)) {
return path.substr(0, cur_path.size());
}
/* Walk "up the path" (check the parent next). */
const int64_t pos_last_slash = cur_path.find_last_of(SEP);
if (pos_last_slash == StringRef::not_found) {
break;
}
cur_path = cur_path.substr(0, pos_last_slash);
std::unique_ptr<bke::ProjectSettings> project_settings = bke::ProjectSettings::load_from_path(
project_root_path);
if (!project_settings) {
return nullptr;
}
return "";
return std::make_unique<BlenderProject>(project_root_path, std::move(project_settings));
}
bool BlenderProject::create_settings_directory(StringRef project_path)
{
std::string project_root_path = project_path_to_native_project_root_path(project_path);
std::string settings_path = project_root_path_to_settings_path(project_root_path);
return BLI_dir_create_recursive(settings_path.c_str());
}
bool BlenderProject::delete_settings_directory(StringRef project_path)
{
std::string project_root_path = project_path_to_native_project_root_path(project_path);
std::string settings_path = project_root_path_to_settings_path(project_root_path);
/* Returns 0 on success. */
if (BLI_delete(settings_path.c_str(), true, true)) {
return false;
}
BlenderProject *active_project = get_active();
if (active_project &&
BLI_path_cmp_normalized(project_root_path.c_str(), active_project->root_path().c_str())) {
active_project->settings_->tag_has_unsaved_changes();
}
return true;
}
bool BlenderProject::delete_settings_directory()
{
if (!delete_settings_directory(root_path_)) {
return false;
}
settings_->tag_has_unsaved_changes();
return true;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Simple getters & setters
* \{ */
StringRefNull BlenderProject::root_path() const
{
return root_path_;
}
ProjectSettings &BlenderProject::get_settings() const
@ -79,7 +128,38 @@ ProjectSettings &BlenderProject::get_settings() const
return *settings_;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Path stuff
* \{ */
StringRef BlenderProject::project_root_path_find_from_path(StringRef path)
{
/* There are two versions of the path used here: One copy that is converted to native slashes,
* and the unmodified original path from the input. */
std::string path_native = path;
BLI_path_slash_native(path_native.data());
StringRef cur_path = path;
while (cur_path.size()) {
std::string cur_path_native = StringRef(path_native.c_str(), cur_path.size());
if (path_is_project_root(cur_path_native)) {
return path.substr(0, cur_path.size());
}
/* Walk "up the path" (check the parent next). */
const int64_t pos_last_slash = cur_path_native.find_last_of(SEP);
if (pos_last_slash == StringRef::not_found) {
break;
}
cur_path = cur_path.substr(0, pos_last_slash);
}
return "";
}
static StringRef path_strip_trailing_native_slash(StringRef path)
{
@ -92,26 +172,53 @@ static StringRef path_strip_trailing_native_slash(StringRef path)
bool BlenderProject::path_is_project_root(StringRef path)
{
path = path_strip_trailing_native_slash(path);
return BLI_exists(std::string(path + SEP_STR + ProjectSettings::SETTINGS_DIRNAME).c_str());
return BLI_exists(std::string(path + SEP_STR + SETTINGS_DIRNAME).c_str());
}
std::string BlenderProject::project_path_to_native_project_root_path(StringRef project_path)
{
std::string project_path_native = project_path;
BLI_path_slash_native(project_path_native.data());
const StringRef path_no_trailing_slashes = path_strip_trailing_native_slash(project_path_native);
if (path_no_trailing_slashes.endswith(SETTINGS_DIRNAME)) {
return StringRef(path_no_trailing_slashes).drop_suffix(SETTINGS_DIRNAME.size());
}
return std::string(path_no_trailing_slashes) + SEP;
}
std::string BlenderProject::project_root_path_to_settings_path(StringRef project_root_path)
{
BLI_assert(project_root_path.back() == SEP);
return project_root_path + SETTINGS_DIRNAME + SEP;
}
std::string BlenderProject::project_root_path_to_settings_filepath(StringRef project_root_path)
{
BLI_assert(project_root_path.back() == SEP);
return project_root_path_to_settings_path(project_root_path) + SETTINGS_FILENAME;
}
/** \} */
} // namespace blender::bke
/* ---------------------------------------------------------------------- */
/** \name C-API
* \{ */
using namespace blender;
bool BKE_project_create_settings_directory(const char *project_root_path)
{
return bke::ProjectSettings::create_settings_directory(project_root_path);
return bke::BlenderProject::create_settings_directory(project_root_path);
}
bool BKE_project_delete_settings_directory(const BlenderProject *project_handle)
bool BKE_project_delete_settings_directory(BlenderProject *project_handle)
{
const bke::BlenderProject *project = reinterpret_cast<const bke::BlenderProject *>(
project_handle);
bke::ProjectSettings &settings = project->get_settings();
return settings.delete_settings_directory();
bke::BlenderProject *project = reinterpret_cast<bke::BlenderProject *>(project_handle);
return project->delete_settings_directory();
}
BlenderProject *BKE_project_active_get(void)
@ -121,7 +228,7 @@ BlenderProject *BKE_project_active_get(void)
void BKE_project_active_unset(void)
{
bke::BlenderProject::set_active_from_settings(nullptr);
bke::BlenderProject::set_active(nullptr);
}
bool BKE_project_is_path_project_root(const char *path)
@ -135,39 +242,15 @@ bool BKE_project_contains_path(const char *path)
return !found_root_path.is_empty();
}
BlenderProject *BKE_project_load_from_path(const char *path)
{
std::unique_ptr<bke::ProjectSettings> project_settings = bke::ProjectSettings::load_from_path(
path);
if (!project_settings) {
return nullptr;
}
return reinterpret_cast<BlenderProject *>(
MEM_new<bke::BlenderProject>(__func__, std::move(project_settings)));
}
void BKE_project_free(BlenderProject **project_handle)
{
bke::BlenderProject *project = reinterpret_cast<bke::BlenderProject *>(*project_handle);
BLI_assert_msg(project != bke::BlenderProject::get_active(),
"Projects loaded with #BKE_project_load_from_path() must never be set active.");
MEM_delete(project);
*project_handle = nullptr;
}
BlenderProject *BKE_project_active_load_from_path(const char *path)
{
/* Project should be unset if the path doesn't contain a project root. Unset in the beginning so
* early exiting behaves correctly. */
BKE_project_active_unset();
std::unique_ptr<bke::ProjectSettings> project_settings = bke::ProjectSettings::load_from_path(
path);
bke::BlenderProject::set_active_from_settings(std::move(project_settings));
std::unique_ptr<bke::BlenderProject> project = bke::BlenderProject::load_from_path(path);
return BKE_project_active_get();
return reinterpret_cast<::BlenderProject *>(bke::BlenderProject::set_active(std::move(project)));
}
bool BKE_project_settings_save(const BlenderProject *project_handle)
@ -175,14 +258,14 @@ bool BKE_project_settings_save(const BlenderProject *project_handle)
const bke::BlenderProject *project = reinterpret_cast<const bke::BlenderProject *>(
project_handle);
bke::ProjectSettings &settings = project->get_settings();
return settings.save_to_disk(settings.project_root_path());
return settings.save_to_disk(project->root_path());
}
const char *BKE_project_root_path_get(const BlenderProject *project_handle)
{
const bke::BlenderProject *project = reinterpret_cast<const bke::BlenderProject *>(
project_handle);
return project->get_settings().project_root_path().c_str();
return project->root_path().c_str();
}
void BKE_project_name_set(const BlenderProject *project_handle, const char *name)
@ -222,3 +305,5 @@ bool BKE_project_has_unsaved_changes(const BlenderProject *project_handle)
const bke::ProjectSettings &settings = project->get_settings();
return settings.has_unsaved_changes();
}
/** \} */

View File

@ -24,35 +24,15 @@ namespace blender::bke {
/* ---------------------------------------------------------------------- */
ProjectSettings::ProjectSettings(StringRef project_root_path)
: project_root_path_(project_root_path)
{
}
struct CustomAssetLibraries : NonCopyable {
ListBase asset_libraries = {nullptr, nullptr}; /* CustomAssetLibraryDefinition */
bool ProjectSettings::create_settings_directory(StringRef project_root_path)
{
std::string project_root_path_native = project_root_path;
BLI_path_slash_native(project_root_path_native.data());
return BLI_dir_create_recursive(
std::string(project_root_path_native + SEP + SETTINGS_DIRNAME).c_str());
}
bool ProjectSettings::delete_settings_directory()
{
BLI_assert(project_root_path_[0] == SEP);
std::string dot_blender_project_dir_path = project_root_path_ + SETTINGS_DIRNAME;
/* Returns 0 on success. */
if (BLI_delete(dot_blender_project_dir_path.c_str(), true, true)) {
return false;
}
has_unsaved_changes_ = true;
return true;
}
/* ---------------------------------------------------------------------- */
CustomAssetLibraries() = default;
CustomAssetLibraries(ListBase asset_libraries);
CustomAssetLibraries(CustomAssetLibraries &&);
~CustomAssetLibraries();
auto operator=(CustomAssetLibraries &&) -> CustomAssetLibraries &;
};
CustomAssetLibraries::CustomAssetLibraries(ListBase asset_libraries)
: asset_libraries(asset_libraries)
@ -196,11 +176,11 @@ std::unique_ptr<serialize::DictionaryValue> ProjectSettings::to_dictionary() con
root_attributes.append_as("project", std::move(project_dict));
}
/* "asset_libraries": */ {
if (!BLI_listbase_is_empty(&asset_libraries_.asset_libraries)) {
if (!BLI_listbase_is_empty(&asset_libraries_->asset_libraries)) {
std::unique_ptr<ArrayValue> asset_libs_array = std::make_unique<ArrayValue>();
ArrayValue::Items &asset_libs_elements = asset_libs_array->elements();
LISTBASE_FOREACH (
const CustomAssetLibraryDefinition *, library, &asset_libraries_.asset_libraries) {
const CustomAssetLibraryDefinition *, library, &asset_libraries_->asset_libraries) {
std::unique_ptr<DictionaryValue> library_dict = std::make_unique<DictionaryValue>();
DictionaryValue::Items &library_attributes = library_dict->elements();
@ -232,67 +212,33 @@ static void write_settings_file(StringRef settings_filepath,
/* ---------------------------------------------------------------------- */
struct ResolvedPaths {
std::string settings_filepath;
std::string project_root_path;
};
static StringRef path_strip_trailing_native_slash(StringRef path)
{
const int64_t pos_before_trailing_slash = path.find_last_not_of(SEP);
return (pos_before_trailing_slash == StringRef::not_found) ?
path :
path.substr(0, pos_before_trailing_slash + 1);
}
/**
* Returned paths can be assumed to use native slashes.
*/
static ResolvedPaths resolve_paths_from_project_path(StringRef project_path)
{
std::string project_path_native = project_path;
BLI_path_slash_native(project_path_native.data());
ResolvedPaths resolved_paths{};
const StringRef path_no_trailing_slashes = path_strip_trailing_native_slash(project_path_native);
if (path_no_trailing_slashes.endswith(ProjectSettings::SETTINGS_DIRNAME)) {
resolved_paths.project_root_path =
StringRef(path_no_trailing_slashes).drop_suffix(ProjectSettings::SETTINGS_DIRNAME.size());
}
else {
resolved_paths.project_root_path = std::string(path_no_trailing_slashes) + SEP;
}
resolved_paths.settings_filepath = resolved_paths.project_root_path +
ProjectSettings::SETTINGS_DIRNAME + SEP +
ProjectSettings::SETTINGS_FILENAME;
return resolved_paths;
}
std::unique_ptr<ProjectSettings> ProjectSettings::load_from_disk(StringRef project_path)
{
ResolvedPaths paths = resolve_paths_from_project_path(project_path);
const std::string project_root_path = BlenderProject::project_path_to_native_project_root_path(
project_path);
if (!BLI_exists(paths.project_root_path.c_str())) {
if (!BLI_exists(project_root_path.c_str())) {
return nullptr;
}
if (!BlenderProject::path_is_project_root(paths.project_root_path.c_str())) {
if (!BlenderProject::path_is_project_root(project_root_path.c_str())) {
return nullptr;
}
std::unique_ptr<serialize::Value> values = read_settings_file(paths.settings_filepath);
const std::string settings_filepath = BlenderProject::project_root_path_to_settings_filepath(
project_root_path);
std::unique_ptr<serialize::Value> values = read_settings_file(settings_filepath);
std::unique_ptr<ExtractedSettings> extracted_settings = nullptr;
if (values) {
BLI_assert(values->as_dictionary_value() != nullptr);
extracted_settings = extract_settings(*values->as_dictionary_value());
}
std::unique_ptr loaded_settings = std::make_unique<ProjectSettings>(paths.project_root_path);
std::unique_ptr loaded_settings = std::make_unique<ProjectSettings>();
if (extracted_settings) {
loaded_settings->project_name_ = extracted_settings->project_name;
/* Moves ownership. */
loaded_settings->asset_libraries_ = CustomAssetLibraries(extracted_settings->asset_libraries);
loaded_settings->asset_libraries_ = std::make_unique<CustomAssetLibraries>(
extracted_settings->asset_libraries);
}
return loaded_settings;
@ -310,17 +256,20 @@ std::unique_ptr<ProjectSettings> ProjectSettings::load_from_path(StringRef path)
bool ProjectSettings::save_to_disk(StringRef project_path)
{
ResolvedPaths paths = resolve_paths_from_project_path(project_path);
const std::string project_root_path = BlenderProject::project_path_to_native_project_root_path(
project_path);
if (!BLI_exists(paths.project_root_path.c_str())) {
if (!BLI_exists(project_root_path.c_str())) {
return false;
}
if (!BlenderProject::path_is_project_root(paths.project_root_path.c_str())) {
if (!BlenderProject::path_is_project_root(project_root_path.c_str())) {
return false;
}
const std::string settings_filepath = BlenderProject::project_root_path_to_settings_filepath(
project_root_path);
std::unique_ptr settings_as_dict = to_dictionary();
write_settings_file(paths.settings_filepath, std::move(settings_as_dict));
write_settings_file(settings_filepath, std::move(settings_as_dict));
has_unsaved_changes_ = false;
@ -329,11 +278,12 @@ bool ProjectSettings::save_to_disk(StringRef project_path)
/* ---------------------------------------------------------------------- */
StringRefNull ProjectSettings::project_root_path() const
ProjectSettings::ProjectSettings() : asset_libraries_(std::make_unique<CustomAssetLibraries>())
{
return project_root_path_;
}
ProjectSettings::~ProjectSettings() = default;
void ProjectSettings::project_name(StringRef new_name)
{
project_name_ = new_name;
@ -347,12 +297,12 @@ StringRefNull ProjectSettings::project_name() const
const ListBase &ProjectSettings::asset_library_definitions() const
{
return asset_libraries_.asset_libraries;
return asset_libraries_->asset_libraries;
}
ListBase &ProjectSettings::asset_library_definitions()
{
return asset_libraries_.asset_libraries;
return asset_libraries_->asset_libraries;
}
void ProjectSettings::tag_has_unsaved_changes()

View File

@ -118,13 +118,13 @@ class ProjectTest : public testing::Test {
TEST_F(ProjectTest, settings_create)
{
test_foreach_project_path([](StringRefNull project_path, StringRefNull project_path_native) {
if (!ProjectSettings::create_settings_directory(project_path)) {
if (!BlenderProject::create_settings_directory(project_path)) {
/* Not a regular test failure, this may fail if there is a permission issue for example. */
FAIL() << "Failed to create project directory in '" << project_path
<< "', check permissions";
}
std::string project_settings_dir = project_path_native + SEP_STR +
ProjectSettings::SETTINGS_DIRNAME;
BlenderProject::SETTINGS_DIRNAME;
EXPECT_TRUE(BLI_exists(project_settings_dir.c_str()) &&
BLI_is_dir(project_settings_dir.c_str()))
<< project_settings_dir + " was not created";
@ -133,31 +133,31 @@ TEST_F(ProjectTest, settings_create)
/* Load the project by pointing to the project root directory (as opposed to the .blender_project
* directory). */
TEST_F(ProjectTest, settings_load_from_project_root_path)
TEST_F(ProjectTest, load_from_project_root_path)
{
test_foreach_project_path([](StringRefNull project_path, StringRefNull project_path_native) {
ProjectSettings::create_settings_directory(project_path);
BlenderProject::create_settings_directory(project_path);
std::unique_ptr project_settings = ProjectSettings::load_from_disk(project_path);
EXPECT_NE(project_settings, nullptr);
EXPECT_EQ(project_settings->project_root_path(), project_path_native);
EXPECT_EQ(project_settings->project_name(), "");
std::unique_ptr project = BlenderProject::load_from_path(project_path);
EXPECT_NE(project, nullptr);
EXPECT_EQ(project->root_path(), project_path_native);
EXPECT_EQ(project->get_settings().project_name(), "");
});
}
/* Load the project by pointing to the .blender_project directory (as opposed to the project root
* directory). */
TEST_F(ProjectTest, settings_load_from_project_settings_path)
TEST_F(ProjectTest, load_from_project_settings_path)
{
test_foreach_project_path([](StringRefNull project_path, StringRefNull project_path_native) {
ProjectSettings::create_settings_directory(project_path);
BlenderProject::create_settings_directory(project_path);
std::unique_ptr project_settings = ProjectSettings::load_from_disk(
std::unique_ptr project = BlenderProject::load_from_path(
project_path + (ELEM(project_path.back(), SEP, ALTSEP) ? "" : SEP_STR) +
ProjectSettings::SETTINGS_DIRNAME);
EXPECT_NE(project_settings, nullptr);
EXPECT_EQ(project_settings->project_root_path(), project_path_native);
EXPECT_EQ(project_settings->project_name(), "");
BlenderProject::SETTINGS_DIRNAME);
EXPECT_NE(project, nullptr);
EXPECT_EQ(project->root_path(), project_path_native);
EXPECT_EQ(project->get_settings().project_name(), "");
});
}
@ -192,7 +192,7 @@ TEST_F(ProjectTest, settings_json_write)
/* Take the settings read from the SVN files and write it to /tmp/ projects. */
test_foreach_project_path(
[&from_project_settings](StringRefNull to_project_path, StringRefNull) {
ProjectSettings::create_settings_directory(to_project_path);
BlenderProject::create_settings_directory(to_project_path);
if (!from_project_settings->save_to_disk(to_project_path)) {
FAIL();
@ -215,7 +215,7 @@ TEST_F(ProjectTest, settings_read_change_write)
/* Take the settings read from the SVN files and write it to /tmp/ projects. */
test_foreach_project_path(
[&from_project_settings](StringRefNull to_project_path, StringRefNull) {
ProjectSettings::create_settings_directory(to_project_path);
BlenderProject::create_settings_directory(to_project_path);
from_project_settings->project_name("новый");
EXPECT_TRUE(from_project_settings->has_unsaved_changes());
@ -240,7 +240,7 @@ TEST_F(ProjectTest, project_root_path_find_from_path)
/* First test without a .blender_project directory present. */
EXPECT_EQ(BlenderProject::project_root_path_find_from_path(project_path), "");
ProjectSettings::create_settings_directory(project_path);
BlenderProject::create_settings_directory(project_path);
EXPECT_EQ(BlenderProject::project_root_path_find_from_path(project_path), project_path);
});
@ -302,14 +302,14 @@ TEST_F(BlendfileProjectLoadingTest, load_blend_file)
TEST_F(ProjectTest, project_load_and_delete)
{
test_foreach_project_path([](StringRefNull project_path, StringRefNull project_path_native) {
ProjectSettings::create_settings_directory(project_path);
BlenderProject::create_settings_directory(project_path);
::BlenderProject *project = BKE_project_active_load_from_path(project_path.c_str());
EXPECT_NE(project, nullptr);
EXPECT_FALSE(BKE_project_has_unsaved_changes(project));
std::string project_settings_dir = project_path_native + SEP_STR +
ProjectSettings::SETTINGS_DIRNAME;
BlenderProject::SETTINGS_DIRNAME;
EXPECT_TRUE(BLI_exists(project_settings_dir.c_str()));
if (!BKE_project_delete_settings_directory(project)) {

View File

@ -12,6 +12,7 @@
#include "BKE_asset_library_custom.h"
#include "BKE_blender_project.h"
#include "BKE_blender_project.hh"
#include "BKE_context.h"
#include "BKE_main.h"
#include "BKE_report.h"
@ -52,15 +53,25 @@ bool ED_project_new(const Main *bmain, const char *project_root_dir, ReportList
return false;
}
std::unique_ptr<bke::BlenderProject> loaded_project = bke::BlenderProject::load_from_path(
project_root_dir);
/* Some default settings for the project. */
if (loaded_project) {
BlenderProject *loaded_project_c = BKE_project_c_handle(loaded_project.get());
ED_project_set_defaults(loaded_project_c);
/* Write defaults to the hard drive. */
BKE_project_settings_save(loaded_project_c);
}
BKE_reportf(reports, RPT_INFO, "Project created and loaded successfully");
const char *blendfile_path = BKE_main_blendfile_path(bmain);
bool activated_new_project = false;
if (blendfile_path[0] && BLI_path_contains(project_root_dir, blendfile_path)) {
if (BKE_project_active_load_from_path(blendfile_path)) {
activated_new_project = true;
}
const char *blend_path = BKE_main_blendfile_path(bmain);
const bool blend_is_in_project = blend_path[0] &&
BLI_path_contains(project_root_dir, blend_path);
if (blend_is_in_project) {
bke::BlenderProject::set_active(std::move(loaded_project));
}
else {
BKE_reportf(reports,
@ -69,21 +80,5 @@ bool ED_project_new(const Main *bmain, const char *project_root_dir, ReportList
"project is not active");
}
/* Some default settings for the project. It has to be loaded for that. */
BlenderProject *new_project = activated_new_project ?
CTX_wm_project() :
BKE_project_load_from_path(project_root_dir);
if (new_project) {
ED_project_set_defaults(new_project);
/* Write defaults to the hard drive. */
BKE_project_settings_save(new_project);
/* We just temporary loaded the project, free it again. */
if (!activated_new_project) {
BKE_project_free(&new_project);
}
}
return true;
}