[Bf-blender-cvs] [cd15fbbed62] blender-projects-basics: Support saving project settings to .blender_project/settings.json

Julian Eisel noreply at git.blender.org
Thu Oct 6 15:58:51 CEST 2022


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. @@



More information about the Bf-blender-cvs mailing list