WIP: Basic Blender Project Support (experimental feature) #107655

Draft
Julian Eisel wants to merge 94 commits from blender-projects-basics into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
6 changed files with 73 additions and 5 deletions
Showing only changes of commit 871bdb5d1f - Show all commits

View File

@ -13,11 +13,13 @@ class PROJECTSETTINGS_HT_header(Header):
@staticmethod
def draw_buttons(layout, context):
project = context.project
layout.operator_context = 'EXEC_AREA'
is_dirty = True
is_dirty = project and project.is_dirty
# Show '*' to let users know the settings have been modified.
# TODO, wrong operator
layout.operator(
"wm.save_project_settings",
text=iface_("Save Settings") + (" *" if is_dirty else ""),

View File

@ -46,6 +46,8 @@ const char *BKE_project_root_path_get(const BlenderProject *project) ATTR_WARN_U
void BKE_project_name_set(const BlenderProject *project_handle, const char *name) ATTR_NONNULL();
const char *BKE_project_name_get(const BlenderProject *project) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
bool BKE_project_has_unsaved_changes(const BlenderProject *project) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
#ifdef __cplusplus
}

View File

@ -48,6 +48,7 @@ class ProjectSettings {
/* Path to the project root using slashes in the OS native format. */
std::string project_root_path_;
std::string project_name_;
bool has_unsaved_changes_ = false;
public:
inline static const StringRefNull SETTINGS_DIRNAME = ".blender_project";
@ -76,13 +77,14 @@ class ProjectSettings {
* \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;
auto save_to_disk(StringRef project_path) -> 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 has_unsaved_changes [[nodiscard]] () const -> bool;
private:
auto to_dictionary() const -> std::unique_ptr<io::serialize::DictionaryValue>;

View File

@ -235,7 +235,7 @@ static void write_settings_file(StringRef settings_filepath,
os.close();
}
bool ProjectSettings::save_to_disk(StringRef project_path) const
bool ProjectSettings::save_to_disk(StringRef project_path)
{
ResolvedPaths paths = resolve_paths_from_project_path(project_path);
@ -249,6 +249,8 @@ bool ProjectSettings::save_to_disk(StringRef project_path) const
std::unique_ptr settings_as_dict = to_dictionary();
write_settings_file(paths.settings_filepath, std::move(settings_as_dict));
has_unsaved_changes_ = false;
return true;
}
@ -260,6 +262,7 @@ StringRefNull ProjectSettings::project_root_path() const
void ProjectSettings::project_name(StringRef new_name)
{
project_name_ = new_name;
has_unsaved_changes_ = true;
}
StringRefNull ProjectSettings::project_name() const
@ -267,6 +270,11 @@ StringRefNull ProjectSettings::project_name() const
return project_name_;
}
bool ProjectSettings::has_unsaved_changes() const
{
return has_unsaved_changes_;
}
} // namespace blender::bke
/* ---------------------------------------------------------------------- */
@ -313,7 +321,7 @@ 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();
bke::ProjectSettings &settings = project->get_settings();
return settings.save_to_disk(settings.project_root_path());
}
@ -337,3 +345,11 @@ const char *BKE_project_name_get(const BlenderProject *project_handle)
project_handle);
return project->get_settings().project_name().c_str();
}
bool BKE_project_has_unsaved_changes(const BlenderProject *project_handle)
{
const bke::BlenderProject *project = reinterpret_cast<const bke::BlenderProject *>(
project_handle);
const bke::ProjectSettings &settings = project->get_settings();
return settings.has_unsaved_changes();
}

View File

@ -185,6 +185,34 @@ TEST_F(ProjectTest, settings_json_write)
});
}
TEST_F(ProjectTest, settings_read_change_write)
{
SVNFiles svn_files{};
std::unique_ptr from_project_settings = ProjectSettings::load_from_disk(svn_files.project_root);
EXPECT_FALSE(from_project_settings->has_unsaved_changes());
/* 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);
from_project_settings->project_name("новый");
EXPECT_TRUE(from_project_settings->has_unsaved_changes());
if (!from_project_settings->save_to_disk(to_project_path)) {
FAIL();
}
EXPECT_FALSE(from_project_settings->has_unsaved_changes());
/* Now check if the settings written to disk match the expectations. */
std::unique_ptr written_settings = ProjectSettings::load_from_disk(to_project_path);
EXPECT_NE(written_settings, nullptr);
EXPECT_EQ(written_settings->project_name(), "новый");
EXPECT_FALSE(from_project_settings->has_unsaved_changes());
});
}
TEST_F(ProjectTest, project_root_path_find_from_path)
{
/* Test the temporarily created directories with their various path formats. */

View File

@ -92,6 +92,16 @@ static int rna_BlenderProject_root_path_editable(PointerRNA *UNUSED(ptr), const
return 0;
}
static bool rna_BlenderProject_is_dirty_get(PointerRNA *ptr)
{
const BlenderProject *project = ptr->data;
if (!project) {
return false;
}
return BKE_project_has_unsaved_changes(project);
}
#else
void RNA_def_blender_project(BlenderRNA *brna)
@ -117,6 +127,14 @@ void RNA_def_blender_project(BlenderRNA *brna)
"rna_BlenderProject_root_path_set");
RNA_def_property_editable_func(prop, "rna_BlenderProject_root_path_editable");
RNA_def_property_ui_text(prop, "Location", "The location of the project on disk");
prop = RNA_def_property(srna, "is_dirty", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_BlenderProject_is_dirty_get", NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(
prop,
"Dirty",
"Project settings have changed since read from disk. Save the settings to keep them");
}
#endif