Refactor editor UI architecture

This commit is contained in:
2026-03-26 21:18:33 +08:00
parent 8f486611d5
commit 6467d87b81
60 changed files with 2994 additions and 1403 deletions

View File

@@ -1,9 +1,14 @@
#include "Actions/EditorActions.h"
#include "Commands/EntityCommands.h"
#include "Commands/SceneCommands.h"
#include "MenuBar.h"
#include "Core/EditorEvents.h"
#include "Core/EventBus.h"
#include "Core/IEditorContext.h"
#include "Core/ISceneManager.h"
#include "Core/IUndoManager.h"
#include "Core/ISelectionManager.h"
#include "Utils/SceneEditorUtils.h"
#include "UI/UI.h"
#include <filesystem>
#include <imgui.h>
@@ -23,56 +28,8 @@ void MenuBar::Render() {
RenderSceneStatus();
ImGui::EndMainMenuBar();
}
}
void MenuBar::NewScene() {
if (!m_context || !SceneEditorUtils::ConfirmSceneSwitch(*m_context)) {
return;
}
m_context->GetSceneManager().NewScene();
m_context->GetSelectionManager().ClearSelection();
m_context->GetUndoManager().ClearHistory();
}
void MenuBar::OpenScene() {
if (!m_context || !SceneEditorUtils::ConfirmSceneSwitch(*m_context)) {
return;
}
const std::string filePath = SceneEditorUtils::OpenSceneFileDialog(
m_context->GetProjectPath(),
m_context->GetSceneManager().GetCurrentScenePath());
if (!filePath.empty()) {
if (m_context->GetSceneManager().LoadScene(filePath)) {
m_context->GetSelectionManager().ClearSelection();
m_context->GetUndoManager().ClearHistory();
}
}
}
void MenuBar::SaveScene() {
if (!m_context) {
return;
}
if (!SceneEditorUtils::SaveCurrentScene(*m_context)) {
return;
}
}
void MenuBar::SaveSceneAs() {
if (!m_context) {
return;
}
const std::string filePath = SceneEditorUtils::SaveSceneFileDialog(
m_context->GetProjectPath(),
m_context->GetSceneManager().GetCurrentScenePath(),
m_context->GetSceneManager().GetCurrentSceneName());
if (!filePath.empty() && m_context->GetSceneManager().SaveSceneAs(filePath)) {
m_context->GetProjectManager().RefreshCurrentFolder();
}
RenderAboutPopup();
}
void MenuBar::HandleShortcuts() {
@@ -80,92 +37,83 @@ void MenuBar::HandleShortcuts() {
return;
}
ImGuiIO& io = ImGui::GetIO();
if (!io.KeyCtrl) {
return;
}
auto& undoManager = m_context->GetUndoManager();
if (ImGui::IsKeyPressed(ImGuiKey_N, false)) {
NewScene();
}
if (ImGui::IsKeyPressed(ImGuiKey_O, false)) {
OpenScene();
}
if (ImGui::IsKeyPressed(ImGuiKey_S, false)) {
if (io.KeyShift) {
SaveSceneAs();
} else {
SaveScene();
}
}
if (!io.WantTextInput) {
if (ImGui::IsKeyPressed(ImGuiKey_Z, false)) {
if (io.KeyShift) {
if (undoManager.CanRedo()) {
undoManager.Redo();
}
} else if (undoManager.CanUndo()) {
undoManager.Undo();
}
}
if (ImGui::IsKeyPressed(ImGuiKey_Y, false) && undoManager.CanRedo()) {
undoManager.Redo();
}
}
Actions::HandleShortcut(Actions::MakeNewSceneAction(), [&]() { Commands::NewScene(*m_context); });
Actions::HandleShortcut(Actions::MakeOpenSceneAction(), [&]() { Commands::OpenSceneWithDialog(*m_context); });
Actions::HandleShortcut(Actions::MakeSaveSceneAction(), [&]() { Commands::SaveCurrentScene(*m_context); });
Actions::HandleShortcut(Actions::MakeSaveSceneAsAction(), [&]() { Commands::SaveSceneAsWithDialog(*m_context); });
Actions::HandleShortcut(Actions::MakeUndoAction(*m_context), [&]() { m_context->GetUndoManager().Undo(); });
Actions::HandleShortcut(Actions::MakeRedoAction(*m_context), [&]() { m_context->GetUndoManager().Redo(); });
}
void MenuBar::ShowFileMenu() {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("New Scene", "Ctrl+N")) {
NewScene();
}
if (ImGui::MenuItem("Open Scene", "Ctrl+O")) {
OpenScene();
}
if (ImGui::MenuItem("Save Scene", "Ctrl+S")) {
SaveScene();
}
if (ImGui::MenuItem("Save Scene As...", "Ctrl+Shift+S")) {
SaveSceneAs();
}
ImGui::Separator();
if (ImGui::MenuItem("Exit", "Alt+F4")) {}
ImGui::EndMenu();
}
UI::DrawMenuScope("File", [&]() {
Actions::DrawMenuAction(Actions::MakeNewSceneAction(), [&]() { Commands::NewScene(*m_context); });
Actions::DrawMenuAction(Actions::MakeOpenSceneAction(), [&]() { Commands::OpenSceneWithDialog(*m_context); });
Actions::DrawMenuAction(Actions::MakeSaveSceneAction(), [&]() { Commands::SaveCurrentScene(*m_context); });
Actions::DrawMenuAction(Actions::MakeSaveSceneAsAction(), [&]() { Commands::SaveSceneAsWithDialog(*m_context); });
Actions::DrawMenuSeparator();
Actions::DrawMenuAction(Actions::MakeExitAction(), [&]() {
m_context->GetEventBus().Publish(EditorExitRequestedEvent{});
});
});
}
void MenuBar::ShowEditMenu() {
if (ImGui::BeginMenu("Edit")) {
auto& undoManager = m_context->GetUndoManager();
const std::string undoLabel = undoManager.CanUndo() ? "Undo " + undoManager.GetUndoLabel() : "Undo";
const std::string redoLabel = undoManager.CanRedo() ? "Redo " + undoManager.GetRedoLabel() : "Redo";
if (ImGui::MenuItem(undoLabel.c_str(), "Ctrl+Z", false, undoManager.CanUndo())) {
undoManager.Undo();
}
if (ImGui::MenuItem(redoLabel.c_str(), "Ctrl+Y", false, undoManager.CanRedo())) {
undoManager.Redo();
}
ImGui::Separator();
if (ImGui::MenuItem("Cut", "Ctrl+X")) {}
if (ImGui::MenuItem("Copy", "Ctrl+C")) {}
if (ImGui::MenuItem("Paste", "Ctrl+V")) {}
ImGui::EndMenu();
}
::XCEngine::Components::GameObject* selectedGameObject = Actions::GetSelectedGameObject(*m_context);
UI::DrawMenuScope("Edit", [&]() {
Actions::DrawMenuAction(Actions::MakeUndoAction(*m_context), [&]() { m_context->GetUndoManager().Undo(); });
Actions::DrawMenuAction(Actions::MakeRedoAction(*m_context), [&]() { m_context->GetUndoManager().Redo(); });
Actions::DrawMenuSeparator();
Actions::DrawMenuAction(Actions::MakeCutAction(false), []() {});
Actions::DrawMenuAction(Actions::MakeCopyEntityAction(selectedGameObject), [&]() {
Commands::CopyEntity(*m_context, selectedGameObject->GetID());
});
Actions::DrawMenuAction(Actions::MakePasteEntityAction(*m_context), [&]() {
Commands::PasteEntity(*m_context, selectedGameObject ? selectedGameObject->GetID() : 0);
});
});
}
void MenuBar::ShowViewMenu() {
if (ImGui::BeginMenu("View")) {
if (ImGui::MenuItem("Reset Layout")) {}
ImGui::EndMenu();
}
UI::DrawMenuScope("View", [&]() {
Actions::DrawMenuAction(Actions::MakeResetLayoutAction(), [&]() {
m_context->GetEventBus().Publish(DockLayoutResetRequestedEvent{});
});
});
}
void MenuBar::ShowHelpMenu() {
if (ImGui::BeginMenu("Help")) {
if (ImGui::MenuItem("About")) {}
ImGui::EndMenu();
UI::DrawMenuScope("Help", [&]() {
Actions::DrawMenuAction(Actions::MakeAboutAction(), [&]() {
m_aboutPopup.RequestOpen();
});
});
}
void MenuBar::RenderAboutPopup() {
m_aboutPopup.ConsumeOpenRequest("About XCEngine Editor");
if (!UI::BeginModalPopup("About XCEngine Editor")) {
return;
}
ImGui::TextUnformatted("XCEngine Editor");
ImGui::Separator();
UI::DrawHintText("Unity-like editor shell built on Dear ImGui.");
ImGui::Spacing();
ImGui::Text("Date: 2026-03-26");
ImGui::Text("UI Refactor: Actions / Commands / Layout in progress");
if (m_context) {
ImGui::Text("Project: %s", m_context->GetProjectPath().c_str());
}
ImGui::Spacing();
if (Actions::DrawButtonAction(Actions::MakeAction("Close"), UI::DialogActionButtonSize())) {
ImGui::CloseCurrentPopup();
}
UI::EndPopup();
}
void MenuBar::RenderSceneStatus() {
@@ -184,27 +132,18 @@ void MenuBar::RenderSceneStatus() {
: std::filesystem::path(sceneManager.GetCurrentScenePath()).filename().string();
const bool dirty = sceneManager.IsSceneDirty();
const std::string statusText = std::string("Scene: ") + fileLabel + (dirty ? " Modified" : " Saved");
const ImVec2 textSize = ImGui::CalcTextSize(statusText.c_str());
const float targetX = ImGui::GetWindowWidth() - textSize.x - 20.0f;
if (targetX > ImGui::GetCursorPosX()) {
ImGui::SetCursorPosX(targetX);
}
const ImVec4 accentColor = dirty
? ImVec4(0.94f, 0.68f, 0.20f, 1.0f)
: ImVec4(0.48f, 0.78f, 0.49f, 1.0f);
ImGui::TextColored(accentColor, "%s", statusText.c_str());
const std::string statusText = dirty ? (std::string("* ") + fileLabel) : fileLabel;
UI::DrawRightAlignedText(
statusText.c_str(),
dirty ? UI::MenuBarStatusDirtyColor() : UI::MenuBarStatusIdleColor());
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::Text("Scene");
ImGui::Separator();
UI::BeginTitledTooltip("Scene");
ImGui::Text("Name: %s", sceneLabel.c_str());
ImGui::Text("File: %s", fileLabel.c_str());
ImGui::Text("State: %s", dirty ? "Modified" : "Saved");
ImGui::Text("Path: %s", sceneManager.GetCurrentScenePath().empty() ? "(not saved yet)" : sceneManager.GetCurrentScenePath().c_str());
ImGui::EndTooltip();
UI::EndTitledTooltip();
}
}