Extract editor interaction states
This commit is contained in:
@@ -41,6 +41,7 @@
|
|||||||
- hierarchy tree node
|
- hierarchy tree node
|
||||||
- asset tile
|
- asset tile
|
||||||
- deferred popup / modal state
|
- deferred popup / modal state
|
||||||
|
- inline text edit state
|
||||||
- inspector component section
|
- inspector component section
|
||||||
- dialog action row
|
- dialog action row
|
||||||
- property grid / scalar / vector 控件
|
- property grid / scalar / vector 控件
|
||||||
@@ -146,10 +147,10 @@
|
|||||||
- 创建 / 删除 / 重命名 / 复制 / 粘贴 / Duplicate / 重挂接走 command
|
- 创建 / 删除 / 重命名 / 复制 / 粘贴 / Duplicate / 重挂接走 command
|
||||||
- 快捷键已接 action 层
|
- 快捷键已接 action 层
|
||||||
- 重命名状态已收成 `Begin / Commit / Cancel`
|
- 重命名状态已收成 `Begin / Commit / Cancel`
|
||||||
|
- 重命名交互已从 panel 局部字段收口到 shared inline edit state
|
||||||
|
|
||||||
仍待完成:
|
仍待完成:
|
||||||
|
|
||||||
- 重命名状态机进一步下沉
|
|
||||||
- 拖拽 / 空白区域目标等细节继续统一
|
- 拖拽 / 空白区域目标等细节继续统一
|
||||||
|
|
||||||
### Project
|
### Project
|
||||||
@@ -175,6 +176,7 @@
|
|||||||
- 组件增删已走 command
|
- 组件增删已走 command
|
||||||
- 组件内容编辑大部分已走 property grid
|
- 组件内容编辑大部分已走 property grid
|
||||||
- Add Component 按钮与 popup 项已接 action 层
|
- Add Component 按钮与 popup 项已接 action 层
|
||||||
|
- Add Component popup 已接 shared popup state
|
||||||
|
|
||||||
仍待完成:
|
仍待完成:
|
||||||
|
|
||||||
@@ -213,10 +215,9 @@
|
|||||||
|
|
||||||
2. 继续将 panel 的本地状态机抽离
|
2. 继续将 panel 的本地状态机抽离
|
||||||
包括:
|
包括:
|
||||||
- hierarchy rename state
|
|
||||||
- project popup / dialog state
|
|
||||||
- inspector component popup state
|
- inspector component popup state
|
||||||
- console filter state
|
- console filter state
|
||||||
|
- 其他仍散落在 panel 内的临时交互状态
|
||||||
|
|
||||||
3. 将剩余少量视觉硬编码继续下沉到 token / widget 层
|
3. 将剩余少量视觉硬编码继续下沉到 token / widget 层
|
||||||
尤其是 `ProjectPanel` 中的 icon color 和一部分 panel 局部尺寸。
|
尤其是 `ProjectPanel` 中的 icon color 和一部分 panel 局部尺寸。
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ public:
|
|||||||
return m_openRequested;
|
return m_openRequested;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Clear() {
|
||||||
|
m_openRequested = false;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_openRequested = false;
|
bool m_openRequested = false;
|
||||||
};
|
};
|
||||||
@@ -77,6 +81,76 @@ private:
|
|||||||
char m_buffer[BufferCapacity] = {};
|
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 UI
|
||||||
} // namespace Editor
|
} // namespace Editor
|
||||||
} // namespace XCEngine
|
} // namespace XCEngine
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ void HierarchyPanel::OnAttach() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HierarchyPanel::OnSelectionChanged(const SelectionChangedEvent& event) {
|
void HierarchyPanel::OnSelectionChanged(const SelectionChangedEvent& event) {
|
||||||
m_needsRefresh = true;
|
if (m_renameState.IsActive() && event.primarySelection != m_renameState.Item()) {
|
||||||
|
CancelRename();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HierarchyPanel::Render() {
|
void HierarchyPanel::Render() {
|
||||||
@@ -61,7 +63,7 @@ void HierarchyPanel::Render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseDown(0) && !ImGui::IsAnyItemHovered()) {
|
if (ImGui::IsWindowHovered() && ImGui::IsMouseDown(0) && !ImGui::IsAnyItemHovered()) {
|
||||||
if (!m_renaming) {
|
if (!m_renameState.IsActive()) {
|
||||||
m_context->GetSelectionManager().ClearSelection();
|
m_context->GetSelectionManager().ClearSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,14 +139,17 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* gameObject
|
|||||||
|
|
||||||
ImGui::PushID(static_cast<int>(gameObject->GetID()));
|
ImGui::PushID(static_cast<int>(gameObject->GetID()));
|
||||||
|
|
||||||
if (m_renaming && m_renamingEntity == gameObject) {
|
if (m_renameState.IsEditing(gameObject->GetID())) {
|
||||||
if (m_renameJustStarted) {
|
if (m_renameState.ConsumeFocusRequest()) {
|
||||||
ImGui::SetKeyboardFocusHere();
|
ImGui::SetKeyboardFocusHere();
|
||||||
m_renameJustStarted = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(-1);
|
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();
|
CommitRename();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,25 +258,24 @@ void HierarchyPanel::BeginRename(::XCEngine::Components::GameObject* gameObject)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_renaming = true;
|
m_renameState.Begin(gameObject->GetID(), gameObject->GetName().c_str());
|
||||||
m_renamingEntity = gameObject;
|
|
||||||
strcpy_s(m_renameBuffer, gameObject->GetName().c_str());
|
|
||||||
m_renameJustStarted = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HierarchyPanel::CommitRename() {
|
void HierarchyPanel::CommitRename() {
|
||||||
if (m_renamingEntity && strlen(m_renameBuffer) > 0) {
|
if (!m_renameState.IsActive()) {
|
||||||
Commands::RenameEntity(*m_context, m_renamingEntity->GetID(), m_renameBuffer);
|
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();
|
CancelRename();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HierarchyPanel::CancelRename() {
|
void HierarchyPanel::CancelRename() {
|
||||||
m_renaming = false;
|
m_renameState.Cancel();
|
||||||
m_renamingEntity = nullptr;
|
|
||||||
m_renameBuffer[0] = '\0';
|
|
||||||
m_renameJustStarted = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HierarchyPanel::HandleDragDrop(::XCEngine::Components::GameObject* gameObject) {
|
void HierarchyPanel::HandleDragDrop(::XCEngine::Components::GameObject* gameObject) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Panel.h"
|
#include "Panel.h"
|
||||||
|
#include "UI/PopupState.h"
|
||||||
#include <XCEngine/Components/GameObject.h>
|
#include <XCEngine/Components/GameObject.h>
|
||||||
#include "Core/ISceneManager.h"
|
#include "Core/ISceneManager.h"
|
||||||
|
|
||||||
@@ -32,13 +33,9 @@ private:
|
|||||||
void SortEntities(std::vector<::XCEngine::Components::GameObject*>& entities);
|
void SortEntities(std::vector<::XCEngine::Components::GameObject*>& entities);
|
||||||
|
|
||||||
char m_searchBuffer[256] = "";
|
char m_searchBuffer[256] = "";
|
||||||
bool m_renaming = false;
|
UI::InlineTextEditState<uint64_t, 256> m_renameState;
|
||||||
::XCEngine::Components::GameObject* m_renamingEntity = nullptr;
|
|
||||||
char m_renameBuffer[256] = "";
|
|
||||||
bool m_renameJustStarted = false;
|
|
||||||
SortMode m_sortMode = SortMode::Name;
|
SortMode m_sortMode = SortMode::Name;
|
||||||
uint64_t m_selectionHandlerId = 0;
|
uint64_t m_selectionHandlerId = 0;
|
||||||
bool m_needsRefresh = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ void InspectorPanel::OnSelectionChanged(const SelectionChangedEvent& event) {
|
|||||||
m_context->GetUndoManager().FinalizeInteractiveChange();
|
m_context->GetUndoManager().FinalizeInteractiveChange();
|
||||||
}
|
}
|
||||||
m_selectedEntityId = event.primarySelection;
|
m_selectedEntityId = event.primarySelection;
|
||||||
|
m_addComponentPopup.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InspectorPanel::Render() {
|
void InspectorPanel::Render() {
|
||||||
@@ -78,7 +79,7 @@ void InspectorPanel::RenderGameObject(::XCEngine::Components::GameObject* gameOb
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Actions::DrawInspectorAction(Actions::MakeAddComponentButtonAction(gameObject != nullptr))) {
|
if (Actions::DrawInspectorAction(Actions::MakeAddComponentButtonAction(gameObject != nullptr))) {
|
||||||
ImGui::OpenPopup("AddComponent");
|
m_addComponentPopup.RequestOpen();
|
||||||
}
|
}
|
||||||
RenderAddComponentPopup(gameObject);
|
RenderAddComponentPopup(gameObject);
|
||||||
|
|
||||||
@@ -97,6 +98,8 @@ void InspectorPanel::RenderEmptyState(const char* title, const char* subtitle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void InspectorPanel::RenderAddComponentPopup(::XCEngine::Components::GameObject* gameObject) {
|
void InspectorPanel::RenderAddComponentPopup(::XCEngine::Components::GameObject* gameObject) {
|
||||||
|
m_addComponentPopup.ConsumeOpenRequest("AddComponent");
|
||||||
|
|
||||||
if (!UI::BeginTitledPopup("AddComponent", "Components")) {
|
if (!UI::BeginTitledPopup("AddComponent", "Components")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Panel.h"
|
#include "Panel.h"
|
||||||
|
#include "UI/PopupState.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ private:
|
|||||||
|
|
||||||
uint64_t m_selectionHandlerId = 0;
|
uint64_t m_selectionHandlerId = 0;
|
||||||
uint64_t m_selectedEntityId = 0;
|
uint64_t m_selectedEntityId = 0;
|
||||||
|
UI::DeferredPopupState m_addComponentPopup;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user