Commit: cd15fbbed622873e9347b48b36931f99c1c6def5 Author: Julian Eisel Date: Thu Oct 6 15:51:10 2022 +0200 Branches: blender-projects-basics https://developer.blender.org/rBcd15fbbed622873e9347b48b36931f99c1c6def5
Support saving project settings to .blender_project/settings.json =================================================================== M release/scripts/startup/bl_ui/space_project_settings.py M source/blender/blenkernel/BKE_blender_project.h M source/blender/blenkernel/BKE_blender_project.hh M source/blender/blenkernel/intern/blender_project.cc M source/blender/blenkernel/intern/blender_project_test.cc M source/blender/windowmanager/intern/wm_files.c M source/blender/windowmanager/intern/wm_operators.c M source/blender/windowmanager/wm_files.h =================================================================== diff --git a/release/scripts/startup/bl_ui/space_project_settings.py b/release/scripts/startup/bl_ui/space_project_settings.py index 9fa42334e87..b2a543e9e07 100644 --- a/release/scripts/startup/bl_ui/space_project_settings.py +++ b/release/scripts/startup/bl_ui/space_project_settings.py @@ -19,8 +19,8 @@ class PROJECTSETTINGS_HT_header(Header): # Show '*' to let users know the settings have been modified. # TODO, wrong operator layout.operator( - "wm.save_userpref", - text=iface_("Save Project Settings") + (" *" if is_dirty else ""), + "wm.save_project_settings", + text=iface_("Save Settings") + (" *" if is_dirty else ""), translate=False, ) diff --git a/source/blender/blenkernel/BKE_blender_project.h b/source/blender/blenkernel/BKE_blender_project.h index e1bcced6cdd..eff87d4aef5 100644 --- a/source/blender/blenkernel/BKE_blender_project.h +++ b/source/blender/blenkernel/BKE_blender_project.h @@ -36,6 +36,8 @@ void BKE_project_active_unset(void); */ BlenderProject *BKE_project_active_load_from_path(const char *path) ATTR_NONNULL(); +bool BKE_project_settings_save(const BlenderProject *project) ATTR_NONNULL(); + const char *BKE_project_root_path_get(const BlenderProject *project) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); const char *BKE_project_name_get(const BlenderProject *project) ATTR_WARN_UNUSED_RESULT diff --git a/source/blender/blenkernel/BKE_blender_project.hh b/source/blender/blenkernel/BKE_blender_project.hh index 221e3c34d2c..bd1a232b592 100644 --- a/source/blender/blenkernel/BKE_blender_project.hh +++ b/source/blender/blenkernel/BKE_blender_project.hh @@ -10,6 +10,10 @@ #include "BLI_string_ref.hh" +namespace blender::io::serialize { +class DictionaryValue; +} + namespace blender::bke { class ProjectSettings; @@ -60,16 +64,27 @@ class ProjectSettings { /** * 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. + * 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) -> std::unique_ptr<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. + */ + auto save_to_disk(StringRef project_path) const -> bool; explicit ProjectSettings(StringRef project_root_path); auto project_root_path [[nodiscard]] () const -> StringRefNull; auto project_name [[nodiscard]] () const -> StringRefNull; + + private: + auto to_dictionary() const -> std::unique_ptr<io::serialize::DictionaryValue>; }; } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/blender_project.cc b/source/blender/blenkernel/intern/blender_project.cc index 5fe10df4cc9..99d92138ca5 100644 --- a/source/blender/blenkernel/intern/blender_project.cc +++ b/source/blender/blenkernel/intern/blender_project.cc @@ -152,36 +152,55 @@ static std::unique_ptr<ExtractedSettings> extract_settings( return extracted_settings; } -std::unique_ptr<ProjectSettings> ProjectSettings::load_from_disk(StringRef project_path) +struct ResolvedPaths { + std::string settings_filepath; + std::string project_root_path; +}; + +/** + * 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()); - if (!BLI_exists(project_path_native.c_str())) { - return nullptr; - } - - StringRef project_root_path = project_path_native; + ResolvedPaths resolved_paths{}; const StringRef path_no_trailing_slashes = path_strip_trailing_native_slash(project_path_native); - if (path_no_trailing_slashes.endswith(SETTINGS_DIRNAME)) { - project_root_path = StringRef(project_path_native).drop_suffix(SETTINGS_DIRNAME.size() + 1); + 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; +} - if (!path_contains_project_settings(project_root_path)) { +std::unique_ptr<ProjectSettings> ProjectSettings::load_from_disk(StringRef project_path) +{ + ResolvedPaths paths = resolve_paths_from_project_path(project_path); + + if (!BLI_exists(paths.project_root_path.c_str())) { + return nullptr; + } + if (!path_contains_project_settings(paths.project_root_path.c_str())) { return nullptr; } - std::string settings_filepath = project_path_native + SEP + SETTINGS_DIRNAME + SEP + - SETTINGS_FILENAME; - std::unique_ptr<serialize::Value> values = read_settings_file(settings_filepath); + std::unique_ptr<serialize::Value> values = read_settings_file(paths.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>(project_root_path); + std::unique_ptr loaded_settings = std::make_unique<ProjectSettings>(paths.project_root_path); if (extracted_settings) { loaded_settings->project_name_ = extracted_settings->project_name; } @@ -189,6 +208,50 @@ std::unique_ptr<ProjectSettings> ProjectSettings::load_from_disk(StringRef proje return loaded_settings; } +std::unique_ptr<serialize::DictionaryValue> ProjectSettings::to_dictionary() const +{ + using namespace serialize; + + std::unique_ptr<DictionaryValue> root = std::make_unique<DictionaryValue>(); + DictionaryValue::Items &root_attributes = root->elements(); + std::unique_ptr<DictionaryValue> project_dict = std::make_unique<DictionaryValue>(); + DictionaryValue::Items &project_attributes = project_dict->elements(); + project_attributes.append_as("name", new StringValue(project_name_)); + root_attributes.append_as("project", std::move(project_dict)); + + return root; +} + +static void write_settings_file(StringRef settings_filepath, + std::unique_ptr<serialize::DictionaryValue> dictionary) +{ + using namespace serialize; + + JsonFormatter formatter; + + std::ofstream os; + os.open(settings_filepath, std::ios::out | std::ios::trunc); + formatter.serialize(os, *dictionary); + os.close(); +} + +bool ProjectSettings::save_to_disk(StringRef project_path) const +{ + ResolvedPaths paths = resolve_paths_from_project_path(project_path); + + if (!BLI_exists(paths.project_root_path.c_str())) { + return false; + } + if (!path_contains_project_settings(paths.project_root_path.c_str())) { + return false; + } + + std::unique_ptr settings_as_dict = to_dictionary(); + write_settings_file(paths.settings_filepath, std::move(settings_as_dict)); + + return true; +} + StringRefNull ProjectSettings::project_root_path() const { return project_root_path_; @@ -241,6 +304,14 @@ BlenderProject *BKE_project_active_load_from_path(const char *path) return BKE_project_active_get(); } +bool BKE_project_settings_save(const BlenderProject *project_handle) +{ + const bke::BlenderProject *project = reinterpret_cast<const bke::BlenderProject *>( + project_handle); + const bke::ProjectSettings &settings = project->get_settings(); + return settings.save_to_disk(settings.project_root_path()); +} + const char *BKE_project_root_path_get(const BlenderProject *project_handle) { const bke::BlenderProject *project = reinterpret_cast<const bke::BlenderProject *>( diff --git a/source/blender/blenkernel/intern/blender_project_test.cc b/source/blender/blenkernel/intern/blender_project_test.cc index 3582bd81755..cb3c56070bb 100644 --- a/source/blender/blenkernel/intern/blender_project_test.cc +++ b/source/blender/blenkernel/intern/blender_project_test.cc @@ -19,6 +19,12 @@ namespace blender::bke::tests { +struct SVNFiles { + const std::string svn_root = blender::tests::flags_test_asset_dir(); + const std::string project_root_rel = "blender_project/the_project"; + const std::string project_root = svn_root + "/blender_project/the_project"; +}; + class ProjectTest : public testing::Test { /* RAII helper to delete the created directories reliably after testing or on errors. */ struct ProjectDirectoryRAIIWrapper { @@ -36,6 +42,9 @@ class ProjectTest : public testing::Test { native_project_path_ = project_path; BLI_path_slash_native(native_project_path_.data()); + if (native_project_path_.back() != SEP) { + native_project_path_ += SEP; + } /** Assert would be preferable but that would only run in debug builds, and #ASSERT_TRUE() * doesn't support printing a message. */ @@ -139,18 +148,42 @@ TEST_F(ProjectTest, settings_load_from_project_settings_path) ProjectSettings::create_settings_directory(project_path); std::unique_ptr project_settings = ProjectSettings::load_from_disk( - project_path + SEP_STR + ProjectSettings::SETTINGS_DIRNAME); + project_path + (ELEM(project_path.back(), SEP, ALTSEP) ? "" : SEP_STR) + + ProjectSettings::SETTINGS_DIRNAME); EXPECT_NE(project_settings, nullptr); EXPECT_EQ(project_settings->project_ @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs