diff --git a/Sources/Overload/OvEditor/include/OvEditor/Core/EditorActions.h b/Sources/Overload/OvEditor/include/OvEditor/Core/EditorActions.h index 9894afaf3..d0dbac37f 100644 --- a/Sources/Overload/OvEditor/include/OvEditor/Core/EditorActions.h +++ b/Sources/Overload/OvEditor/include/OvEditor/Core/EditorActions.h @@ -69,6 +69,23 @@ namespace OvEditor::Core */ void ResetLayout(); + /** + * Save the editor layout to the given configuration file + * @param p_filePath + */ + void SaveLayout(const std::filesystem::path& p_filePath); + + /** + * Save the current editor layout to the last used configuration file + */ + void SaveCurrentLayout(); + + /** + * Set and load the editor layout from the given configuration file + * @param p_filePath + */ + void SetLayout(const std::filesystem::path& p_filePath); + /** * Defines the scene view camera speed * @param p_speed diff --git a/Sources/Overload/OvEditor/include/OvEditor/Settings/EditorSettings.h b/Sources/Overload/OvEditor/include/OvEditor/Settings/EditorSettings.h index 5dadb5a3d..4a1b49df8 100644 --- a/Sources/Overload/OvEditor/include/OvEditor/Settings/EditorSettings.h +++ b/Sources/Overload/OvEditor/include/OvEditor/Settings/EditorSettings.h @@ -94,5 +94,6 @@ namespace OvEditor::Settings inline static Property RotationSnapUnit = { 15.0f }; inline static Property ScalingSnapUnit = { 1.0f }; inline static Property ColorTheme = { static_cast(OvUI::Styling::EStyle::DEFAULT_DARK) }; + inline static Property LatestLayout = { "" }; }; } diff --git a/Sources/Overload/OvEditor/src/OvEditor/Core/Context.cpp b/Sources/Overload/OvEditor/src/OvEditor/Core/Context.cpp index 90bd58fc2..9360dbd39 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Core/Context.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Core/Context.cpp @@ -127,7 +127,7 @@ OvEditor::Core::Context::Context(const std::string& p_projectPath, const std::st uiManager->EnableDocking(true); if (!std::filesystem::exists(OvTools::Utils::SystemCalls::GetPathToAppdata() + "\\OverloadTech\\OvEditor\\layout.ini")) - uiManager->ResetLayout("Config\\layout.ini"); + uiManager->ResetToDefaultLayout(); /* Audio */ audioEngine = std::make_unique(projectAssetsPath); diff --git a/Sources/Overload/OvEditor/src/OvEditor/Core/Editor.cpp b/Sources/Overload/OvEditor/src/OvEditor/Core/Editor.cpp index 19ec816a7..58a285985 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Core/Editor.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Core/Editor.cpp @@ -76,6 +76,11 @@ void OvEditor::Core::Editor::SetupUI() m_canvas.MakeDockspace(true); m_context.uiManager->SetCanvas(m_canvas); + + if (!Settings::EditorSettings::LatestLayout.Get().empty()) + { + m_context.uiManager->SetIniLayout(Settings::EditorSettings::LatestLayout.Get()); + } } void OvEditor::Core::Editor::PreUpdate() diff --git a/Sources/Overload/OvEditor/src/OvEditor/Core/EditorActions.cpp b/Sources/Overload/OvEditor/src/OvEditor/Core/EditorActions.cpp index 46b6daf10..f711a7519 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Core/EditorActions.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Core/EditorActions.cpp @@ -374,7 +374,22 @@ void OvEditor::Core::EditorActions::SetActorSpawnMode(EActorSpawnMode p_value) void OvEditor::Core::EditorActions::ResetLayout() { - DelayAction([this]() {m_context.uiManager->ResetLayout("Config\\layout.ini"); }); + DelayAction([this]() {m_context.uiManager->ResetToDefaultLayout(); }); +} + +void OvEditor::Core::EditorActions::SaveLayout(const std::filesystem::path& p_filePath) +{ + DelayAction([&]() {m_context.uiManager->SaveLayout(p_filePath); }); +} + +void OvEditor::Core::EditorActions::SaveCurrentLayout() +{ + DelayAction([&]() {m_context.uiManager->SaveCurrentLayout(); }); +} + +void OvEditor::Core::EditorActions::SetLayout(const std::filesystem::path& p_filePath) +{ + DelayAction([&]() {m_context.uiManager->SetLayout(p_filePath); }); } void OvEditor::Core::EditorActions::SetSceneViewCameraSpeed(int p_speed) diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/MenuBar.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/MenuBar.cpp index 205584361..aa91864b8 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/MenuBar.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/MenuBar.cpp @@ -4,6 +4,8 @@ * @licence: MIT */ +#include + #include #include @@ -31,6 +33,7 @@ #include "OvEditor/Core/EditorActions.h" #include "OvEditor/Settings/EditorSettings.h" #include "OvEditor/Utils/ActorCreationMenu.h" +#include "OvUI/Plugins/ContextualMenu.h" using namespace OvUI::Panels; using namespace OvUI::Widgets; @@ -216,8 +219,90 @@ void OvEditor::Panels::MenuBar::CreateSettingsMenu() void OvEditor::Panels::MenuBar::CreateLayoutMenu() { - auto& layoutMenu = CreateWidget("Layout"); - layoutMenu.CreateWidget("Reset").ClickedEvent += EDITOR_BIND(ResetLayout); + auto& layoutMenuList = CreateWidget("Layout"); + + auto& uiManager = *EDITOR_CONTEXT(uiManager); + const std::filesystem::path& layoutsPath = uiManager.GetLayoutsPath(); + + auto& saveMenuItem = layoutMenuList.CreateWidget("Save"); + saveMenuItem.ClickedEvent += EDITOR_BIND(SaveCurrentLayout); + + auto& saveNewMenuList = layoutMenuList.CreateWidget("Save New"); + auto& layoutInputText = saveNewMenuList.CreateWidget("Layout Name"); + layoutInputText.selectAllOnClick = true; + layoutInputText.EnterPressedEvent += [&layoutsPath](std::string p_input) + { + if (p_input.empty()) + return; + + auto& uiManager = *EDITOR_CONTEXT(uiManager); + EDITOR_EXEC(DelayAction(std::bind(&OvUI::Core::UIManager::SaveLayout, &uiManager, layoutsPath / (p_input + ".ini")), 1)); + }; + + auto& loadMenuList = layoutMenuList.CreateWidget("Load"); + + loadMenuList.ClickedEvent += [&] + { + loadMenuList.RemoveAllWidgets(); + + for (const auto& entry : std::filesystem::directory_iterator(layoutsPath)) + { + if (entry.is_regular_file() && entry.path().extension() == ".ini") + { + auto& layoutMenuItem = loadMenuList.CreateWidget(entry.path().stem().string()); + layoutMenuItem.name = entry.path().stem().string(); + + std::shared_ptr currentPath = std::make_shared(entry.path()); + + layoutMenuItem.ClickedEvent += [currentPath] + { + auto& uiManager = *EDITOR_CONTEXT(uiManager); + EDITOR_EXEC(DelayAction(std::bind(&OvUI::Core::UIManager::SetLayout, &uiManager, *currentPath), 1)); + + Settings::EditorSettings::LatestLayout = currentPath->stem().string(); + }; + + auto& contextualMenu = layoutMenuItem.AddPlugin(); + auto& deleteMenuItem = contextualMenu.CreateWidget("Delete"); + + deleteMenuItem.ClickedEvent += [currentPath, &layoutMenuItem] + { + auto& uiManager = *EDITOR_CONTEXT(uiManager); + EDITOR_EXEC(DelayAction(std::bind(&OvUI::Core::UIManager::DeleteLayout, &uiManager, *currentPath), 1)); + layoutMenuItem.enabled = false; + + }; + auto& renameToMenuList = contextualMenu.CreateWidget("Rename to..."); + + auto& renameInputText = renameToMenuList.CreateWidget(""); + renameInputText.content = entry.path().stem().string(); + renameInputText.selectAllOnClick = true; + + renameInputText.EnterPressedEvent += [currentPath, &layoutMenuItem, &layoutsPath, &contextualMenu](std::string p_newName) + { + if (p_newName.empty()) + return; + + layoutMenuItem.name = p_newName; + + auto& uiManager = *EDITOR_CONTEXT(uiManager); + std::filesystem::path newPath = layoutsPath / (p_newName + ".ini"); + EDITOR_EXEC(DelayAction(std::bind(&OvUI::Core::UIManager::RenameLayout, &uiManager, *currentPath, newPath), 1)); + + if (Settings::EditorSettings::LatestLayout.Get() == currentPath->stem().string()) + { + Settings::EditorSettings::LatestLayout = newPath.stem().string(); + } + + *currentPath = newPath; + + contextualMenu.Close(); + }; + } + } + }; + + layoutMenuList.CreateWidget("Reset").ClickedEvent += EDITOR_BIND(ResetLayout); } void OvEditor::Panels::MenuBar::CreateHelpMenu() diff --git a/Sources/Overload/OvEditor/src/OvEditor/Settings/EditorSettings.cpp b/Sources/Overload/OvEditor/src/OvEditor/Settings/EditorSettings.cpp index 8e7367de3..65cd866c4 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Settings/EditorSettings.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Settings/EditorSettings.cpp @@ -43,6 +43,7 @@ void OvEditor::Settings::EditorSettings::Save() iniFile.Add("rotation_snap_unit", RotationSnapUnit.Get()); iniFile.Add("scaling_snap_unit", ScalingSnapUnit.Get()); iniFile.Add("color_theme", ColorTheme.Get()); + iniFile.Add("latest_layout", LatestLayout.Get()); iniFile.Rewrite(); } @@ -59,4 +60,5 @@ void OvEditor::Settings::EditorSettings::Load() LoadIniEntry(iniFile, "rotation_snap_unit", RotationSnapUnit); LoadIniEntry(iniFile, "scaling_snap_unit", ScalingSnapUnit); LoadIniEntry(iniFile, "color_theme", ColorTheme); + LoadIniEntry(iniFile, "latest_layout", LatestLayout); } diff --git a/Sources/Overload/OvUI/include/OvUI/Core/UIManager.h b/Sources/Overload/OvUI/include/OvUI/Core/UIManager.h index 1c4e44d91..f4272c360 100644 --- a/Sources/Overload/OvUI/include/OvUI/Core/UIManager.h +++ b/Sources/Overload/OvUI/include/OvUI/Core/UIManager.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include @@ -77,7 +78,7 @@ namespace OvUI::Core /** * Defines a filename for the editor layout save file */ - void SetEditorLayoutSaveFilename(const std::string& p_filename); + void SetEditorLayoutSaveFilename(const std::filesystem::path& p_filePath); /** * Defines a frequency (in seconds) for the auto saving system of the editor layout @@ -96,11 +97,53 @@ namespace OvUI::Core */ void EnableDocking(bool p_value); - /** - * Reset the UI layout to the given configuration file - * @param p_config - */ - void ResetLayout(const std::string & p_config) const; + /** + * Reset the UI layout to the default configuration file + * @param p_config + */ + void ResetToDefaultLayout() const; + + /** + * Reset the UI layout to the given configuration file + * @param p_config + */ + void ResetLayout(const std::string& p_config) const; + + /** + * Save the UI layout to the given configuration file + * @param p_filePath + */ + void SaveLayout(const std::filesystem::path& p_filePath); + + /** + * Save the current UI layout to the last used configuration file + */ + void SaveCurrentLayout(); + + /** + * Set and load the UI ini layout from the given file name + * @param p_fileName + */ + void SetIniLayout(const std::string& p_fileName); + + /** + * Set and load the UI layout from the given configuration file + * @param p_filePath + */ + void SetLayout(const std::filesystem::path& p_filePath); + + /** + * Delete the UI layout configuration file + * @param p_filePath + */ + void DeleteLayout(const std::filesystem::path& p_filePath); + + /** + * Rename a UI layout configuration file + * @param p_filePath + * @param p_newFilePath + */ + void RenameLayout(const std::filesystem::path& p_filePath, const std::filesystem::path& p_newFilePath); /** * Return true if the docking system is enabled @@ -124,6 +167,8 @@ namespace OvUI::Core */ void Render(); + const std::filesystem::path& GetLayoutsPath() const; + private: void PushCurrentFont(); void PopCurrentFont(); @@ -133,5 +178,7 @@ namespace OvUI::Core Modules::Canvas* m_currentCanvas = nullptr; std::unordered_map m_fonts; std::string m_layoutSaveFilename = "imgui.ini"; + const std::filesystem::path m_defaultLayout; + const std::filesystem::path m_layoutsPath; }; } \ No newline at end of file diff --git a/Sources/Overload/OvUI/src/OvUI/Core/UIManager.cpp b/Sources/Overload/OvUI/src/OvUI/Core/UIManager.cpp index c2d757c7a..e59fb04cb 100644 --- a/Sources/Overload/OvUI/src/OvUI/Core/UIManager.cpp +++ b/Sources/Overload/OvUI/src/OvUI/Core/UIManager.cpp @@ -28,7 +28,13 @@ namespace } } -OvUI::Core::UIManager::UIManager(GLFWwindow* p_glfwWindow, Styling::EStyle p_style, std::string_view p_glslVersion) +#include + +#include + +OvUI::Core::UIManager::UIManager(GLFWwindow* p_glfwWindow, Styling::EStyle p_style, std::string_view p_glslVersion) : +m_defaultLayout{ std::filesystem::path{} / "Config" / "layout.ini" }, +m_layoutsPath(std::filesystem::path { OvTools::Utils::SystemCalls::GetPathToAppdata() } / "OverloadTech" / "OvEditor" / "Layouts") { ImGui::CreateContext(); @@ -39,6 +45,8 @@ OvUI::Core::UIManager::UIManager(GLFWwindow* p_glfwWindow, Styling::EStyle p_sty ImGui_ImplGlfw_InitForOpenGL(p_glfwWindow, true); ImGui_ImplOpenGL3_Init(p_glslVersion.data()); + + std::filesystem::create_directories(m_layoutsPath); } OvUI::Core::UIManager::~UIManager() @@ -112,9 +120,10 @@ bool OvUI::Core::UIManager::IsEditorLayoutSaveEnabled() const return ImGui::GetIO().IniFilename != nullptr; } -void OvUI::Core::UIManager::SetEditorLayoutSaveFilename(const std::string & p_filename) +void OvUI::Core::UIManager::SetEditorLayoutSaveFilename(const std::filesystem::path& p_filePath) { - m_layoutSaveFilename = p_filename; + m_layoutSaveFilename = p_filePath.string(); + if (IsEditorLayoutSaveEnabled()) ImGui::GetIO().IniFilename = m_layoutSaveFilename.c_str(); } @@ -139,9 +148,63 @@ void OvUI::Core::UIManager::EnableDocking(bool p_value) ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_DockingEnable; } +void OvUI::Core::UIManager::ResetToDefaultLayout() const +{ + ImGui::LoadIniSettingsFromDisk(m_defaultLayout.string().c_str()); +} + void OvUI::Core::UIManager::ResetLayout(const std::string& p_config) const { - ImGui::LoadIniSettingsFromDisk(p_config.c_str()); + ImGui::LoadIniSettingsFromDisk(p_config.c_str()); +} + +void OvUI::Core::UIManager::SaveLayout(const std::filesystem::path& p_filePath) +{ + SetEditorLayoutSaveFilename(p_filePath); + + ImGui::SaveIniSettingsToDisk(m_layoutSaveFilename.c_str()); +} + +void OvUI::Core::UIManager::SaveCurrentLayout() +{ + if (!std::filesystem::exists(m_layoutSaveFilename)) + { + auto path = m_layoutsPath / "layout.ini"; + m_layoutSaveFilename = path.string(); + + SetEditorLayoutSaveFilename(path); + } + ImGui::SaveIniSettingsToDisk(m_layoutSaveFilename.c_str()); +} + +void OvUI::Core::UIManager::SetIniLayout(const std::string& p_fileName) +{ + auto iniLayoutPath = m_layoutsPath / (p_fileName + ".ini"); + + if(std::filesystem::exists(iniLayoutPath)) + SetLayout(iniLayoutPath); +} + +void OvUI::Core::UIManager::SetLayout(const std::filesystem::path& p_filePath) +{ + SetEditorLayoutSaveFilename(p_filePath); + + ImGui::LoadIniSettingsFromDisk(p_filePath.string().c_str()); +} + +void OvUI::Core::UIManager::DeleteLayout(const std::filesystem::path& p_filePath) +{ + std::filesystem::remove(p_filePath); +} + +void OvUI::Core::UIManager::RenameLayout(const std::filesystem::path& p_filePath, const std::filesystem::path& p_newFilePath) +{ + std::filesystem::rename(p_filePath, p_newFilePath); + + if (m_layoutSaveFilename == p_filePath) + { + SetEditorLayoutSaveFilename(p_newFilePath); + } } bool OvUI::Core::UIManager::IsDockingEnabled() const @@ -170,6 +233,11 @@ void OvUI::Core::UIManager::Render() } } +const std::filesystem::path& OvUI::Core::UIManager::GetLayoutsPath() const +{ + return m_layoutsPath; +} + void OvUI::Core::UIManager::PushCurrentFont() { @@ -178,4 +246,4 @@ void OvUI::Core::UIManager::PushCurrentFont() void OvUI::Core::UIManager::PopCurrentFont() { -} \ No newline at end of file +}