Extract editor interaction states

This commit is contained in:
2026-03-26 21:30:46 +08:00
parent 6467d87b81
commit f6286d432c
6 changed files with 106 additions and 25 deletions

View File

@@ -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 局部尺寸。

View File

@@ -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 <typename ItemId, size_t BufferCapacity>
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

View File

@@ -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<int>(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) {

View File

@@ -1,6 +1,7 @@
#pragma once
#include "Panel.h"
#include "UI/PopupState.h"
#include <XCEngine/Components/GameObject.h>
#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<uint64_t, 256> m_renameState;
SortMode m_sortMode = SortMode::Name;
uint64_t m_selectionHandlerId = 0;
bool m_needsRefresh = false;
};
}

View File

@@ -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;
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "Panel.h"
#include "UI/PopupState.h"
#include <cstdint>
@@ -28,6 +29,7 @@ private:
uint64_t m_selectionHandlerId = 0;
uint64_t m_selectedEntityId = 0;
UI::DeferredPopupState m_addComponentPopup;
};
}