// // Editor.cpp:docking 骨架实现。 // // 布局方案(首帧 DockBuilder 一次,之后 imgui.ini 接管): // ┌──────────────────────────────┬───────────────┐ // │ Hierarchy │ │ // ├──────────────────────────────┤ Inspector │ // │ Stats │ (28%) │ // └──────────────────────────────┴───────────────┘ // #include "Editor.h" #include "HierarchyPanel.h" #include "InspectorPanel.h" #include "ViewportPanel.h" #include "StatsPanel.h" #include #include // DockBuilder*(内部 API) #include using namespace Engine; // kDisplayViewport / kDisplayDirect(SceneTypes.h) Editor::Editor() = default; Editor::~Editor() = default; void Editor::Init(const EditorContext& ctx) { m_Ctx = ctx; m_Hierarchy = std::make_unique(); m_Inspector = std::make_unique(); m_Viewport = std::make_unique(); m_Stats = std::make_unique(); } void Editor::Draw() { // host 窗口:铺满主视口,承载菜单栏 + DockSpace ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->Pos); ImGui::SetNextWindowSize(viewport->Size); ImGui::SetNextWindowViewport(viewport->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("##EditorDockHost", nullptr, ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus); ImGui::PopStyleVar(3); DrawMenuBar(); DrawDockSpace(); ImGui::End(); // 常规面板窗口(窗口名 = docking 身份) if (m_ShowHierarchy) m_Hierarchy->Draw(m_Ctx, m_Selected); if (m_ShowInspector) m_Inspector->Draw(m_Ctx, m_Selected); if (m_ShowViewport) { if (auto picked = m_Viewport->Draw(m_Ctx, m_VpInput)) { SelectEntity(*picked); // Viewport 点击拾取 → 选中联动 Inspector/Hierarchy } } if (m_ShowStats) m_Stats->Draw(m_Ctx); DrawQuitModal(); } // 输入归属判定:ImGui 侧经自身后端链式回调永远正常收输入,这里 // 只决定"剩余输入给不给游戏侧"。UI 被隐藏(反引号)时不会走到 // Draw → m_VpInput 停在旧值,谓词调用方(SandboxApp)须先查 // GetImGuiEnabled() 再问这里。 bool Editor::BlockingKeyboard() const { const ImGuiIO& io = ImGui::GetIO(); if (io.WantTextInput) return true; // 打字中(改名等)不让位快捷键 // 游戏形态(Fullscreen):WASD 全窗口生效,键盘只让位文本输入 if (m_Ctx.renderer && m_Ctx.renderer->GetDisplayMode() == kDisplayDirect) { return false; } // 编辑器形态:Viewport 面板持焦才拥有键盘(UE Scene view 语义) return !m_VpInput.focused; } bool Editor::BlockingMouse() const { // 游戏形态:沿用 ImGui 全局判定(UI 隐藏时 WantCaptureMouse=false) if (m_Ctx.renderer && m_Ctx.renderer->GetDisplayMode() == kDisplayDirect) { return ImGui::GetIO().WantCaptureMouse; } // 编辑器形态:悬停 Viewport 才放行(模态框遮挡时 hovered=false) return !m_VpInput.hovered; } void Editor::DrawMenuBar() { if (!ImGui::BeginMenuBar()) { return; } if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Quit", "Alt+F4")) { RequestQuit(); } ImGui::EndMenu(); } if (ImGui::BeginMenu("View")) { ImGui::MenuItem("Hierarchy", nullptr, &m_ShowHierarchy); ImGui::MenuItem("Inspector", nullptr, &m_ShowInspector); ImGui::MenuItem("Viewport", nullptr, &m_ShowViewport); ImGui::MenuItem("Stats", nullptr, &m_ShowStats); ImGui::EndMenu(); } ImGui::EndMenuBar(); } void Editor::DrawDockSpace() { const ImGuiID dockspaceId = ImGui::GetID("EditorDockSpace"); // 默认布局:仅当 dock node 不存在时构建一次 //(imgui.ini 已保存过布局 → DockBuilderGetNode 非空 → 跳过) if (!m_LayoutBuilt && ImGui::DockBuilderGetNode(dockspaceId) == nullptr) { m_LayoutBuilt = true; ImGui::DockBuilderRemoveNode(dockspaceId); ImGui::DockBuilderAddNode(dockspaceId, ImGuiDockNodeFlags_DockSpace); ImGui::DockBuilderSetNodeSize(dockspaceId, ImGui::GetMainViewport()->Size); ImGuiID dockLeft = 0, dockRight = 0, dockCenter = 0, dockStats = 0; ImGui::DockBuilderSplitNode(dockspaceId, ImGuiDir_Left, 0.20f, &dockLeft, &dockCenter); ImGui::DockBuilderSplitNode(dockCenter, ImGuiDir_Right, 0.28f, &dockRight, &dockCenter); ImGui::DockBuilderSplitNode(dockLeft, ImGuiDir_Down, 0.38f, &dockStats, &dockLeft); ImGui::DockBuilderDockWindow("Hierarchy", dockLeft); ImGui::DockBuilderDockWindow("Stats", dockStats); ImGui::DockBuilderDockWindow("Inspector", dockRight); ImGui::DockBuilderDockWindow("Viewport", dockCenter); ImGui::DockBuilderFinish(dockspaceId); } ImGui::DockSpace(dockspaceId, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None); } void Editor::DrawQuitModal() { if (m_ShowQuitConfirm) { ImGui::OpenPopup("Should quit?"); m_ShowQuitConfirm = false; } ImGui::SetNextWindowPos( ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal("Should quit?", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::TextUnformatted("Quit the application?"); ImGui::Separator(); if (ImGui::Button("Yes", ImVec2(120, 0))) { m_QuitConfirmed = true; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("No", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } if (m_QuitConfirmed) { m_QuitConfirmed = false; m_Ctx.app->Close(); } }