Skip to content

Editor Layouts Management #431

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Sources/Overload/OvEditor/include/OvEditor/Core/EditorActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,6 @@ namespace OvEditor::Settings
inline static Property<float> RotationSnapUnit = { 15.0f };
inline static Property<float> ScalingSnapUnit = { 1.0f };
inline static Property<int> ColorTheme = { static_cast<int>(OvUI::Styling::EStyle::DEFAULT_DARK) };
inline static Property<std::string> LatestLayout = { "" };
};
}
2 changes: 1 addition & 1 deletion Sources/Overload/OvEditor/src/OvEditor/Core/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<OvAudio::Core::AudioEngine>(projectAssetsPath);
Expand Down
5 changes: 5 additions & 0 deletions Sources/Overload/OvEditor/src/OvEditor/Core/Editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
17 changes: 16 additions & 1 deletion Sources/Overload/OvEditor/src/OvEditor/Core/EditorActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
89 changes: 87 additions & 2 deletions Sources/Overload/OvEditor/src/OvEditor/Panels/MenuBar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* @licence: MIT
*/

#include <filesystem>

#include <OvTools/Utils/SystemCalls.h>

#include <OvCore/ECS/Components/CCamera.h>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -216,8 +219,90 @@ void OvEditor::Panels::MenuBar::CreateSettingsMenu()

void OvEditor::Panels::MenuBar::CreateLayoutMenu()
{
auto& layoutMenu = CreateWidget<MenuList>("Layout");
layoutMenu.CreateWidget<MenuItem>("Reset").ClickedEvent += EDITOR_BIND(ResetLayout);
auto& layoutMenuList = CreateWidget<MenuList>("Layout");

auto& uiManager = *EDITOR_CONTEXT(uiManager);
const std::filesystem::path& layoutsPath = uiManager.GetLayoutsPath();

auto& saveMenuItem = layoutMenuList.CreateWidget<MenuItem>("Save");
saveMenuItem.ClickedEvent += EDITOR_BIND(SaveCurrentLayout);

auto& saveNewMenuList = layoutMenuList.CreateWidget<MenuList>("Save New");
auto& layoutInputText = saveNewMenuList.CreateWidget<InputFields::InputText>("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));
Copy link
Member

@adriengivry adriengivry Apr 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this call, we should also update Settings::EditorSettings::LatestLayout, otherwise creating a new layout and exiting, result in the LatestLayout still being the previous one.

Suggested change
EDITOR_EXEC(DelayAction(std::bind(&OvUI::Core::UIManager::SaveLayout, &uiManager, layoutsPath / (p_input + ".ini")), 1));
EDITOR_EXEC(DelayAction(std::bind(&OvUI::Core::UIManager::SaveLayout, &uiManager, layoutsPath / (p_input + ".ini")), 1));
Settings::EditorSettings::LatestLayout = p_input;

};

auto& loadMenuList = layoutMenuList.CreateWidget<MenuList>("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<MenuItem>(entry.path().stem().string());
layoutMenuItem.name = entry.path().stem().string();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could display which layout is currently selected with a checkmark

Suggested change
layoutMenuItem.name = entry.path().stem().string();
layoutMenuItem.name = entry.path().stem().string();
layoutMenuItem.checkable = true;
layoutMenuItem.checked = Settings::EditorSettings::LatestLayout.Get() == entry.path().stem().string();

image

Copy link
Member

@adriengivry adriengivry Apr 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, since with this change we can show the currently selected layout, maybe we should always have a "Default" option in the "Load" list (instead of "Reset" outside of the list), and the "Default" option would have no contextual menu (so it cannot be deleted/renamed)


std::shared_ptr<std::filesystem::path> currentPath = std::make_shared<std::filesystem::path>(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<OvUI::Plugins::ContextualMenu>();
auto& deleteMenuItem = contextualMenu.CreateWidget<MenuItem>("Delete");

deleteMenuItem.ClickedEvent += [currentPath, &layoutMenuItem]
{
auto& uiManager = *EDITOR_CONTEXT(uiManager);
EDITOR_EXEC(DelayAction(std::bind(&OvUI::Core::UIManager::DeleteLayout, &uiManager, *currentPath), 1));
Copy link
Member

@adriengivry adriengivry Apr 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a layout is deleted, and was currently selected, it should also be removed from LastLayout.
I've tried creating a layout, setting it as current, deleting it, and closing the editor, and in editor.ini I still have it as selected. Also, the actual layout file is never deleted, so although I deleted the layout, it still loads when I restart the editor.

Actually what happens is that the layout gets deleted, but since it's still set as LatestLayout, and used by ImGUI, closing the editor will recreate the file.

layoutMenuItem.enabled = false;

};
auto& renameToMenuList = contextualMenu.CreateWidget<MenuList>("Rename to...");

auto& renameInputText = renameToMenuList.CreateWidget<InputFields::InputText>("");
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<MenuItem>("Reset").ClickedEvent += EDITOR_BIND(ResetLayout);
}

void OvEditor::Panels::MenuBar::CreateHelpMenu()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand All @@ -59,4 +60,5 @@ void OvEditor::Settings::EditorSettings::Load()
LoadIniEntry<float>(iniFile, "rotation_snap_unit", RotationSnapUnit);
LoadIniEntry<float>(iniFile, "scaling_snap_unit", ScalingSnapUnit);
LoadIniEntry<int>(iniFile, "color_theme", ColorTheme);
LoadIniEntry<std::string>(iniFile, "latest_layout", LatestLayout);
}
59 changes: 53 additions & 6 deletions Sources/Overload/OvUI/include/OvUI/Core/UIManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#pragma once

#include <filesystem>
#include <string>
#include <unordered_map>

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -124,6 +167,8 @@ namespace OvUI::Core
*/
void Render();

const std::filesystem::path& GetLayoutsPath() const;

private:
void PushCurrentFont();
void PopCurrentFont();
Expand All @@ -133,5 +178,7 @@ namespace OvUI::Core
Modules::Canvas* m_currentCanvas = nullptr;
std::unordered_map<std::string, ImFont*> m_fonts;
std::string m_layoutSaveFilename = "imgui.ini";
const std::filesystem::path m_defaultLayout;
const std::filesystem::path m_layoutsPath;
};
}
Loading