Unify inspector and console panel actions
This commit is contained in:
@@ -105,6 +105,8 @@
|
||||
- `Edit` 动作解析与菜单绘制 / shortcut 分发已开始从 `MenuBar` 抽成共享 router
|
||||
- `Hierarchy / Project` 的上下文菜单与创建弹窗也开始下沉到 shared action router
|
||||
- `Project` 右键菜单目标已不再依赖 panel 内裸索引字段,而是改成 targeted popup state
|
||||
- `Inspector / Console` 的局部 action 组装也开始继续下沉到 shared router
|
||||
- `Inspector` 的 component section header 菜单已开始改成 callback/router 驱动,而不是在 widget 层硬编码动作
|
||||
|
||||
### 5. Dock / Layout 层
|
||||
|
||||
@@ -134,6 +136,8 @@
|
||||
- panel 的 attach / detach / render 顺序有了统一入口
|
||||
- 后续继续拆 panel 或补 panel 时,不需要再改一大片壳层代码
|
||||
- startup scene / dock attach / panel tree 组装已继续从 `EditorLayer` 收口到 `EditorWorkspace`
|
||||
- `Inspector` 的 selection 订阅已从 `Render()` 懒接线改回生命周期接线
|
||||
- `Hierarchy` 的事件订阅也已从析构清理改回显式 `OnAttach / OnDetach` 生命周期清理
|
||||
|
||||
### 7. Application / ImGui Session 层
|
||||
|
||||
@@ -151,6 +155,7 @@
|
||||
- Win32 window/message pump 已抽成 `Platform/Win32EditorHost.h`
|
||||
- DX12 swapchain / render target / present / resize 已抽成 `Platform/D3D12WindowRenderer.h`
|
||||
- scene title 拼装已抽成 `Core/EditorWindowTitle.h`
|
||||
- crash filter / stderr redirect / logging sink 初始化已继续从 `Application.cpp` 抽离
|
||||
|
||||
## 主要面板状态
|
||||
|
||||
@@ -212,6 +217,8 @@
|
||||
- 组件内容编辑大部分已走 property grid
|
||||
- Add Component 按钮与 popup 项已接 action 层
|
||||
- Add Component popup 已接 shared popup state
|
||||
- Add Component popup 菜单项组装已开始从 panel 下沉到 shared inspector action router
|
||||
- 组件 section header 的移除动作已开始从 widget 层硬编码迁回 inspector action router
|
||||
|
||||
仍待完成:
|
||||
|
||||
@@ -226,6 +233,7 @@
|
||||
- 日志行 hover 表现已统一
|
||||
- `Clear / Filter` 已接 action 层
|
||||
- console filter 状态已从 panel 裸布尔字段收成独立 state object
|
||||
- console toolbar action 与日志文本格式化已继续从 panel 下沉到共享层
|
||||
|
||||
仍待完成:
|
||||
|
||||
|
||||
29
editor/src/Actions/ConsoleActionRouter.h
Normal file
29
editor/src/Actions/ConsoleActionRouter.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "EditorActions.h"
|
||||
#include "Core/EditorConsoleSink.h"
|
||||
#include "UI/ConsoleFilterState.h"
|
||||
#include "UI/UI.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace Actions {
|
||||
|
||||
inline void DrawConsoleToolbarActions(Debug::EditorConsoleSink& sink, UI::ConsoleFilterState& filterState) {
|
||||
if (DrawToolbarAction(MakeClearConsoleAction())) {
|
||||
sink.Clear();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
UI::DrawToolbarLabel("Filter");
|
||||
ImGui::SameLine();
|
||||
DrawToolbarToggleAction(MakeConsoleInfoFilterAction(filterState.ShowInfo()), filterState.ShowInfo());
|
||||
ImGui::SameLine();
|
||||
DrawToolbarToggleAction(MakeConsoleWarningFilterAction(filterState.ShowWarning()), filterState.ShowWarning());
|
||||
ImGui::SameLine();
|
||||
DrawToolbarToggleAction(MakeConsoleErrorFilterAction(filterState.ShowError()), filterState.ShowError());
|
||||
}
|
||||
|
||||
} // namespace Actions
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
@@ -161,6 +161,10 @@ inline ActionBinding MakeAddComponentMenuAction(const std::string& label, bool e
|
||||
return MakeAction(label, nullptr, false, enabled);
|
||||
}
|
||||
|
||||
inline ActionBinding MakeRemoveComponentAction(bool enabled = true) {
|
||||
return MakeAction("Remove Component", nullptr, false, enabled);
|
||||
}
|
||||
|
||||
inline ActionBinding MakeClearConsoleAction(bool enabled = true) {
|
||||
return MakeAction("Clear", nullptr, false, enabled);
|
||||
}
|
||||
|
||||
65
editor/src/Actions/InspectorActionRouter.h
Normal file
65
editor/src/Actions/InspectorActionRouter.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "EditorActions.h"
|
||||
#include "Commands/ComponentCommands.h"
|
||||
#include "ComponentEditors/ComponentEditorRegistry.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
#include "UI/UI.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace Actions {
|
||||
|
||||
inline std::string BuildAddComponentMenuLabel(const IComponentEditor& editor, ::XCEngine::Components::GameObject* gameObject) {
|
||||
std::string label = editor.GetDisplayName();
|
||||
if (Commands::CanAddComponent(editor, gameObject)) {
|
||||
return label;
|
||||
}
|
||||
|
||||
const char* reason = editor.GetAddDisabledReason(gameObject);
|
||||
if (reason && reason[0] != '\0') {
|
||||
label += " (";
|
||||
label += reason;
|
||||
label += ")";
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
inline bool DrawInspectorAddComponentMenu(IEditorContext& context, ::XCEngine::Components::GameObject* gameObject) {
|
||||
bool drewAnyEntry = false;
|
||||
|
||||
for (const auto& editor : ComponentEditorRegistry::Get().GetEditors()) {
|
||||
if (!editor || !editor->ShowInAddComponentMenu()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
drewAnyEntry = true;
|
||||
const bool canAdd = Commands::CanAddComponent(*editor, gameObject);
|
||||
const std::string label = BuildAddComponentMenuLabel(*editor, gameObject);
|
||||
DrawMenuAction(MakeAddComponentMenuAction(label, canAdd), [&]() {
|
||||
if (Commands::AddComponent(context, *editor, gameObject)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return drewAnyEntry;
|
||||
}
|
||||
|
||||
inline bool DrawInspectorComponentMenu(
|
||||
IEditorContext& context,
|
||||
::XCEngine::Components::Component* component,
|
||||
::XCEngine::Components::GameObject* gameObject,
|
||||
const IComponentEditor* editor) {
|
||||
const bool canRemove = Commands::CanRemoveComponent(component, editor);
|
||||
return DrawMenuAction(MakeRemoveComponentAction(canRemove), [&]() {
|
||||
Commands::RemoveComponent(context, component, gameObject, editor);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Actions
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
@@ -1,43 +1,14 @@
|
||||
#include "Application.h"
|
||||
#include "Core/EditorLoggingSetup.h"
|
||||
#include "Core/EditorWindowTitle.h"
|
||||
#include "Layers/EditorLayer.h"
|
||||
#include "Core/EditorContext.h"
|
||||
#include "Core/EditorConsoleSink.h"
|
||||
#include "Core/EditorEvents.h"
|
||||
#include "Core/EventBus.h"
|
||||
#include "Platform/Win32Utf8.h"
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
#include <XCEngine/Debug/FileLogSink.h>
|
||||
#include <XCEngine/Debug/ConsoleLogSink.h>
|
||||
#include <stdio.h>
|
||||
#include "Platform/WindowsProcessDiagnostics.h"
|
||||
#include <windows.h>
|
||||
|
||||
namespace {
|
||||
std::string GetExecutableLogPath(const char* fileName) {
|
||||
return XCEngine::Editor::Platform::GetExecutableDirectoryUtf8() + "\\" + fileName;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static LONG WINAPI GlobalExceptionFilter(EXCEPTION_POINTERS* exceptionPointers) {
|
||||
const std::string logPath = GetExecutableLogPath("crash.log");
|
||||
|
||||
FILE* f = nullptr;
|
||||
fopen_s(&f, logPath.c_str(), "a");
|
||||
if (f) {
|
||||
fprintf(f, "[CRASH] ExceptionCode=0x%08X, Address=0x%p\n",
|
||||
exceptionPointers->ExceptionRecord->ExceptionCode,
|
||||
exceptionPointers->ExceptionRecord->ExceptionAddress);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
fprintf(stderr, "[CRASH] ExceptionCode=0x%08X, Address=0x%p\n",
|
||||
exceptionPointers->ExceptionRecord->ExceptionCode,
|
||||
exceptionPointers->ExceptionRecord->ExceptionAddress);
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
@@ -47,21 +18,11 @@ Application& Application::Get() {
|
||||
}
|
||||
|
||||
bool Application::Initialize(HWND hwnd) {
|
||||
SetUnhandledExceptionFilter(GlobalExceptionFilter);
|
||||
|
||||
{
|
||||
const std::string stderrPath = GetExecutableLogPath("stderr.log");
|
||||
freopen(stderrPath.c_str(), "w", stderr);
|
||||
}
|
||||
|
||||
Debug::Logger::Get().AddSink(std::make_unique<Debug::ConsoleLogSink>());
|
||||
Debug::Logger::Get().AddSink(std::make_unique<Debug::EditorConsoleSink>());
|
||||
Platform::InstallCrashExceptionFilter();
|
||||
Platform::RedirectStderrToExecutableLog();
|
||||
|
||||
const std::string exeDir = Platform::GetExecutableDirectoryUtf8();
|
||||
std::string logPath = exeDir + "\\editor.log";
|
||||
Debug::Logger::Get().AddSink(std::make_unique<Debug::FileLogSink>(logPath.c_str()));
|
||||
Debug::Logger::Get().Info(Debug::LogCategory::General, "Editor Application starting...");
|
||||
Debug::Logger::Get().Info(Debug::LogCategory::General, ("Log file: " + logPath).c_str());
|
||||
ConfigureEditorLogging(exeDir);
|
||||
|
||||
m_hwnd = hwnd;
|
||||
|
||||
|
||||
27
editor/src/Core/EditorLoggingSetup.h
Normal file
27
editor/src/Core/EditorLoggingSetup.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/EditorConsoleSink.h"
|
||||
|
||||
#include <XCEngine/Debug/ConsoleLogSink.h>
|
||||
#include <XCEngine/Debug/FileLogSink.h>
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
inline void ConfigureEditorLogging(const std::string& executableDirectory) {
|
||||
auto& logger = Debug::Logger::Get();
|
||||
logger.AddSink(std::make_unique<Debug::ConsoleLogSink>());
|
||||
logger.AddSink(std::make_unique<Debug::EditorConsoleSink>());
|
||||
|
||||
const std::string logPath = executableDirectory + "\\editor.log";
|
||||
logger.AddSink(std::make_unique<Debug::FileLogSink>(logPath.c_str()));
|
||||
logger.Info(Debug::LogCategory::General, "Editor Application starting...");
|
||||
logger.Info(Debug::LogCategory::General, ("Log file: " + logPath).c_str());
|
||||
}
|
||||
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
51
editor/src/Platform/WindowsProcessDiagnostics.h
Normal file
51
editor/src/Platform/WindowsProcessDiagnostics.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "Platform/Win32Utf8.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace Platform {
|
||||
|
||||
inline std::string GetExecutableLogPath(const char* fileName) {
|
||||
return GetExecutableDirectoryUtf8() + "\\" + fileName;
|
||||
}
|
||||
|
||||
inline LONG WINAPI CrashExceptionFilter(EXCEPTION_POINTERS* exceptionPointers) {
|
||||
const std::string logPath = GetExecutableLogPath("crash.log");
|
||||
|
||||
FILE* file = nullptr;
|
||||
fopen_s(&file, logPath.c_str(), "a");
|
||||
if (file) {
|
||||
fprintf(
|
||||
file,
|
||||
"[CRASH] ExceptionCode=0x%08X, Address=0x%p\n",
|
||||
exceptionPointers->ExceptionRecord->ExceptionCode,
|
||||
exceptionPointers->ExceptionRecord->ExceptionAddress);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
fprintf(
|
||||
stderr,
|
||||
"[CRASH] ExceptionCode=0x%08X, Address=0x%p\n",
|
||||
exceptionPointers->ExceptionRecord->ExceptionCode,
|
||||
exceptionPointers->ExceptionRecord->ExceptionAddress);
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
inline void InstallCrashExceptionFilter() {
|
||||
SetUnhandledExceptionFilter(CrashExceptionFilter);
|
||||
}
|
||||
|
||||
inline void RedirectStderrToExecutableLog() {
|
||||
const std::string stderrPath = GetExecutableLogPath("stderr.log");
|
||||
freopen(stderrPath.c_str(), "w", stderr);
|
||||
}
|
||||
|
||||
} // namespace Platform
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
36
editor/src/UI/ConsoleLogFormatter.h
Normal file
36
editor/src/UI/ConsoleLogFormatter.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Debug/LogCategory.h>
|
||||
#include <XCEngine/Debug/LogEntry.h>
|
||||
#include <XCEngine/Debug/LogLevel.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace UI {
|
||||
|
||||
inline const char* ConsoleLogPrefix(::XCEngine::Debug::LogLevel level) {
|
||||
switch (level) {
|
||||
case ::XCEngine::Debug::LogLevel::Verbose:
|
||||
case ::XCEngine::Debug::LogLevel::Debug:
|
||||
case ::XCEngine::Debug::LogLevel::Info:
|
||||
return "[INFO] ";
|
||||
case ::XCEngine::Debug::LogLevel::Warning:
|
||||
return "[WARN] ";
|
||||
case ::XCEngine::Debug::LogLevel::Error:
|
||||
case ::XCEngine::Debug::LogLevel::Fatal:
|
||||
return "[ERROR] ";
|
||||
}
|
||||
|
||||
return "[LOG] ";
|
||||
}
|
||||
|
||||
inline std::string BuildConsoleLogText(const ::XCEngine::Debug::LogEntry& log) {
|
||||
const char* category = ::XCEngine::Debug::LogCategoryToString(log.category);
|
||||
return std::string(ConsoleLogPrefix(log.level)) + "[" + category + "] " + log.message.CStr();
|
||||
}
|
||||
|
||||
} // namespace UI
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "BaseTheme.h"
|
||||
#include "ConsoleFilterState.h"
|
||||
#include "ConsoleLogFormatter.h"
|
||||
#include "Core.h"
|
||||
#include "DockHostStyle.h"
|
||||
#include "PanelChrome.h"
|
||||
|
||||
@@ -11,7 +11,6 @@ namespace UI {
|
||||
|
||||
struct ComponentSectionResult {
|
||||
bool open = false;
|
||||
bool removeRequested = false;
|
||||
};
|
||||
|
||||
struct HierarchyNodeResult {
|
||||
@@ -281,10 +280,11 @@ inline void DrawAssetIcon(ImDrawList* drawList, const ImVec2& min, const ImVec2&
|
||||
drawList->AddLine(foldA, foldB, lineColor);
|
||||
}
|
||||
|
||||
template <typename DrawMenuFn>
|
||||
inline ComponentSectionResult BeginComponentSection(
|
||||
const void* id,
|
||||
const char* label,
|
||||
bool canRemove,
|
||||
DrawMenuFn&& drawMenu,
|
||||
bool defaultOpen = true) {
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, InspectorSectionFramePadding());
|
||||
@@ -302,15 +302,19 @@ inline ComponentSectionResult BeginComponentSection(
|
||||
const bool open = ImGui::TreeNodeEx(id, flags, "%s", label);
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
bool removeRequested = false;
|
||||
if (BeginPopupContextItem("##ComponentSettings")) {
|
||||
DrawMenuCommand(MenuCommand::Action("Remove Component", nullptr, false, canRemove), [&]() {
|
||||
removeRequested = true;
|
||||
});
|
||||
drawMenu();
|
||||
EndPopup();
|
||||
}
|
||||
|
||||
return ComponentSectionResult{ open, removeRequested };
|
||||
return ComponentSectionResult{ open };
|
||||
}
|
||||
|
||||
inline ComponentSectionResult BeginComponentSection(
|
||||
const void* id,
|
||||
const char* label,
|
||||
bool defaultOpen = true) {
|
||||
return BeginComponentSection(id, label, []() {}, defaultOpen);
|
||||
}
|
||||
|
||||
inline void EndComponentSection() {
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#include "Actions/ConsoleActionRouter.h"
|
||||
#include "Actions/ActionRouting.h"
|
||||
#include "Actions/EditorActions.h"
|
||||
#include "ConsolePanel.h"
|
||||
#include "Core/EditorConsoleSink.h"
|
||||
#include "UI/UI.h"
|
||||
#include <XCEngine/Debug/LogCategory.h>
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
#include <imgui.h>
|
||||
|
||||
namespace XCEngine {
|
||||
@@ -26,18 +24,7 @@ void ConsolePanel::Render() {
|
||||
{
|
||||
UI::PanelToolbarScope toolbar("ConsoleToolbar", UI::StandardPanelToolbarHeight());
|
||||
if (toolbar.IsOpen()) {
|
||||
if (Actions::DrawToolbarAction(Actions::MakeClearConsoleAction())) {
|
||||
sink->Clear();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
UI::DrawToolbarLabel("Filter");
|
||||
ImGui::SameLine();
|
||||
Actions::DrawToolbarToggleAction(Actions::MakeConsoleInfoFilterAction(m_filterState.ShowInfo()), m_filterState.ShowInfo());
|
||||
ImGui::SameLine();
|
||||
Actions::DrawToolbarToggleAction(Actions::MakeConsoleWarningFilterAction(m_filterState.ShowWarning()), m_filterState.ShowWarning());
|
||||
ImGui::SameLine();
|
||||
Actions::DrawToolbarToggleAction(Actions::MakeConsoleErrorFilterAction(m_filterState.ShowError()), m_filterState.ShowError());
|
||||
Actions::DrawConsoleToolbarActions(*sink, m_filterState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,27 +40,9 @@ void ConsolePanel::Render() {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char* prefix;
|
||||
|
||||
switch (log.level) {
|
||||
case ::XCEngine::Debug::LogLevel::Verbose:
|
||||
case ::XCEngine::Debug::LogLevel::Debug:
|
||||
case ::XCEngine::Debug::LogLevel::Info:
|
||||
prefix = "[INFO] ";
|
||||
break;
|
||||
case ::XCEngine::Debug::LogLevel::Warning:
|
||||
prefix = "[WARN] ";
|
||||
break;
|
||||
case ::XCEngine::Debug::LogLevel::Error:
|
||||
case ::XCEngine::Debug::LogLevel::Fatal:
|
||||
prefix = "[ERROR] ";
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::PushID(static_cast<int>(logIndex));
|
||||
|
||||
const char* category = ::XCEngine::Debug::LogCategoryToString(log.category);
|
||||
std::string fullMessage = std::string(prefix) + "[" + category + "] " + log.message.CStr();
|
||||
const std::string fullMessage = UI::BuildConsoleLogText(log);
|
||||
if (UI::DrawConsoleLogRow(fullMessage.c_str())) {
|
||||
ImGui::SetClipboardText(fullMessage.c_str());
|
||||
}
|
||||
|
||||
@@ -16,14 +16,11 @@ namespace Editor {
|
||||
HierarchyPanel::HierarchyPanel() : Panel("Hierarchy") {
|
||||
}
|
||||
|
||||
HierarchyPanel::~HierarchyPanel() {
|
||||
if (m_context) {
|
||||
m_context->GetEventBus().Unsubscribe<SelectionChangedEvent>(m_selectionHandlerId);
|
||||
m_context->GetEventBus().Unsubscribe<EntityRenameRequestedEvent>(m_renameRequestHandlerId);
|
||||
}
|
||||
void HierarchyPanel::OnAttach() {
|
||||
if (!m_context || m_selectionHandlerId || m_renameRequestHandlerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
void HierarchyPanel::OnAttach() {
|
||||
m_selectionHandlerId = m_context->GetEventBus().Subscribe<SelectionChangedEvent>(
|
||||
[this](const SelectionChangedEvent& event) {
|
||||
OnSelectionChanged(event);
|
||||
@@ -36,6 +33,21 @@ void HierarchyPanel::OnAttach() {
|
||||
);
|
||||
}
|
||||
|
||||
void HierarchyPanel::OnDetach() {
|
||||
if (!m_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_selectionHandlerId) {
|
||||
m_context->GetEventBus().Unsubscribe<SelectionChangedEvent>(m_selectionHandlerId);
|
||||
m_selectionHandlerId = 0;
|
||||
}
|
||||
if (m_renameRequestHandlerId) {
|
||||
m_context->GetEventBus().Unsubscribe<EntityRenameRequestedEvent>(m_renameRequestHandlerId);
|
||||
m_renameRequestHandlerId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void HierarchyPanel::OnSelectionChanged(const SelectionChangedEvent& event) {
|
||||
if (m_renameState.IsActive() && event.primarySelection != m_renameState.Item()) {
|
||||
CancelRename();
|
||||
|
||||
@@ -13,9 +13,9 @@ enum class SortMode { Name, ComponentCount, TransformFirst };
|
||||
class HierarchyPanel : public Panel {
|
||||
public:
|
||||
HierarchyPanel();
|
||||
~HierarchyPanel();
|
||||
|
||||
void OnAttach() override;
|
||||
void OnDetach() override;
|
||||
void Render() override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "Actions/InspectorActionRouter.h"
|
||||
#include "Actions/ActionRouting.h"
|
||||
#include "Actions/EditorActions.h"
|
||||
#include "Commands/ComponentCommands.h"
|
||||
#include "InspectorPanel.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
#include "Core/ISceneManager.h"
|
||||
@@ -19,9 +19,28 @@ namespace Editor {
|
||||
InspectorPanel::InspectorPanel() : Panel("Inspector") {}
|
||||
|
||||
InspectorPanel::~InspectorPanel() {
|
||||
if (m_context) {
|
||||
m_context->GetEventBus().Unsubscribe<SelectionChangedEvent>(m_selectionHandlerId);
|
||||
}
|
||||
|
||||
void InspectorPanel::OnAttach() {
|
||||
if (!m_context || m_selectionHandlerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_selectionHandlerId = m_context->GetEventBus().Subscribe<SelectionChangedEvent>(
|
||||
[this](const SelectionChangedEvent& event) {
|
||||
OnSelectionChanged(event);
|
||||
}
|
||||
);
|
||||
m_selectedEntityId = m_context->GetSelectionManager().GetSelectedEntity();
|
||||
}
|
||||
|
||||
void InspectorPanel::OnDetach() {
|
||||
if (!m_context || !m_selectionHandlerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_context->GetEventBus().Unsubscribe<SelectionChangedEvent>(m_selectionHandlerId);
|
||||
m_selectionHandlerId = 0;
|
||||
}
|
||||
|
||||
void InspectorPanel::OnSelectionChanged(const SelectionChangedEvent& event) {
|
||||
@@ -40,16 +59,6 @@ void InspectorPanel::Render() {
|
||||
|
||||
Actions::ObserveInactiveActionRoute(*m_context);
|
||||
|
||||
if (!m_selectionHandlerId && m_context) {
|
||||
m_selectionHandlerId = m_context->GetEventBus().Subscribe<SelectionChangedEvent>(
|
||||
[this](const SelectionChangedEvent& event) {
|
||||
OnSelectionChanged(event);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
m_selectedEntityId = m_context->GetSelectionManager().GetSelectedEntity();
|
||||
|
||||
if (m_selectedEntityId) {
|
||||
auto& sceneManager = m_context->GetSceneManager();
|
||||
auto* gameObject = sceneManager.GetEntity(m_selectedEntityId);
|
||||
@@ -107,32 +116,7 @@ void InspectorPanel::RenderAddComponentPopup(::XCEngine::Components::GameObject*
|
||||
return;
|
||||
}
|
||||
|
||||
bool drewAnyEntry = false;
|
||||
for (const auto& editor : ComponentEditorRegistry::Get().GetEditors()) {
|
||||
if (!editor || !editor->ShowInAddComponentMenu()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
drewAnyEntry = true;
|
||||
const bool canAdd = Commands::CanAddComponent(*editor, gameObject);
|
||||
std::string label = editor->GetDisplayName();
|
||||
if (!canAdd) {
|
||||
const char* reason = editor->GetAddDisabledReason(gameObject);
|
||||
if (reason && reason[0] != '\0') {
|
||||
label += " (";
|
||||
label += reason;
|
||||
label += ")";
|
||||
}
|
||||
}
|
||||
|
||||
Actions::DrawMenuAction(Actions::MakeAddComponentMenuAction(label, canAdd), [&]() {
|
||||
if (Commands::AddComponent(*m_context, *editor, gameObject)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!drewAnyEntry) {
|
||||
if (!Actions::DrawInspectorAddComponentMenu(*m_context, gameObject)) {
|
||||
UI::DrawHintText("No registered component editors");
|
||||
}
|
||||
|
||||
@@ -145,14 +129,15 @@ void InspectorPanel::RenderComponent(::XCEngine::Components::Component* componen
|
||||
IComponentEditor* editor = ComponentEditorRegistry::Get().FindEditor(component);
|
||||
const std::string name = component->GetName();
|
||||
|
||||
const UI::ComponentSectionResult section =
|
||||
UI::BeginComponentSection(
|
||||
bool removed = false;
|
||||
const UI::ComponentSectionResult section = UI::BeginComponentSection(
|
||||
(void*)typeid(*component).hash_code(),
|
||||
name.c_str(),
|
||||
Commands::CanRemoveComponent(component, editor));
|
||||
[&]() {
|
||||
removed = Actions::DrawInspectorComponentMenu(*m_context, component, gameObject, editor);
|
||||
});
|
||||
|
||||
if (section.removeRequested) {
|
||||
Commands::RemoveComponent(*m_context, component, gameObject, editor);
|
||||
if (removed) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ public:
|
||||
InspectorPanel();
|
||||
~InspectorPanel();
|
||||
|
||||
void OnAttach() override;
|
||||
void OnDetach() override;
|
||||
void Render() override;
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user