From f6286d432c44d1d933ae087fcd8ea1a3bb2b9886 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Thu, 26 Mar 2026 21:30:46 +0800 Subject: [PATCH] Extract editor interaction states --- docs/plan/Editor重构3.26.md | 7 +-- editor/src/UI/PopupState.h | 74 ++++++++++++++++++++++++++++ editor/src/panels/HierarchyPanel.cpp | 36 ++++++++------ editor/src/panels/HierarchyPanel.h | 7 +-- editor/src/panels/InspectorPanel.cpp | 5 +- editor/src/panels/InspectorPanel.h | 2 + 6 files changed, 106 insertions(+), 25 deletions(-) diff --git a/docs/plan/Editor重构3.26.md b/docs/plan/Editor重构3.26.md index ff9b0d72..00a6bada 100644 --- a/docs/plan/Editor重构3.26.md +++ b/docs/plan/Editor重构3.26.md @@ -41,6 +41,7 @@ - hierarchy tree node - asset tile - deferred popup / modal state +- inline text edit state - inspector component section - dialog action row - property grid / scalar / vector 控件 @@ -146,10 +147,10 @@ - 创建 / 删除 / 重命名 / 复制 / 粘贴 / Duplicate / 重挂接走 command - 快捷键已接 action 层 - 重命名状态已收成 `Begin / Commit / Cancel` +- 重命名交互已从 panel 局部字段收口到 shared inline edit state 仍待完成: -- 重命名状态机进一步下沉 - 拖拽 / 空白区域目标等细节继续统一 ### Project @@ -175,6 +176,7 @@ - 组件增删已走 command - 组件内容编辑大部分已走 property grid - Add Component 按钮与 popup 项已接 action 层 +- Add Component popup 已接 shared popup state 仍待完成: @@ -213,10 +215,9 @@ 2. 继续将 panel 的本地状态机抽离 包括: - - hierarchy rename state - - project popup / dialog state - inspector component popup state - console filter state + - 其他仍散落在 panel 内的临时交互状态 3. 将剩余少量视觉硬编码继续下沉到 token / widget 层 尤其是 `ProjectPanel` 中的 icon color 和一部分 panel 局部尺寸。 diff --git a/editor/src/UI/PopupState.h b/editor/src/UI/PopupState.h index 0052f1b6..5cce8c12 100644 --- a/editor/src/UI/PopupState.h +++ b/editor/src/UI/PopupState.h @@ -27,6 +27,10 @@ public: return m_openRequested; } + void Clear() { + m_openRequested = false; + } + private: bool m_openRequested = false; }; @@ -77,6 +81,76 @@ private: char m_buffer[BufferCapacity] = {}; }; +template +class InlineTextEditState { +public: + void Begin(ItemId itemId, const char* initialValue = "") { + m_active = true; + m_itemId = itemId; + SetValue(initialValue); + m_focusRequested = true; + } + + bool IsActive() const { + return m_active; + } + + bool IsEditing(ItemId itemId) const { + return m_active && m_itemId == itemId; + } + + ItemId Item() const { + return m_itemId; + } + + bool ConsumeFocusRequest() { + if (!m_focusRequested) { + return false; + } + + m_focusRequested = false; + return true; + } + + void SetValue(const char* value) { + if (value && value[0] != '\0') { + strcpy_s(m_buffer, value); + return; + } + + m_buffer[0] = '\0'; + } + + bool Empty() const { + return m_buffer[0] == '\0'; + } + + char* Buffer() { + return m_buffer; + } + + const char* Buffer() const { + return m_buffer; + } + + constexpr size_t BufferSize() const { + return BufferCapacity; + } + + void Cancel() { + m_active = false; + m_itemId = ItemId{}; + m_buffer[0] = '\0'; + m_focusRequested = false; + } + +private: + bool m_active = false; + ItemId m_itemId = ItemId{}; + char m_buffer[BufferCapacity] = {}; + bool m_focusRequested = false; +}; + } // namespace UI } // namespace Editor } // namespace XCEngine diff --git a/editor/src/panels/HierarchyPanel.cpp b/editor/src/panels/HierarchyPanel.cpp index 4d04aacf..126e78c4 100644 --- a/editor/src/panels/HierarchyPanel.cpp +++ b/editor/src/panels/HierarchyPanel.cpp @@ -32,7 +32,9 @@ void HierarchyPanel::OnAttach() { } void HierarchyPanel::OnSelectionChanged(const SelectionChangedEvent& event) { - m_needsRefresh = true; + if (m_renameState.IsActive() && event.primarySelection != m_renameState.Item()) { + CancelRename(); + } } void HierarchyPanel::Render() { @@ -61,7 +63,7 @@ void HierarchyPanel::Render() { } if (ImGui::IsWindowHovered() && ImGui::IsMouseDown(0) && !ImGui::IsAnyItemHovered()) { - if (!m_renaming) { + if (!m_renameState.IsActive()) { m_context->GetSelectionManager().ClearSelection(); } } @@ -137,14 +139,17 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* gameObject ImGui::PushID(static_cast(gameObject->GetID())); - if (m_renaming && m_renamingEntity == gameObject) { - if (m_renameJustStarted) { + if (m_renameState.IsEditing(gameObject->GetID())) { + if (m_renameState.ConsumeFocusRequest()) { ImGui::SetKeyboardFocusHere(); - m_renameJustStarted = false; } ImGui::SetNextItemWidth(-1); - if (ImGui::InputText("##Rename", m_renameBuffer, sizeof(m_renameBuffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) { + if (ImGui::InputText( + "##Rename", + m_renameState.Buffer(), + m_renameState.BufferSize(), + ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) { CommitRename(); } @@ -253,25 +258,24 @@ void HierarchyPanel::BeginRename(::XCEngine::Components::GameObject* gameObject) return; } - m_renaming = true; - m_renamingEntity = gameObject; - strcpy_s(m_renameBuffer, gameObject->GetName().c_str()); - m_renameJustStarted = true; + m_renameState.Begin(gameObject->GetID(), gameObject->GetName().c_str()); } void HierarchyPanel::CommitRename() { - if (m_renamingEntity && strlen(m_renameBuffer) > 0) { - Commands::RenameEntity(*m_context, m_renamingEntity->GetID(), m_renameBuffer); + if (!m_renameState.IsActive()) { + return; + } + + const uint64_t entityId = m_renameState.Item(); + if (!m_renameState.Empty() && m_context->GetSceneManager().GetEntity(entityId)) { + Commands::RenameEntity(*m_context, entityId, m_renameState.Buffer()); } CancelRename(); } void HierarchyPanel::CancelRename() { - m_renaming = false; - m_renamingEntity = nullptr; - m_renameBuffer[0] = '\0'; - m_renameJustStarted = false; + m_renameState.Cancel(); } void HierarchyPanel::HandleDragDrop(::XCEngine::Components::GameObject* gameObject) { diff --git a/editor/src/panels/HierarchyPanel.h b/editor/src/panels/HierarchyPanel.h index fcdfbc02..57846e21 100644 --- a/editor/src/panels/HierarchyPanel.h +++ b/editor/src/panels/HierarchyPanel.h @@ -1,6 +1,7 @@ #pragma once #include "Panel.h" +#include "UI/PopupState.h" #include #include "Core/ISceneManager.h" @@ -32,13 +33,9 @@ private: void SortEntities(std::vector<::XCEngine::Components::GameObject*>& entities); char m_searchBuffer[256] = ""; - bool m_renaming = false; - ::XCEngine::Components::GameObject* m_renamingEntity = nullptr; - char m_renameBuffer[256] = ""; - bool m_renameJustStarted = false; + UI::InlineTextEditState m_renameState; SortMode m_sortMode = SortMode::Name; uint64_t m_selectionHandlerId = 0; - bool m_needsRefresh = false; }; } diff --git a/editor/src/panels/InspectorPanel.cpp b/editor/src/panels/InspectorPanel.cpp index 54c31fcc..c8b2ff1f 100644 --- a/editor/src/panels/InspectorPanel.cpp +++ b/editor/src/panels/InspectorPanel.cpp @@ -28,6 +28,7 @@ void InspectorPanel::OnSelectionChanged(const SelectionChangedEvent& event) { m_context->GetUndoManager().FinalizeInteractiveChange(); } m_selectedEntityId = event.primarySelection; + m_addComponentPopup.Clear(); } void InspectorPanel::Render() { @@ -78,7 +79,7 @@ void InspectorPanel::RenderGameObject(::XCEngine::Components::GameObject* gameOb } if (Actions::DrawInspectorAction(Actions::MakeAddComponentButtonAction(gameObject != nullptr))) { - ImGui::OpenPopup("AddComponent"); + m_addComponentPopup.RequestOpen(); } RenderAddComponentPopup(gameObject); @@ -97,6 +98,8 @@ void InspectorPanel::RenderEmptyState(const char* title, const char* subtitle) { } void InspectorPanel::RenderAddComponentPopup(::XCEngine::Components::GameObject* gameObject) { + m_addComponentPopup.ConsumeOpenRequest("AddComponent"); + if (!UI::BeginTitledPopup("AddComponent", "Components")) { return; } diff --git a/editor/src/panels/InspectorPanel.h b/editor/src/panels/InspectorPanel.h index deb2e96a..e4881c7f 100644 --- a/editor/src/panels/InspectorPanel.h +++ b/editor/src/panels/InspectorPanel.h @@ -1,6 +1,7 @@ #pragma once #include "Panel.h" +#include "UI/PopupState.h" #include @@ -28,6 +29,7 @@ private: uint64_t m_selectionHandlerId = 0; uint64_t m_selectedEntityId = 0; + UI::DeferredPopupState m_addComponentPopup; }; }