WIP: Basic Blender Project Support (experimental feature) #107655
|
@ -38,6 +38,8 @@ BlenderProject *BKE_project_active_load_from_path(const char *path) 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
|
||||
ATTR_NONNULL();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -43,9 +43,11 @@ class BlenderProject {
|
|||
class ProjectSettings {
|
||||
/* Path to the project root using slashes in the OS native format. */
|
||||
std::string project_root_path_;
|
||||
std::string project_name_;
|
||||
|
||||
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
|
||||
|
@ -67,6 +69,7 @@ class ProjectSettings {
|
|||
explicit ProjectSettings(StringRef project_root_path);
|
||||
|
||||
auto project_root_path [[nodiscard]] () const -> StringRefNull;
|
||||
auto project_name [[nodiscard]] () const -> StringRefNull;
|
||||
};
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -4,10 +4,13 @@
|
|||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "BKE_blender_project.h"
|
||||
#include "BKE_blender_project.hh"
|
||||
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_serialize.hh"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
|
@ -15,6 +18,8 @@
|
|||
#include "BLI_fileops.h"
|
||||
#include "BLI_path_util.h"
|
||||
|
||||
namespace serialize = blender::io::serialize;
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
static StringRef path_strip_trailing_native_slash(StringRef path);
|
||||
|
@ -102,6 +107,51 @@ static bool path_contains_project_settings(StringRef path)
|
|||
return BLI_exists(std::string(path + SEP_STR + ProjectSettings::SETTINGS_DIRNAME).c_str());
|
||||
}
|
||||
|
||||
struct ExtractedSettings {
|
||||
std::string project_name;
|
||||
};
|
||||
|
||||
static std::unique_ptr<serialize::Value> read_settings_file(StringRef settings_filepath)
|
||||
{
|
||||
std::ifstream is;
|
||||
is.open(settings_filepath);
|
||||
if (is.fail()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
serialize::JsonFormatter formatter;
|
||||
/* Will not be a dictionary in case of error (corrupted file). */
|
||||
std::unique_ptr<serialize::Value> deserialized_values = formatter.deserialize(is);
|
||||
is.close();
|
||||
|
||||
if (deserialized_values->type() != serialize::eValueType::Dictionary) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return deserialized_values;
|
||||
}
|
||||
|
||||
static std::unique_ptr<ExtractedSettings> extract_settings(
|
||||
const serialize::DictionaryValue &dictionary)
|
||||
{
|
||||
using namespace serialize;
|
||||
|
||||
std::unique_ptr extracted_settings = std::make_unique<ExtractedSettings>();
|
||||
|
||||
const DictionaryValue::Lookup attributes = dictionary.create_lookup();
|
||||
const DictionaryValue::LookupValue *project_value = attributes.lookup_ptr("project");
|
||||
BLI_assert(project_value != nullptr);
|
||||
|
||||
const DictionaryValue *project_dict = (*project_value)->as_dictionary_value();
|
||||
const StringValue *project_name_value =
|
||||
project_dict->create_lookup().lookup("name")->as_string_value();
|
||||
if (project_name_value) {
|
||||
extracted_settings->project_name = project_name_value->value();
|
||||
}
|
||||
|
||||
return extracted_settings;
|
||||
}
|
||||
|
||||
std::unique_ptr<ProjectSettings> ProjectSettings::load_from_disk(StringRef project_path)
|
||||
{
|
||||
std::string project_path_native = project_path;
|
||||
|
@ -122,7 +172,21 @@ std::unique_ptr<ProjectSettings> ProjectSettings::load_from_disk(StringRef proje
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<ProjectSettings>(project_root_path);
|
||||
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<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);
|
||||
if (extracted_settings) {
|
||||
loaded_settings->project_name_ = extracted_settings->project_name;
|
||||
}
|
||||
|
||||
return loaded_settings;
|
||||
}
|
||||
|
||||
StringRefNull ProjectSettings::project_root_path() const
|
||||
|
@ -130,6 +194,11 @@ StringRefNull ProjectSettings::project_root_path() const
|
|||
return project_root_path_;
|
||||
}
|
||||
|
||||
StringRefNull ProjectSettings::project_name() const
|
||||
{
|
||||
return project_name_;
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
@ -178,3 +247,10 @@ const char *BKE_project_root_path_get(const BlenderProject *project_handle)
|
|||
project_handle);
|
||||
return project->get_settings().project_root_path().c_str();
|
||||
}
|
||||
|
||||
const char *BKE_project_name_get(const BlenderProject *project_handle)
|
||||
{
|
||||
const bke::BlenderProject *project = reinterpret_cast<const bke::BlenderProject *>(
|
||||
project_handle);
|
||||
return project->get_settings().project_name().c_str();
|
||||
}
|
||||
|
|
|
@ -127,6 +127,7 @@ TEST_F(ProjectTest, settings_load_from_project_root_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(), "");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -141,6 +142,7 @@ TEST_F(ProjectTest, settings_load_from_project_settings_path)
|
|||
project_path + 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(), "");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -188,6 +190,7 @@ TEST_F(BlendfileProjectLoadingTest, load_blend_file)
|
|||
::BlenderProject *svn_project = BKE_project_active_load_from_path(bfile->main->filepath);
|
||||
EXPECT_NE(svn_project, nullptr);
|
||||
EXPECT_EQ(BKE_project_active_get(), svn_project);
|
||||
EXPECT_STREQ("Ružena", BKE_project_name_get(svn_project));
|
||||
/* Note: The project above will be freed once a different active project is set. So get the path
|
||||
* for future comparisons. */
|
||||
std::string svn_project_path = BKE_project_root_path_get(svn_project);
|
||||
|
@ -204,6 +207,7 @@ TEST_F(BlendfileProjectLoadingTest, load_blend_file)
|
|||
EXPECT_NE(svn_project_from_nested, nullptr);
|
||||
EXPECT_EQ(BKE_project_active_get(), svn_project_from_nested);
|
||||
EXPECT_STREQ(svn_project_path.c_str(), BKE_project_root_path_get(svn_project_from_nested));
|
||||
EXPECT_STREQ("Ružena", BKE_project_name_get(svn_project_from_nested));
|
||||
blendfile_free();
|
||||
|
||||
/* Check if loading a .blend that's not in the project unsets the project properly. */
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_blender_project.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_icons.h"
|
||||
|
@ -447,6 +448,8 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
|
|||
|
||||
void wm_window_title(wmWindowManager *wm, wmWindow *win)
|
||||
{
|
||||
#define MAX_PROJECT_NAME_HINT (MAX_NAME + 4)
|
||||
|
||||
if (WM_window_is_temp_screen(win)) {
|
||||
/* Nothing to do for 'temp' windows,
|
||||
* because #WM_window_open always sets window title. */
|
||||
|
@ -455,14 +458,23 @@ void wm_window_title(wmWindowManager *wm, wmWindow *win)
|
|||
/* this is set to 1 if you don't have startup.blend open */
|
||||
const char *blendfile_path = BKE_main_blendfile_path_from_global();
|
||||
if (blendfile_path[0] != '\0') {
|
||||
char str[sizeof(((Main *)NULL)->filepath) + 24];
|
||||
char project_name_hint[MAX_PROJECT_NAME_HINT] = "";
|
||||
char str[sizeof(((Main *)NULL)->filepath) + sizeof(project_name_hint) + 24];
|
||||
|
||||
struct BlenderProject *project = CTX_wm_project();
|
||||
if (project) {
|
||||
const char *name = BKE_project_name_get(project);
|
||||
BLI_snprintf(project_name_hint,
|
||||
sizeof(project_name_hint),
|
||||
"%s - ",
|
||||
(name && name[0]) ? name : IFACE_("Unnamed project"));
|
||||
}
|
||||
|
||||
BLI_snprintf(str,
|
||||
sizeof(str),
|
||||
"Blender%s [%s%s%s]",
|
||||
wm->file_saved ? "" : "*",
|
||||
project ? IFACE_("Has Project - ") : "",
|
||||
project_name_hint,
|
||||
blendfile_path,
|
||||
G_MAIN->recovered ? " (Recovered)" : "");
|
||||
GHOST_SetTitle(win->ghostwin, str);
|
||||
|
@ -476,6 +488,8 @@ void wm_window_title(wmWindowManager *wm, wmWindow *win)
|
|||
* terminate request (e.g. OS Shortcut Alt+F4, Command+Q, (...), or session end). */
|
||||
GHOST_SetWindowModifiedState(win->ghostwin, (bool)!wm->file_saved);
|
||||
}
|
||||
|
||||
#undef MAX_PROJECT_NAME_HINT
|
||||
}
|
||||
|
||||
void WM_window_set_dpi(const wmWindow *win)
|
||||
|
|
Loading…
Reference in New Issue