2026-03-20 17:08:06 +08:00
|
|
|
#include "MenuBar.h"
|
2026-03-26 01:26:26 +08:00
|
|
|
#include "Core/IEditorContext.h"
|
|
|
|
|
#include "Core/ISceneManager.h"
|
2026-03-26 01:59:14 +08:00
|
|
|
#include "Core/IUndoManager.h"
|
|
|
|
|
#include "Core/ISelectionManager.h"
|
2026-03-26 01:26:26 +08:00
|
|
|
#include "Utils/SceneEditorUtils.h"
|
|
|
|
|
#include <filesystem>
|
2026-03-20 17:08:06 +08:00
|
|
|
#include <imgui.h>
|
|
|
|
|
|
2026-03-24 20:02:38 +08:00
|
|
|
namespace XCEngine {
|
|
|
|
|
namespace Editor {
|
2026-03-20 17:08:06 +08:00
|
|
|
|
|
|
|
|
MenuBar::MenuBar() : Panel("MenuBar") {}
|
|
|
|
|
|
|
|
|
|
void MenuBar::Render() {
|
2026-03-26 01:26:26 +08:00
|
|
|
HandleShortcuts();
|
|
|
|
|
|
2026-03-20 17:08:06 +08:00
|
|
|
if (ImGui::BeginMainMenuBar()) {
|
|
|
|
|
ShowFileMenu();
|
|
|
|
|
ShowEditMenu();
|
|
|
|
|
ShowViewMenu();
|
|
|
|
|
ShowHelpMenu();
|
2026-03-26 01:26:26 +08:00
|
|
|
RenderSceneStatus();
|
2026-03-20 17:08:06 +08:00
|
|
|
ImGui::EndMainMenuBar();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 01:26:26 +08:00
|
|
|
void MenuBar::NewScene() {
|
|
|
|
|
if (!m_context || !SceneEditorUtils::ConfirmSceneSwitch(*m_context)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_context->GetSceneManager().NewScene();
|
2026-03-26 01:59:14 +08:00
|
|
|
m_context->GetSelectionManager().ClearSelection();
|
|
|
|
|
m_context->GetUndoManager().ClearHistory();
|
2026-03-26 01:26:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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()) {
|
2026-03-26 01:59:14 +08:00
|
|
|
if (m_context->GetSceneManager().LoadScene(filePath)) {
|
|
|
|
|
m_context->GetSelectionManager().ClearSelection();
|
|
|
|
|
m_context->GetUndoManager().ClearHistory();
|
|
|
|
|
}
|
2026-03-26 01:26:26 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MenuBar::HandleShortcuts() {
|
|
|
|
|
if (!m_context) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
|
if (!io.KeyCtrl) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 01:59:14 +08:00
|
|
|
auto& undoManager = m_context->GetUndoManager();
|
2026-03-26 01:26:26 +08:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-26 01:59:14 +08:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-26 01:26:26 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-20 17:08:06 +08:00
|
|
|
void MenuBar::ShowFileMenu() {
|
|
|
|
|
if (ImGui::BeginMenu("File")) {
|
2026-03-26 01:26:26 +08:00
|
|
|
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();
|
|
|
|
|
}
|
2026-03-20 17:08:06 +08:00
|
|
|
ImGui::Separator();
|
|
|
|
|
if (ImGui::MenuItem("Exit", "Alt+F4")) {}
|
|
|
|
|
ImGui::EndMenu();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MenuBar::ShowEditMenu() {
|
|
|
|
|
if (ImGui::BeginMenu("Edit")) {
|
2026-03-26 01:59:14 +08:00
|
|
|
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();
|
|
|
|
|
}
|
2026-03-20 17:08:06 +08:00
|
|
|
ImGui::Separator();
|
|
|
|
|
if (ImGui::MenuItem("Cut", "Ctrl+X")) {}
|
|
|
|
|
if (ImGui::MenuItem("Copy", "Ctrl+C")) {}
|
|
|
|
|
if (ImGui::MenuItem("Paste", "Ctrl+V")) {}
|
|
|
|
|
ImGui::EndMenu();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MenuBar::ShowViewMenu() {
|
|
|
|
|
if (ImGui::BeginMenu("View")) {
|
|
|
|
|
if (ImGui::MenuItem("Reset Layout")) {}
|
|
|
|
|
ImGui::EndMenu();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MenuBar::ShowHelpMenu() {
|
|
|
|
|
if (ImGui::BeginMenu("Help")) {
|
|
|
|
|
if (ImGui::MenuItem("About")) {}
|
|
|
|
|
ImGui::EndMenu();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 01:26:26 +08:00
|
|
|
void MenuBar::RenderSceneStatus() {
|
|
|
|
|
if (!m_context) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto& sceneManager = m_context->GetSceneManager();
|
|
|
|
|
std::string sceneLabel = sceneManager.HasActiveScene() ? sceneManager.GetCurrentSceneName() : "No Scene";
|
|
|
|
|
if (sceneLabel.empty()) {
|
|
|
|
|
sceneLabel = "Untitled Scene";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string fileLabel = sceneManager.GetCurrentScenePath().empty()
|
|
|
|
|
? "Unsaved.xc"
|
|
|
|
|
: 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());
|
|
|
|
|
|
|
|
|
|
if (ImGui::IsItemHovered()) {
|
|
|
|
|
ImGui::BeginTooltip();
|
|
|
|
|
ImGui::Text("Scene");
|
|
|
|
|
ImGui::Separator();
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2026-03-24 20:02:38 +08:00
|
|
|
}
|