diff --git a/Sources/OvCore/include/OvCore/Scripting/Common/TScriptEngine.h b/Sources/OvCore/include/OvCore/Scripting/Common/TScriptEngine.h index d7a17f68..82567ce8 100644 --- a/Sources/OvCore/include/OvCore/Scripting/Common/TScriptEngine.h +++ b/Sources/OvCore/include/OvCore/Scripting/Common/TScriptEngine.h @@ -31,12 +31,12 @@ namespace OvCore::Scripting public: /** * Constructor of the generic script engine - * @param p_scriptsFolder - * @param p_engineResourcesFolder + * @param p_projectAssetsPath + * @param p_engineAssetsPath */ TScriptEngine( - const std::filesystem::path& p_scriptsFolder, - const std::filesystem::path& p_engineResourcesFolder + const std::filesystem::path& p_projectAssetsPath, + const std::filesystem::path& p_engineAssetsPath ); /** @@ -46,9 +46,10 @@ namespace OvCore::Scripting /** * Create necessary project files. + * @param p_projectFolder Root folder of the user's project * @param p_force */ - bool CreateProjectFiles(bool p_force = false); + bool CreateProjectFiles(const std::filesystem::path& p_projectFolder, bool p_force = false); /** * Returns a list of valid extensions for scripts. diff --git a/Sources/OvCore/include/OvCore/Scripting/Lua/LuaScriptEngine.h b/Sources/OvCore/include/OvCore/Scripting/Lua/LuaScriptEngine.h index 3a6bf2cf..752abf79 100644 --- a/Sources/OvCore/include/OvCore/Scripting/Lua/LuaScriptEngine.h +++ b/Sources/OvCore/include/OvCore/Scripting/Lua/LuaScriptEngine.h @@ -30,8 +30,8 @@ namespace OvCore::Scripting struct LuaScriptEngineContext { std::unique_ptr luaState; - std::filesystem::path scriptRootFolder; - std::filesystem::path engineResourcesFolder; + std::filesystem::path projectAssetsPath; + std::filesystem::path engineAssetsPath; std::vector> behaviours; uint32_t errorCount; }; @@ -46,12 +46,12 @@ namespace OvCore::Scripting public: /** * Constructor of the lua script engine - * @param p_scriptsFolder - * @param p_engineResourcesFolder + * @param p_projectAssetsPath + * @param p_engineAssetsPath */ LuaScriptEngine( - const std::filesystem::path& p_scriptsFolder, - const std::filesystem::path& p_engineResourcesFolder + const std::filesystem::path& p_projectAssetsPath, + const std::filesystem::path& p_engineAssetsPath ); /** diff --git a/Sources/OvCore/src/OvCore/Scripting/Lua/Bindings/LuaActorBindings.cpp b/Sources/OvCore/src/OvCore/Scripting/Lua/Bindings/LuaActorBindings.cpp index 5b32f8b3..f73015be 100644 --- a/Sources/OvCore/src/OvCore/Scripting/Lua/Bindings/LuaActorBindings.cpp +++ b/Sources/OvCore/src/OvCore/Scripting/Lua/Bindings/LuaActorBindings.cpp @@ -4,6 +4,8 @@ * @licence: MIT */ +#include + #include #include @@ -70,7 +72,29 @@ void BindLuaActor(sol::state& p_luaState) /* Behaviours relatives */ "GetBehaviour", [](Actor& p_this, const std::string& p_name) -> sol::table { - if (auto behaviour = p_this.GetBehaviour(p_name)) + // First try matching by script name (stem without path or extension) + OvCore::ECS::Components::Behaviour* behaviour = nullptr; + for (auto& [key, b] : p_this.GetBehaviours()) + { + if (std::filesystem::path(b.name).stem().string() == p_name) + { + behaviour = &b; + break; + } + } + + // Fall back to path-based match: try as-is, then with .lua appended if no extension given + if (!behaviour) + { + behaviour = p_this.GetBehaviour(p_name); + } + + if (!behaviour && std::filesystem::path(p_name).extension().empty()) + { + behaviour = p_this.GetBehaviour(p_name + ".lua"); + } + + if (behaviour) { if (auto script = behaviour->GetScript()) { diff --git a/Sources/OvCore/src/OvCore/Scripting/Lua/LuaScriptEngine.cpp b/Sources/OvCore/src/OvCore/Scripting/Lua/LuaScriptEngine.cpp index 31737b32..b84e10b0 100644 --- a/Sources/OvCore/src/OvCore/Scripting/Lua/LuaScriptEngine.cpp +++ b/Sources/OvCore/src/OvCore/Scripting/Lua/LuaScriptEngine.cpp @@ -129,23 +129,23 @@ namespace template<> OvCore::Scripting::LuaScriptEngineBase::TScriptEngine( - const std::filesystem::path& p_scriptRootFolder, - const std::filesystem::path& p_engineResourcesFolder + const std::filesystem::path& p_projectAssetsPath, + const std::filesystem::path& p_engineAssetsPath ) { - m_context.scriptRootFolder = p_scriptRootFolder; - m_context.engineResourcesFolder = p_engineResourcesFolder; + m_context.projectAssetsPath = p_projectAssetsPath; + m_context.engineAssetsPath = p_engineAssetsPath; } template<> OvCore::Scripting::LuaScriptEngineBase::~TScriptEngine() {} template<> -bool OvCore::Scripting::LuaScriptEngineBase::CreateProjectFiles(bool p_force) +bool OvCore::Scripting::LuaScriptEngineBase::CreateProjectFiles(const std::filesystem::path& p_projectFolder, bool p_force) { - // Create a .luarc.json file inside the project's script folder. + // Create a .luarc.json file at the root of the user's project. // This file will allow Lua LSPs to properly discover Lua symbols exposed by Overload. - const std::filesystem::path luarcPath = m_context.scriptRootFolder / ".luarc.json"; + const std::filesystem::path luarcPath = p_projectFolder / ".luarc.json"; // Prevent the .luarc.json from being overrided UNLESS p_force is used if (!p_force && std::filesystem::exists(luarcPath)) @@ -154,7 +154,7 @@ bool OvCore::Scripting::LuaScriptEngineBase::CreateProjectFiles(bool p_force) } std::ofstream luarc(luarcPath); - luarc << GetLuarcFileContent(m_context.engineResourcesFolder); + luarc << GetLuarcFileContent(m_context.engineAssetsPath); return true; } @@ -173,7 +173,19 @@ std::vector OvCore::Scripting::LuaScriptEngineBase::GetValidExtensi template<> std::string OvCore::Scripting::LuaScriptEngineBase::GetDefaultScriptContent(const std::string& p_name) { - return "---@class " + p_name + " : Behaviour\nlocal " + p_name + " =\n{\n}\n\nfunction " + p_name + ":OnStart()\nend\n\nfunction " + p_name + ":OnUpdate(deltaTime)\nend\n\nreturn " + p_name; + return + "---@class " + p_name + " : Behaviour\n" + "local " + p_name + " =\n" + "{\n" + "}\n" + "\n" + "function " + p_name + ":OnStart()\n" + "end\n" + "\n" + "function " + p_name + ":OnUpdate(deltaTime)\n" + "end\n" + "\n" + "return " + p_name; } template<> @@ -183,8 +195,7 @@ void OvCore::Scripting::LuaScriptEngineBase::AddBehaviour(OvCore::ECS::Component m_context.behaviours.push_back(std::ref(p_toAdd)); - const auto scriptFileName = p_toAdd.name + GetDefaultExtension(); - const auto scriptPath = m_context.scriptRootFolder / scriptFileName; + const auto scriptPath = m_context.projectAssetsPath / p_toAdd.name; if (!RegisterBehaviour(*m_context.luaState, p_toAdd, scriptPath.string())) { @@ -311,11 +322,11 @@ void OvCore::Scripting::LuaScriptEngineBase::OnTriggerExit(OvCore::ECS::Componen } OvCore::Scripting::LuaScriptEngine::LuaScriptEngine( - const std::filesystem::path& p_scriptsFolder, - const std::filesystem::path& p_engineResourcesFolder + const std::filesystem::path& p_projectAssetsPath, + const std::filesystem::path& p_engineAssetsPath ) : OvCore::Scripting::LuaScriptEngineBase( - p_scriptsFolder, - p_engineResourcesFolder + p_projectAssetsPath, + p_engineAssetsPath ) { CreateContext(); @@ -342,8 +353,7 @@ void OvCore::Scripting::LuaScriptEngine::CreateContext() std::for_each(m_context.behaviours.begin(), m_context.behaviours.end(), [this](std::reference_wrapper behaviour) { - const auto scriptFileName = behaviour.get().name + GetDefaultExtension(); - const auto scriptPath = m_context.scriptRootFolder / scriptFileName; + const auto scriptPath = m_context.projectAssetsPath / behaviour.get().name; if (!RegisterBehaviour(*m_context.luaState, behaviour.get(), scriptPath.string())) { ++m_context.errorCount; diff --git a/Sources/OvCore/src/OvCore/Scripting/Null/NullScriptEngine.cpp b/Sources/OvCore/src/OvCore/Scripting/Null/NullScriptEngine.cpp index 043b91e1..21589cd0 100644 --- a/Sources/OvCore/src/OvCore/Scripting/Null/NullScriptEngine.cpp +++ b/Sources/OvCore/src/OvCore/Scripting/Null/NullScriptEngine.cpp @@ -10,15 +10,15 @@ template<> OvCore::Scripting::NullScriptEngineBase::TScriptEngine( - const std::filesystem::path& p_scriptRootFolder, - const std::filesystem::path& p_engineResourcesFolder + const std::filesystem::path& p_projectAssetsPath, + const std::filesystem::path& p_engineAssetsPath ) {} template<> OvCore::Scripting::NullScriptEngineBase::~TScriptEngine() {} template<> -bool OvCore::Scripting::NullScriptEngineBase::CreateProjectFiles(bool p_force) { return true; } +bool OvCore::Scripting::NullScriptEngineBase::CreateProjectFiles(const std::filesystem::path& p_projectFolder, bool p_force) { return true; } template<> std::string OvCore::Scripting::NullScriptEngineBase::GetDefaultExtension() diff --git a/Sources/OvEditor/include/OvEditor/Core/Context.h b/Sources/OvEditor/include/OvEditor/Core/Context.h index ea10a7c1..af5e45fe 100644 --- a/Sources/OvEditor/include/OvEditor/Core/Context.h +++ b/Sources/OvEditor/include/OvEditor/Core/Context.h @@ -67,7 +67,6 @@ namespace OvEditor::Core const std::filesystem::path projectFile; const std::filesystem::path engineAssetsPath; const std::filesystem::path projectAssetsPath; - const std::filesystem::path projectScriptsPath; const std::filesystem::path editorAssetsPath; std::unique_ptr device; diff --git a/Sources/OvEditor/include/OvEditor/Core/EditorActions.h b/Sources/OvEditor/include/OvEditor/Core/EditorActions.h index 21468160..1eab35ee 100644 --- a/Sources/OvEditor/include/OvEditor/Core/EditorActions.h +++ b/Sources/OvEditor/include/OvEditor/Core/EditorActions.h @@ -295,11 +295,18 @@ namespace OvEditor::Core std::string GetResourcePath(const std::string& p_path, bool p_isFromEngine = false); /** - * Returns the script path of a file + * Returns the script path of a file (relative to projectAssetsPath, forward-slash separated) * @param p_path */ std::string GetScriptPath(const std::string& p_path); + /** + * Migrates scripts from a legacy Scripts/ folder into Assets/Scripts/. + * If a Scripts/ folder is found in the project root, prompts the user and + * moves it into Assets/, updating all scene files accordingly. + */ + void MigrateScripts(); + /** * Propagate the folder rename everywhere (Resource manager, scenes, materials...) * @param p_previousName @@ -313,13 +320,6 @@ namespace OvEditor::Core */ void PropagateFolderDestruction(std::string p_folderPath); - /** - * Propagate the script rename in scenes and inspector - * @param p_previousName - * @param p_newName - */ - void PropagateScriptRename(std::string p_previousName, std::string p_newName); - /** * Propagate the file rename everywhere it is used * @param p_previousName diff --git a/Sources/OvEditor/include/OvEditor/Panels/AssetBrowser.h b/Sources/OvEditor/include/OvEditor/Panels/AssetBrowser.h index 61774a80..0f18bdb5 100644 --- a/Sources/OvEditor/include/OvEditor/Panels/AssetBrowser.h +++ b/Sources/OvEditor/include/OvEditor/Panels/AssetBrowser.h @@ -51,8 +51,8 @@ namespace OvEditor::Panels void Refresh(); private: - void ParseFolder(OvUI::Widgets::Layout::TreeNode& p_root, const std::filesystem::directory_entry& p_directory, bool p_isEngineItem, bool p_scriptFolder = false); - void ConsiderItem(OvUI::Widgets::Layout::TreeNode* p_root, const std::filesystem::directory_entry& p_entry, bool p_isEngineItem, bool p_autoOpen = false, bool p_scriptFolder = false); + void ParseFolder(OvUI::Widgets::Layout::TreeNode& p_root, const std::filesystem::directory_entry& p_directory, bool p_isEngineItem); + void ConsiderItem(OvUI::Widgets::Layout::TreeNode* p_root, const std::filesystem::directory_entry& p_entry, bool p_isEngineItem, bool p_autoOpen = false); private: OvUI::Widgets::Layout::Group* m_assetList; diff --git a/Sources/OvEditor/src/OvEditor/Core/Context.cpp b/Sources/OvEditor/src/OvEditor/Core/Context.cpp index 1c5ac765..67d2b8e3 100644 --- a/Sources/OvEditor/src/OvEditor/Core/Context.cpp +++ b/Sources/OvEditor/src/OvEditor/Core/Context.cpp @@ -70,7 +70,6 @@ OvEditor::Core::Context::Context(const std::filesystem::path& p_projectFolder) : projectFile(Utils::ProjectManagement::GetProjectFile(p_projectFolder)), engineAssetsPath(std::filesystem::current_path() / "Data" / "Engine"), projectAssetsPath(projectFolder / "Assets"), - projectScriptsPath(projectFolder / "Scripts"), editorAssetsPath(std::filesystem::current_path() / "Data" / "Editor"), sceneManager(projectAssetsPath.string()), projectSettings(projectFile.string()) @@ -149,7 +148,7 @@ OvEditor::Core::Context::Context(const std::filesystem::path& p_projectFolder) : /* Scripting */ scriptEngine = std::make_unique( - projectScriptsPath, + projectAssetsPath, engineAssetsPath ); @@ -157,6 +156,7 @@ OvEditor::Core::Context::Context(const std::filesystem::path& p_projectFolder) : // If Overload's installation directory changes, references to engine symbols would be lost, // hence this invocation. scriptEngine->CreateProjectFiles( + projectFolder, Settings::EditorSettings::RegenerateScriptingProjectFilesOnStartup ); diff --git a/Sources/OvEditor/src/OvEditor/Core/EditorActions.cpp b/Sources/OvEditor/src/OvEditor/Core/EditorActions.cpp index 8b10e5ab..28a67712 100644 --- a/Sources/OvEditor/src/OvEditor/Core/EditorActions.cpp +++ b/Sources/OvEditor/src/OvEditor/Core/EditorActions.cpp @@ -283,36 +283,19 @@ void OvEditor::Core::EditorActions::BuildAtLocation(const std::string & p_config OVLOG_INFO("Data/User/Assets/ directory copied"); std::filesystem::copy( - m_context.projectScriptsPath, - p_buildPath / "Data" / "User" / "Scripts", + m_context.engineAssetsPath, + p_buildPath / "Data" / "Engine", std::filesystem::copy_options::recursive, err ); if (!err) { - OVLOG_INFO("Data/User/Scripts/ directory copied"); - - std::filesystem::copy( - m_context.engineAssetsPath, - p_buildPath / "Data" / "Engine", - std::filesystem::copy_options::recursive, - err - ); - - if (!err) - { - OVLOG_INFO("Data/Engine/ directory copied"); - } - else - { - OVLOG_ERROR("Data/Engine/ directory failed to copy"); - failed = true; - } + OVLOG_INFO("Data/Engine/ directory copied"); } else { - OVLOG_ERROR("Data/User/Scripts/ directory failed to copy"); + OVLOG_ERROR("Data/Engine/ directory failed to copy"); failed = true; } } @@ -751,7 +734,7 @@ void OvEditor::Core::EditorActions::SaveMaterials() void OvEditor::Core::EditorActions::RegenerateScriptingProjectFiles() { - if (m_context.scriptEngine->CreateProjectFiles(true)) + if (m_context.scriptEngine->CreateProjectFiles(m_context.projectFolder, true)) { OVLOG_INFO("Lua symbol regenerated (.luarc.json created)"); } @@ -800,14 +783,16 @@ bool OvEditor::Core::EditorActions::ImportAsset(const std::string& p_initialDest std::string shaderFormats = "*.ovfx;"; std::string shaderPartFormats = "*.ovfxh;"; std::string soundFormats = "*.mp3;*.ogg;*.wav;"; + std::string scriptFormats = "*.lua;"; OpenFileDialog selectAssetDialog("Select an asset to import"); - selectAssetDialog.AddFileType("Any supported format", modelFormats + textureFormats + shaderFormats + shaderPartFormats + soundFormats); + selectAssetDialog.AddFileType("Any supported format", modelFormats + textureFormats + shaderFormats + shaderPartFormats + soundFormats + scriptFormats); selectAssetDialog.AddFileType("Model (.fbx, .obj)", modelFormats); selectAssetDialog.AddFileType("Texture (.png, .jpeg, .jpg, .tga, .hdr)", textureFormats); selectAssetDialog.AddFileType("Shader (.ovfx)", shaderFormats); selectAssetDialog.AddFileType("Shader Parts (.ovfxh)", shaderPartFormats); selectAssetDialog.AddFileType("Sound (.mp3, .ogg, .wav)", soundFormats); + selectAssetDialog.AddFileType("Script (.lua)", scriptFormats); selectAssetDialog.Show(); if (selectAssetDialog.HasSucceeded()) @@ -846,14 +831,16 @@ bool OvEditor::Core::EditorActions::ImportAssetAtLocation(const std::string& p_d std::string shaderFormats = "*.ovfx;"; std::string shaderPartFormats = "*.ovfxh;"; std::string soundFormats = "*.mp3;*.ogg;*.wav;"; + std::string scriptFormats = "*.lua;"; OpenFileDialog selectAssetDialog("Select an asset to import"); - selectAssetDialog.AddFileType("Any supported format", modelFormats + textureFormats + shaderFormats + soundFormats); + selectAssetDialog.AddFileType("Any supported format", modelFormats + textureFormats + shaderFormats + soundFormats + scriptFormats); selectAssetDialog.AddFileType("Model (.fbx, .obj)", modelFormats); selectAssetDialog.AddFileType("Texture (.png, .jpeg, .jpg, .tga, .hdr)", textureFormats); selectAssetDialog.AddFileType("Shader (.ovfx)", shaderFormats); selectAssetDialog.AddFileType("Shader Parts (.ovfxh)", shaderPartFormats); selectAssetDialog.AddFileType("Sound (.mp3, .ogg, .wav)", soundFormats); + selectAssetDialog.AddFileType("Script (.lua)", scriptFormats); selectAssetDialog.Show(); if (selectAssetDialog.HasSucceeded()) @@ -915,20 +902,15 @@ std::string OvEditor::Core::EditorActions::GetScriptPath(const std::string & p_p { std::string result = p_path; - OvTools::Utils::String::Replace(result, m_context.projectScriptsPath.string(), ""); + OvTools::Utils::String::Replace(result, m_context.projectAssetsPath.string(), ""); if (result.starts_with(std::filesystem::path::preferred_separator)) { result = result.substr(1); } - for (auto& extension : OVSERVICE(OvCore::Scripting::ScriptEngine).GetValidExtensions()) - { - if (result.ends_with(extension)) - { - result = result.substr(0, result.size() - extension.size()); - } - } + // Normalize to forward slashes for cross-platform consistency + std::replace(result.begin(), result.end(), '\\', '/'); return result; } @@ -952,7 +934,10 @@ void OvEditor::Core::EditorActions::PropagateFolderRename(std::string p_previous previousFileName = p_previousName; } - PropagateFileRename(OvTools::Utils::PathParser::MakeWindowsStyle(previousFileName), OvTools::Utils::PathParser::MakeWindowsStyle(newFileName)); + PropagateFileRename( + OvTools::Utils::PathParser::MakeWindowsStyle(previousFileName), + OvTools::Utils::PathParser::MakeWindowsStyle(newFileName) + ); } } } @@ -968,19 +953,62 @@ void OvEditor::Core::EditorActions::PropagateFolderDestruction(std::string p_fol } } -void OvEditor::Core::EditorActions::PropagateScriptRename(std::string p_previousName, std::string p_newName) +void OvEditor::Core::EditorActions::MigrateScripts() { - p_previousName = GetScriptPath(p_previousName); - p_newName = GetScriptPath(p_newName); + const auto legacyScriptsPath = m_context.projectFolder / "Scripts"; + + if (!std::filesystem::exists(legacyScriptsPath) || !std::filesystem::is_directory(legacyScriptsPath)) + { + return; + } - if (auto currentScene = m_context.sceneManager.GetCurrentScene()) - for (auto actor : currentScene->GetActors()) - if (actor->RemoveBehaviour(p_previousName)) - actor->AddBehaviour(p_newName); + using namespace OvWindowing::Dialogs; - PropagateFileRenameThroughSavedFilesOfType(p_previousName, p_newName, OvTools::Utils::PathParser::EFileType::SCENE); + MessageBox message( + "Legacy Scripts/ folder detected", + "A \"Scripts/\" folder was found in your project directory.\n\n" + "Scripts are now stored inside \"Assets/\" and support subdirectories.\n\n" + "Would you like to migrate your scripts to \"Assets/Scripts/\"?\n" + "All scene files referencing these scripts will be updated automatically.", + MessageBox::EMessageType::WARNING, + MessageBox::EButtonLayout::YES_NO, + true + ); + + if (message.GetUserAction() != MessageBox::EUserAction::YES) + { + return; + } - EDITOR_PANEL(Panels::Inspector, "Inspector").Refresh(); + const auto targetPath = m_context.projectAssetsPath / "Scripts"; + + std::error_code err; + std::filesystem::rename(legacyScriptsPath, targetPath, err); + + if (err) + { + OVLOG_ERROR("Failed to migrate Scripts/ folder: " + err.message()); + return; + } + + OVLOG_INFO("Scripts/ folder migrated to Assets/Scripts/"); + + // Update all scene files: replace old behaviour type (just the stem) with the new relative path + for (const auto& entry : std::filesystem::recursive_directory_iterator(targetPath)) + { + if (!entry.is_directory()) + { + if (OvTools::Utils::PathParser::GetFileType(entry.path().string()) == OvTools::Utils::PathParser::EFileType::SCRIPT) + { + const auto stem = entry.path().stem().string(); + const auto newRelPath = (std::filesystem::path("Scripts") / entry.path().filename()).generic_string(); + + PropagateFileRenameThroughSavedFilesOfType(stem, newRelPath, OvTools::Utils::PathParser::EFileType::SCENE); + } + } + } + + OVLOG_INFO("Scene files updated with new script paths"); } void OvEditor::Core::EditorActions::PropagateFileRename(std::string p_previousName, std::string p_newName) @@ -1110,6 +1138,33 @@ void OvEditor::Core::EditorActions::PropagateFileRename(std::string p_previousNa switch (OvTools::Utils::PathParser::GetFileType(p_previousName)) { + case OvTools::Utils::PathParser::EFileType::SCRIPT: + { + // Normalize to forward slashes (Behaviour::name uses forward slashes as path separator) + std::string prev = p_previousName; + std::string next = p_newName; + std::replace(prev.begin(), prev.end(), '\\', '/'); + if (next != "?") std::replace(next.begin(), next.end(), '\\', '/'); + + if (auto currentScene = m_context.sceneManager.GetCurrentScene()) + { + for (auto actor : currentScene->GetActors()) + { + if (actor->RemoveBehaviour(prev) && next != "?") + { + actor->AddBehaviour(next); + } + } + } + + if (next != "?") + { + PropagateFileRenameThroughSavedFilesOfType(prev, next, OvTools::Utils::PathParser::EFileType::SCENE); + } + + EDITOR_PANEL(Panels::Inspector, "Inspector").Refresh(); + break; + } case OvTools::Utils::PathParser::EFileType::MATERIAL: PropagateFileRenameThroughSavedFilesOfType(p_previousName, p_newName, OvTools::Utils::PathParser::EFileType::SCENE); break; diff --git a/Sources/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp b/Sources/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp index cfa6f648..7a72c686 100644 --- a/Sources/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp +++ b/Sources/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp @@ -260,6 +260,17 @@ namespace public: FolderContextualMenu(const std::string& p_filePath, bool p_protected = false) : BrowserItemContextualMenu(p_filePath, p_protected) {} + void CreateScript(const std::string& p_name, const std::string& p_path) + { + const std::string fileContent = EDITOR_CONTEXT(scriptEngine)->GetDefaultScriptContent(p_name); + + std::ofstream outfile(p_path); + outfile << fileContent << std::endl; + + ItemAddedEvent.Invoke(p_path); + Close(); + } + void CreateNewShader(const std::string& p_shaderName, std::optional p_type) { const auto finalPath = FindAvailableFilePath(filePath / (p_shaderName + ".ovfx")); @@ -378,6 +389,7 @@ namespace auto& createFolderMenu = createMenu.CreateWidget("Folder"); auto& createSceneMenu = createMenu.CreateWidget("Scene"); + auto& createScriptMenu = createMenu.CreateWidget("Script"); auto& createShaderMenu = createMenu.CreateWidget("Shader"); auto& createMaterialMenu = createMenu.CreateWidget("Material"); @@ -396,6 +408,7 @@ namespace auto& createFolder = createFolderMenu.CreateWidget(""); auto& createScene = createSceneMenu.CreateWidget(""); + auto& createScript = createScriptMenu.CreateWidget(""); auto& createEmptyMaterial = createEmptyMaterialMenu.CreateWidget(""); auto& createStandardMaterial = createStandardMaterialMenu.CreateWidget(""); @@ -412,6 +425,7 @@ namespace createFolderMenu.ClickedEvent += [&createFolder] { createFolder.content = ""; }; createSceneMenu.ClickedEvent += [&createScene] { createScene.content = ""; }; + createScriptMenu.ClickedEvent += [&createScript] { createScript.content = ""; }; createStandardShaderMenu.ClickedEvent += [&createStandardShader] { createStandardShader.content = ""; }; createUnlitShaderMenu.ClickedEvent += [&createUnlitShader] { createUnlitShader.content = ""; }; createSkysphereShaderMenu.ClickedEvent += [&createSkysphereShader] { createSkysphereShader.content = ""; }; @@ -455,6 +469,20 @@ namespace Close(); }; + createScript.EnterPressedEvent += [this](std::string p_newName) { + std::erase_if(p_newName, [](char c) { + return std::find(kAllowedFilenameChars.begin(), kAllowedFilenameChars.end(), c) == kAllowedFilenameChars.end(); + }); + + const auto extension = EDITOR_CONTEXT(scriptEngine)->GetDefaultExtension(); + const auto newPath = FindAvailableFilePath(filePath / (p_newName + extension)); + + if (!p_newName.empty()) + { + CreateScript(p_newName, newPath.string()); + } + }; + CreateNewShaderCallback(createEmptyShader); CreateNewShaderCallback(createStandardShader, "Standard"); CreateNewShaderCallback(createUnlitShader, "Unlit"); @@ -515,50 +543,6 @@ namespace OvTools::Eventing::Event ItemAddedEvent; }; - class ScriptFolderContextualMenu : public FolderContextualMenu - { - public: - ScriptFolderContextualMenu(const std::string& p_filePath, bool p_protected = false) : FolderContextualMenu(p_filePath, p_protected) {} - - void CreateScript(const std::string& p_name, const std::string& p_path) - { - const std::string fileContent = EDITOR_CONTEXT(scriptEngine)->GetDefaultScriptContent(p_name); - - std::ofstream outfile(p_path); - outfile << fileContent << std::endl; - - ItemAddedEvent.Invoke(p_path); - Close(); - } - - virtual void CreateList() override - { - FolderContextualMenu::CreateList(); - - auto& newScriptMenu = CreateWidget("New script..."); - auto& nameEditor = newScriptMenu.CreateWidget(""); - - newScriptMenu.ClickedEvent += [this, &nameEditor] { - nameEditor.content = OvTools::Utils::PathParser::GetElementName(""); - }; - - nameEditor.EnterPressedEvent += [this](std::string p_newName) { - // Clean the name (Remove special chars) - std::erase_if(p_newName, [](char c) { - return std::find(kAllowedFilenameChars.begin(), kAllowedFilenameChars.end(), c) == kAllowedFilenameChars.end(); - }); - - const auto extension = EDITOR_CONTEXT(scriptEngine)->GetDefaultExtension(); - const auto newPath = filePath / (p_newName + extension); - - if (!std::filesystem::exists(newPath)) - { - CreateScript(p_newName, newPath.string()); - } - }; - } - }; - class FileContextualMenu : public BrowserItemContextualMenu { public: @@ -877,15 +861,7 @@ OvEditor::Panels::AssetBrowser::AssetBrowser ); } - if (std::filesystem::create_directories(EDITOR_CONTEXT(projectScriptsPath))) - { - MessageBox message( - "Scripts folder not found", - "The \"Scripts/\" folders hasn't been found in your project directory.\nIt has been automatically generated", - MessageBox::EMessageType::WARNING, - MessageBox::EButtonLayout::OK - ); - } + EDITOR_EXEC(MigrateScripts()); auto& refreshButton = CreateWidget("Refresh"); refreshButton.ClickedEvent += std::bind(&AssetBrowser::Refresh, this); @@ -898,7 +874,7 @@ OvEditor::Panels::AssetBrowser::AssetBrowser importButton.lineBreak = false; auto& codeEditorButton = CreateWidget("Edit Scripts"); - codeEditorButton.ClickedEvent += [this] { EDITOR_EXEC(OpenInCodeEditor(EDITOR_CONTEXT(projectScriptsPath))); }; + codeEditorButton.ClickedEvent += [this] { EDITOR_EXEC(OpenInCodeEditor(EDITOR_CONTEXT(projectFolder))); }; codeEditorButton.idleBackgroundColor = { 0.1f, 0.3f, 0.7f }; m_assetList = &CreateWidget(); @@ -912,8 +888,6 @@ void OvEditor::Panels::AssetBrowser::Fill() ConsiderItem(nullptr, std::filesystem::directory_entry(EDITOR_CONTEXT(engineAssetsPath)), true); m_assetList->CreateWidget(); ConsiderItem(nullptr, std::filesystem::directory_entry(EDITOR_CONTEXT(projectAssetsPath)), false); - m_assetList->CreateWidget(); - ConsiderItem(nullptr, std::filesystem::directory_entry(EDITOR_CONTEXT(projectScriptsPath)), false, false, true); } void OvEditor::Panels::AssetBrowser::Clear() @@ -927,7 +901,7 @@ void OvEditor::Panels::AssetBrowser::Refresh() Fill(); } -void OvEditor::Panels::AssetBrowser::ParseFolder(Layout::TreeNode& p_root, const std::filesystem::directory_entry& p_directory, bool p_isEngineItem, bool p_scriptFolder) +void OvEditor::Panels::AssetBrowser::ParseFolder(Layout::TreeNode& p_root, const std::filesystem::directory_entry& p_directory, bool p_isEngineItem) { // Collect all entries first std::vector entries; @@ -946,7 +920,7 @@ void OvEditor::Panels::AssetBrowser::ParseFolder(Layout::TreeNode& p_root, const { if (item.is_directory()) { - ConsiderItem(&p_root, item, p_isEngineItem, false, p_scriptFolder); + ConsiderItem(&p_root, item, p_isEngineItem); } } @@ -955,12 +929,12 @@ void OvEditor::Panels::AssetBrowser::ParseFolder(Layout::TreeNode& p_root, const { if (!item.is_directory()) { - ConsiderItem(&p_root, item, p_isEngineItem, false, p_scriptFolder); + ConsiderItem(&p_root, item, p_isEngineItem); } } } -void OvEditor::Panels::AssetBrowser::ConsiderItem(OvUI::Widgets::Layout::TreeNode* p_root, const std::filesystem::directory_entry& p_entry, bool p_isEngineItem, bool p_autoOpen, bool p_scriptFolder) +void OvEditor::Panels::AssetBrowser::ConsiderItem(OvUI::Widgets::Layout::TreeNode* p_root, const std::filesystem::directory_entry& p_entry, bool p_isEngineItem, bool p_autoOpen) { const bool isDirectory = p_entry.is_directory(); const std::string itemname = OvTools::Utils::PathParser::GetElementName(p_entry.path().string()); @@ -997,28 +971,25 @@ void OvEditor::Panels::AssetBrowser::ConsiderItem(OvUI::Widgets::Layout::TreeNod auto& ddSource = treeNode.AddPlugin>>("Folder", resourceFormatPath, std::make_pair(resourceFormatPath, &itemGroup)); - if (!p_root || p_scriptFolder) + if (!p_root) { treeNode.RemoveAllPlugins(); } - auto& contextMenu = !p_scriptFolder ? treeNode.AddPlugin(path, protectedItem && resourceFormatPath != "") : treeNode.AddPlugin(path, protectedItem && resourceFormatPath != ""); + auto& contextMenu = treeNode.AddPlugin(path, protectedItem && resourceFormatPath != ""); contextMenu.userData = static_cast(&treeNode); - contextMenu.ItemAddedEvent += [this, &treeNode, p_isEngineItem, p_scriptFolder] (std::filesystem::path p_path) { + contextMenu.ItemAddedEvent += [this, &treeNode, p_isEngineItem] (std::filesystem::path p_path) { treeNode.Open(); treeNode.RemoveAllWidgets(); ParseFolder( treeNode, std::filesystem::directory_entry(p_path.parent_path()), - p_isEngineItem, - p_scriptFolder + p_isEngineItem ); }; - if (!p_scriptFolder) - { - if (!p_isEngineItem) /* Prevent engine item from being DDTarget (Can't Drag and drop to engine folder) */ + if (!p_isEngineItem) /* Prevent engine item from being DDTarget (Can't Drag and drop to engine folder) */ { treeNode.AddPlugin>>("Folder").DataReceivedEvent += [this, &treeNode, path, p_isEngineItem](std::pair p_data) { @@ -1154,15 +1125,13 @@ void OvEditor::Panels::AssetBrowser::ConsiderItem(OvUI::Widgets::Layout::TreeNod p_isEngineItem ); }; - - } contextMenu.CreateList(); - treeNode.OpenedEvent += [this, &treeNode, path, p_isEngineItem, p_scriptFolder] { + treeNode.OpenedEvent += [this, &treeNode, path, p_isEngineItem] { treeNode.RemoveAllWidgets(); std::filesystem::path updatedPath = std::filesystem::path{path}.parent_path() / treeNode.name; - ParseFolder(treeNode, std::filesystem::directory_entry(updatedPath), p_isEngineItem, p_scriptFolder); + ParseFolder(treeNode, std::filesystem::directory_entry(updatedPath), p_isEngineItem); }; treeNode.ClosedEvent += [this, &treeNode] { @@ -1197,7 +1166,7 @@ void OvEditor::Panels::AssetBrowser::ConsiderItem(OvUI::Widgets::Layout::TreeNod std::make_pair(resourceFormatPath, &itemGroup) ); - contextMenu.RenamedEvent += [&ddSource, &clickableText, p_scriptFolder]( + contextMenu.RenamedEvent += [&ddSource, &clickableText, fileType]( std::filesystem::path p_prev, std::filesystem::path p_newPath ) { @@ -1210,19 +1179,15 @@ void OvEditor::Panels::AssetBrowser::ConsiderItem(OvUI::Widgets::Layout::TreeNod ddSource.data.first = (std::filesystem::path{ ddSource.data.first }.parent_path() / elementName).string(); ddSource.tooltip = ddSource.data.first; - if (!p_scriptFolder) - { - EDITOR_EXEC(PropagateFileRename(p_prev.string(), p_newPath.string())); + EDITOR_EXEC(PropagateFileRename(p_prev.string(), p_newPath.string())); + if (fileType != OvTools::Utils::PathParser::EFileType::SCRIPT) + { if (EDITOR_CONTEXT(sceneManager).GetCurrentSceneSourcePath() == p_prev) // Modify current scene source path if the renamed file is the current scene { EDITOR_CONTEXT(sceneManager).StoreCurrentSceneSourcePath(p_newPath.string()); } } - else - { - EDITOR_EXEC(PropagateScriptRename(p_prev.string(), p_newPath.string())); - } clickableText.content = elementName.string(); } @@ -1241,7 +1206,7 @@ void OvEditor::Panels::AssetBrowser::ConsiderItem(OvUI::Widgets::Layout::TreeNod }; contextMenu.DuplicateEvent += [this, &clickableText, p_root, p_isEngineItem] (std::filesystem::path newItem) { - EDITOR_EXEC(DelayAction(std::bind(&AssetBrowser::ConsiderItem, this, p_root, std::filesystem::directory_entry{ newItem }, p_isEngineItem, false, false), 0)); + EDITOR_EXEC(DelayAction(std::bind(&AssetBrowser::ConsiderItem, this, p_root, std::filesystem::directory_entry{ newItem }, p_isEngineItem, false), 0)); }; if (fileType == OvTools::Utils::PathParser::EFileType::SOUND || diff --git a/Sources/OvEditor/src/OvEditor/Panels/Inspector.cpp b/Sources/OvEditor/src/OvEditor/Panels/Inspector.cpp index 16c60db6..66942cff 100644 --- a/Sources/OvEditor/src/OvEditor/Panels/Inspector.cpp +++ b/Sources/OvEditor/src/OvEditor/Panels/Inspector.cpp @@ -4,6 +4,7 @@ * @licence: MIT */ +#include #include #include @@ -295,11 +296,7 @@ void OvEditor::Panels::Inspector::_DrawAddScriptSection() _UpdateAddScriptButton(); m_addScriptButton->ClickedEvent += [this] { - const std::string defaultScriptExtension = OVSERVICE(OvCore::Scripting::ScriptEngine).GetDefaultExtension(); - - const auto realScriptPath = - EDITOR_CONTEXT(projectScriptsPath) / - std::format("{}{}", m_selectedScript, defaultScriptExtension); + const auto realScriptPath = EDITOR_CONTEXT(projectAssetsPath) / m_selectedScript; // Ensure that the script is a valid one if (std::filesystem::exists(realScriptPath)) @@ -329,7 +326,7 @@ void OvEditor::Panels::Inspector::_DrawComponent(AComponent& p_component) void OvEditor::Panels::Inspector::_DrawBehaviour(Behaviour& p_behaviour) { - auto& header = m_content->CreateWidget(p_behaviour.name); + auto& header = m_content->CreateWidget(std::filesystem::path(p_behaviour.name).replace_extension().string()); header.closable = true; header.CloseEvent += [&p_behaviour] { p_behaviour.owner.RemoveBehaviour(p_behaviour); @@ -358,11 +355,7 @@ void OvEditor::Panels::Inspector::_UpdateAddScriptButton() { OVASSERT(m_addScriptButton.has_value(), "Add script button not set"); - const std::string defaultScriptExtension = OVSERVICE(OvCore::Scripting::ScriptEngine).GetDefaultExtension(); - - const auto realScriptPath = - EDITOR_CONTEXT(projectScriptsPath) / - std::format("{}{}", m_selectedScript, defaultScriptExtension); + const auto realScriptPath = EDITOR_CONTEXT(projectAssetsPath) / m_selectedScript; const bool canAdd = std::filesystem::exists(realScriptPath) && diff --git a/Sources/OvGame/include/OvGame/Core/Context.h b/Sources/OvGame/include/OvGame/Core/Context.h index ec71a080..898b6009 100644 --- a/Sources/OvGame/include/OvGame/Core/Context.h +++ b/Sources/OvGame/include/OvGame/Core/Context.h @@ -50,7 +50,6 @@ namespace OvGame::Core public: const std::filesystem::path engineAssetsPath; const std::filesystem::path projectAssetsPath; - const std::filesystem::path projectScriptsPath; std::unique_ptr device; std::unique_ptr window; diff --git a/Sources/OvGame/src/OvGame/Core/Context.cpp b/Sources/OvGame/src/OvGame/Core/Context.cpp index 7078c633..bb797ce5 100644 --- a/Sources/OvGame/src/OvGame/Core/Context.cpp +++ b/Sources/OvGame/src/OvGame/Core/Context.cpp @@ -49,7 +49,6 @@ std::array CalculateOptimalWindowSizeAndPosition( OvGame::Core::Context::Context() : engineAssetsPath(std::filesystem::current_path() / "Data" / "Engine"), projectAssetsPath(std::filesystem::current_path() / "Data" / "User" / "Assets"), - projectScriptsPath(std::filesystem::current_path() / "Data" / "User" / "Scripts"), projectSettings((std::filesystem::current_path() / "Data" / "User" / "Game.ini").string()), sceneManager(projectAssetsPath.string()) { @@ -129,7 +128,7 @@ OvGame::Core::Context::Context() : /* Scripting */ scriptEngine = std::make_unique( - projectScriptsPath, + projectAssetsPath, engineAssetsPath );