diff --git a/docs/plan/NewEditor_PortBoundaryRefactorPlan_2026-04-23.md b/docs/plan/NewEditor_PortBoundaryRefactorPlan_2026-04-23.md new file mode 100644 index 00000000..c9f124ae --- /dev/null +++ b/docs/plan/NewEditor_PortBoundaryRefactorPlan_2026-04-23.md @@ -0,0 +1,176 @@ +# NewEditor Port Boundary Refactor Plan + +Date: 2026-04-23 +Status: In Progress + +## 1. Objective + +Refactor `new_editor` so that external boundaries are modeled explicitly instead of being mixed into a single `app/Ports` bucket. + +This plan targets three concrete issues: + +1. `SystemInteractionPort` is a platform/system boundary. +2. `TexturePort` and `ViewportRenderPort` are rendering host boundaries. +3. `ShaderResourceDescriptorAllocatorPort` is not a real application boundary because it leaks `d3d12.h` types directly into the interface. + +The goal is to make the directory structure match the actual architecture, reduce "add another port" cargo cult growth, and make later editor features cheaper to extend. + +## 2. Current Problems + +### 2.1 Mixed abstraction levels + +`app/Ports` currently mixes: + +1. operating-system integration +2. rendering-host integration +3. backend-internal D3D12 helpers + +These are not the same architectural seam and should not share one bucket. + +### 2.2 The word "Port" is being used too broadly + +The current structure encourages a bad rule: + +"If something crosses a layer, add a port." + +That is not the intended use of ports. A port should exist only for a real boundary contract, not for every data handoff or helper collaboration. + +### 2.3 Fake abstraction in D3D12 descriptor allocation + +`ShaderResourceDescriptorAllocatorPort` exposes: + +1. `D3D12_CPU_DESCRIPTOR_HANDLE` +2. `D3D12_GPU_DESCRIPTOR_HANDLE` +3. `UINT` +4. direct D3D12 header dependency + +That makes it a D3D12-specific helper wearing an interface costume. It should be concrete backend code unless there is a proven need for a cross-backend contract. + +## 3. Target End State + +After the refactor: + +1. system-facing services live in a system-oriented location and namespace +2. rendering host contracts live under rendering host boundaries +3. D3D12-only helpers stay D3D12-local unless a backend-agnostic contract is justified +4. new features do not add interfaces by default; they only add one when a real host boundary appears + +## 4. Boundary Rules + +Use a boundary interface only when all of the following are true: + +1. the caller depends on an external capability +2. dependency inversion is useful at the application boundary +3. the contract can be expressed without leaking backend-only details +4. there is a realistic need for substitution, testing, or multiple implementations + +Do not add a boundary interface for: + +1. feature-to-feature communication +2. panel-to-runtime state transfer +3. helpers inside one rendering backend +4. temporary include avoidance + +## 5. Execution Phases + +### Phase A. Split the `app/Ports` bucket by responsibility + +Create explicit homes for: + +1. system interaction service +2. rendering host contracts + +Then move call sites to the new locations and names. + +### Phase B. Remove the fake D3D12 port + +Delete `ShaderResourceDescriptorAllocatorPort` and make the descriptor allocator a concrete D3D12 helper again. + +### Phase C. Clean up forwarding and naming + +Replace `Ports::...` references with names that explain the real role: + +1. `System::SystemInteractionService` +2. `Rendering::Host::UiTextureHost` +3. `Rendering::Host::ViewportRenderHost` + +### Phase D. Validate and harden + +1. ensure no remaining includes depend on `app/Ports` +2. ensure the app still builds +3. document the rule for future extension + +## 6. File Plan + +### New files + +1. `new_editor/app/System/SystemInteractionService.h` +2. `new_editor/app/Rendering/Host/HostFwd.h` +3. `new_editor/app/Rendering/Host/UiTextureHost.h` +4. `new_editor/app/Rendering/Host/ViewportRenderHost.h` + +### Files expected to be updated + +1. `new_editor/app/Bootstrap/Application.h` +2. `new_editor/app/Bootstrap/Application.cpp` +3. `new_editor/app/Composition/EditorContext.h` +4. `new_editor/app/Composition/EditorContext.cpp` +5. `new_editor/app/Composition/EditorShellRuntime.h` +6. `new_editor/app/Composition/EditorShellRuntime.cpp` +7. `new_editor/app/Features/Project/ProjectPanel.h` +8. `new_editor/app/Features/Project/ProjectPanel.cpp` +9. `new_editor/app/Features/Scene/SceneViewportFeature.h` +10. `new_editor/app/Features/Scene/SceneViewportFeature.cpp` +11. `new_editor/app/Features/Scene/SceneViewportController.h` +12. `new_editor/app/Features/Scene/SceneViewportController.cpp` +13. `new_editor/app/Features/Scene/SceneViewportToolOverlay.h` +14. `new_editor/app/Features/Scene/SceneViewportToolOverlay.cpp` +15. `new_editor/app/Rendering/Assets/BuiltInIcons.h` +16. `new_editor/app/Rendering/Assets/BuiltInIcons.cpp` +17. `new_editor/app/Rendering/D3D12/D3D12UiTextureHost.h` +18. `new_editor/app/Rendering/D3D12/D3D12WindowRenderer.h` +19. `new_editor/app/Rendering/D3D12/D3D12ShaderResourceDescriptorAllocator.h` +20. `new_editor/app/Rendering/Viewport/ViewportHostService.h` +21. `new_editor/app/Rendering/Viewport/ViewportHostService.cpp` +22. `new_editor/app/Rendering/Viewport/ViewportRenderTargets.h` +23. `new_editor/app/Rendering/Viewport/ViewportRenderTargets.cpp` +24. `new_editor/app/Support/EmbeddedPngLoader.h` +25. `new_editor/app/Support/EmbeddedPngLoader.cpp` +26. `new_editor/app/Platform/Win32/System/Win32SystemInteractionHost.h` +27. `new_editor/app/Platform/Win32/Content/EditorWindowContentController.h` + +### Files expected to be retired + +1. `new_editor/app/Ports/SystemInteractionPort.h` +2. `new_editor/app/Ports/TexturePort.h` +3. `new_editor/app/Ports/ViewportRenderPort.h` +4. `new_editor/app/Ports/PortFwd.h` +5. `new_editor/app/Ports/ShaderResourceDescriptorAllocatorPort.h` + +## 7. First Execution Slice + +Execute the smallest high-value slice first: + +1. introduce the new system and rendering host interface headers +2. switch call sites away from `Ports::` +3. remove `ShaderResourceDescriptorAllocatorPort` +4. leave behavior unchanged + +This keeps risk lower while immediately fixing the worst architectural confusion. + +## 8. Validation + +Minimum validation for this refactor: + +1. `rg "Ports::|Ports/" new_editor/app` returns no production references +2. `ShaderResourceDescriptorAllocatorPort` no longer exists +3. `XCUIEditorApp` compiles, or any build failure is isolated and reported precisely + +## 9. Completion Criteria + +This refactor is complete only when: + +1. `app/Ports` is no longer the active boundary bucket +2. system and rendering host boundaries are visibly separated +3. D3D12-only helpers are no longer presented as generic app ports +4. future contributors can answer "where should a new boundary go?" without guessing diff --git a/engine/include/XCEngine/Components/GameObject.h b/engine/include/XCEngine/Components/GameObject.h index 5abd137f..b79f933f 100644 --- a/engine/include/XCEngine/Components/GameObject.h +++ b/engine/include/XCEngine/Components/GameObject.h @@ -189,6 +189,7 @@ public: GameObject* GetParent() const { return m_parent; } void SetParent(GameObject* parent); void SetParent(GameObject* parent, bool worldPositionStays); + void SetParent(GameObject* parent, size_t siblingIndex, bool worldPositionStays); size_t GetChildCount() const { return m_children.size(); } GameObject* GetChild(size_t index) const; diff --git a/engine/src/Components/GameObject.cpp b/engine/src/Components/GameObject.cpp index 18c5cf2e..64efd24f 100644 --- a/engine/src/Components/GameObject.cpp +++ b/engine/src/Components/GameObject.cpp @@ -114,6 +114,22 @@ void GameObject::SetParent(GameObject* parent, bool worldPositionStays) { return; } + SetParent( + parent, + parent != nullptr + ? parent->m_children.size() + : (m_scene != nullptr ? m_scene->m_rootGameObjects.size() : 0u), + worldPositionStays); +} + +void GameObject::SetParent( + GameObject* parent, + size_t siblingIndex, + bool worldPositionStays) { + if (parent == this) { + return; + } + const bool oldParentActiveInHierarchy = m_parent ? m_parent->IsActiveInHierarchy() : true; const bool wasActiveInHierarchy = oldParentActiveInHierarchy && m_activeSelf; @@ -128,11 +144,16 @@ void GameObject::SetParent(GameObject* parent, bool worldPositionStays) { m_parent = parent; if (m_parent) { - m_parent->m_children.push_back(this); + auto& siblings = m_parent->m_children; + const size_t insertIndex = (std::min)(siblingIndex, siblings.size()); + siblings.insert(siblings.begin() + static_cast(insertIndex), this); } else if (m_scene) { auto& roots = m_scene->m_rootGameObjects; if (std::find(roots.begin(), roots.end(), m_id) == roots.end()) { - roots.push_back(m_id); + const size_t insertIndex = (std::min)(siblingIndex, roots.size()); + roots.insert( + roots.begin() + static_cast(insertIndex), + m_id); } } diff --git a/new_editor/app/Features/Hierarchy/HierarchyPanel.cpp b/new_editor/app/Features/Hierarchy/HierarchyPanel.cpp index 754ebf40..f4ffd13a 100644 --- a/new_editor/app/Features/Hierarchy/HierarchyPanel.cpp +++ b/new_editor/app/Features/Hierarchy/HierarchyPanel.cpp @@ -473,6 +473,28 @@ void HierarchyPanel::ProcessDragAndFrameEvents( return model.CanReparent(draggedItemId, targetItemId); } + bool CanInsertBeforeItem( + std::string_view draggedItemId, + std::string_view targetItemId) const { + if (draggedItemId.empty() || + targetItemId.empty() || + draggedItemId == targetItemId) { + return false; + } + + const std::optional targetParentId = + model.GetParentId(targetItemId); + return targetParentId.has_value() + ? model.CanReparent(draggedItemId, targetParentId.value()) + : model.ContainsNode(draggedItemId); + } + + bool CanInsertAfterItem( + std::string_view draggedItemId, + std::string_view targetItemId) const { + return CanInsertBeforeItem(draggedItemId, targetItemId); + } + bool CanDropToRoot(std::string_view draggedItemId) const { return model.GetParentId(draggedItemId).has_value(); } @@ -484,6 +506,20 @@ void HierarchyPanel::ProcessDragAndFrameEvents( sceneRuntime->ReparentGameObject(draggedItemId, targetItemId); } + bool CommitInsertBeforeItem( + std::string_view draggedItemId, + std::string_view targetItemId) { + return sceneRuntime != nullptr && + sceneRuntime->MoveGameObjectBefore(draggedItemId, targetItemId); + } + + bool CommitInsertAfterItem( + std::string_view draggedItemId, + std::string_view targetItemId) { + return sceneRuntime != nullptr && + sceneRuntime->MoveGameObjectAfter(draggedItemId, targetItemId); + } + bool CommitDropToRoot(std::string_view draggedItemId) { return sceneRuntime != nullptr && sceneRuntime->MoveGameObjectToRoot(draggedItemId); @@ -705,7 +741,7 @@ void HierarchyPanel::Append(UIDrawList& drawList) const { m_treeFrame.layout, presentedItems, m_dragState.dragging && m_dragState.validDropTarget, - m_dragState.dropToRoot, + m_dragState.dropPlacement, m_dragState.dropTargetItemId, kDragPreviewColor, 1.0f, diff --git a/new_editor/app/Platform/Win32/BorderlessWindowChrome.cpp b/new_editor/app/Platform/Win32/Chrome/BorderlessWindowChrome.cpp similarity index 100% rename from new_editor/app/Platform/Win32/BorderlessWindowChrome.cpp rename to new_editor/app/Platform/Win32/Chrome/BorderlessWindowChrome.cpp diff --git a/new_editor/app/Platform/Win32/BorderlessWindowChrome.h b/new_editor/app/Platform/Win32/Chrome/BorderlessWindowChrome.h similarity index 100% rename from new_editor/app/Platform/Win32/BorderlessWindowChrome.h rename to new_editor/app/Platform/Win32/Chrome/BorderlessWindowChrome.h diff --git a/new_editor/app/Platform/Win32/BorderlessWindowFrame.cpp b/new_editor/app/Platform/Win32/Chrome/BorderlessWindowFrame.cpp similarity index 100% rename from new_editor/app/Platform/Win32/BorderlessWindowFrame.cpp rename to new_editor/app/Platform/Win32/Chrome/BorderlessWindowFrame.cpp diff --git a/new_editor/app/Platform/Win32/BorderlessWindowFrame.h b/new_editor/app/Platform/Win32/Chrome/BorderlessWindowFrame.h similarity index 100% rename from new_editor/app/Platform/Win32/BorderlessWindowFrame.h rename to new_editor/app/Platform/Win32/Chrome/BorderlessWindowFrame.h diff --git a/new_editor/app/Platform/Win32/EditorWindowChromeController.cpp b/new_editor/app/Platform/Win32/Chrome/EditorWindowChromeController.cpp similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowChromeController.cpp rename to new_editor/app/Platform/Win32/Chrome/EditorWindowChromeController.cpp diff --git a/new_editor/app/Platform/Win32/EditorWindowChromeController.h b/new_editor/app/Platform/Win32/Chrome/EditorWindowChromeController.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowChromeController.h rename to new_editor/app/Platform/Win32/Chrome/EditorWindowChromeController.h diff --git a/new_editor/app/Platform/Win32/HostRuntimeState.h b/new_editor/app/Platform/Win32/Chrome/HostRuntimeState.h similarity index 100% rename from new_editor/app/Platform/Win32/HostRuntimeState.h rename to new_editor/app/Platform/Win32/Chrome/HostRuntimeState.h diff --git a/new_editor/app/Platform/Win32/EditorAddComponentUtilityWindowContentController.cpp b/new_editor/app/Platform/Win32/Content/EditorAddComponentUtilityWindowContentController.cpp similarity index 100% rename from new_editor/app/Platform/Win32/EditorAddComponentUtilityWindowContentController.cpp rename to new_editor/app/Platform/Win32/Content/EditorAddComponentUtilityWindowContentController.cpp diff --git a/new_editor/app/Platform/Win32/EditorAddComponentUtilityWindowContentController.h b/new_editor/app/Platform/Win32/Content/EditorAddComponentUtilityWindowContentController.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorAddComponentUtilityWindowContentController.h rename to new_editor/app/Platform/Win32/Content/EditorAddComponentUtilityWindowContentController.h diff --git a/new_editor/app/Platform/Win32/EditorColorPickerUtilityWindowContentController.cpp b/new_editor/app/Platform/Win32/Content/EditorColorPickerUtilityWindowContentController.cpp similarity index 100% rename from new_editor/app/Platform/Win32/EditorColorPickerUtilityWindowContentController.cpp rename to new_editor/app/Platform/Win32/Content/EditorColorPickerUtilityWindowContentController.cpp diff --git a/new_editor/app/Platform/Win32/EditorColorPickerUtilityWindowContentController.h b/new_editor/app/Platform/Win32/Content/EditorColorPickerUtilityWindowContentController.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorColorPickerUtilityWindowContentController.h rename to new_editor/app/Platform/Win32/Content/EditorColorPickerUtilityWindowContentController.h diff --git a/new_editor/app/Platform/Win32/EditorStandaloneUtilityWindowContentController.cpp b/new_editor/app/Platform/Win32/Content/EditorStandaloneUtilityWindowContentController.cpp similarity index 100% rename from new_editor/app/Platform/Win32/EditorStandaloneUtilityWindowContentController.cpp rename to new_editor/app/Platform/Win32/Content/EditorStandaloneUtilityWindowContentController.cpp diff --git a/new_editor/app/Platform/Win32/EditorStandaloneUtilityWindowContentController.h b/new_editor/app/Platform/Win32/Content/EditorStandaloneUtilityWindowContentController.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorStandaloneUtilityWindowContentController.h rename to new_editor/app/Platform/Win32/Content/EditorStandaloneUtilityWindowContentController.h diff --git a/new_editor/app/Platform/Win32/EditorUtilityWindowKind.h b/new_editor/app/Platform/Win32/Content/EditorUtilityWindowKind.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorUtilityWindowKind.h rename to new_editor/app/Platform/Win32/Content/EditorUtilityWindowKind.h diff --git a/new_editor/app/Platform/Win32/EditorUtilityWindowRegistry.cpp b/new_editor/app/Platform/Win32/Content/EditorUtilityWindowRegistry.cpp similarity index 100% rename from new_editor/app/Platform/Win32/EditorUtilityWindowRegistry.cpp rename to new_editor/app/Platform/Win32/Content/EditorUtilityWindowRegistry.cpp diff --git a/new_editor/app/Platform/Win32/EditorUtilityWindowRegistry.h b/new_editor/app/Platform/Win32/Content/EditorUtilityWindowRegistry.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorUtilityWindowRegistry.h rename to new_editor/app/Platform/Win32/Content/EditorUtilityWindowRegistry.h diff --git a/new_editor/app/Platform/Win32/EditorWindowContentController.h b/new_editor/app/Platform/Win32/Content/EditorWindowContentController.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowContentController.h rename to new_editor/app/Platform/Win32/Content/EditorWindowContentController.h diff --git a/new_editor/app/Platform/Win32/EditorWorkspaceWindowContentController.cpp b/new_editor/app/Platform/Win32/Content/EditorWorkspaceWindowContentController.cpp similarity index 100% rename from new_editor/app/Platform/Win32/EditorWorkspaceWindowContentController.cpp rename to new_editor/app/Platform/Win32/Content/EditorWorkspaceWindowContentController.cpp diff --git a/new_editor/app/Platform/Win32/EditorWorkspaceWindowContentController.h b/new_editor/app/Platform/Win32/Content/EditorWorkspaceWindowContentController.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWorkspaceWindowContentController.h rename to new_editor/app/Platform/Win32/Content/EditorWorkspaceWindowContentController.h diff --git a/new_editor/app/Platform/Win32/EditorWindowFrameDriver.cpp b/new_editor/app/Platform/Win32/Runtime/EditorWindowFrameDriver.cpp similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowFrameDriver.cpp rename to new_editor/app/Platform/Win32/Runtime/EditorWindowFrameDriver.cpp diff --git a/new_editor/app/Platform/Win32/EditorWindowFrameDriver.h b/new_editor/app/Platform/Win32/Runtime/EditorWindowFrameDriver.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowFrameDriver.h rename to new_editor/app/Platform/Win32/Runtime/EditorWindowFrameDriver.h diff --git a/new_editor/app/Platform/Win32/EditorWindowFrameOrchestrator.cpp b/new_editor/app/Platform/Win32/Runtime/EditorWindowFrameOrchestrator.cpp similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowFrameOrchestrator.cpp rename to new_editor/app/Platform/Win32/Runtime/EditorWindowFrameOrchestrator.cpp diff --git a/new_editor/app/Platform/Win32/EditorWindowFrameOrchestrator.h b/new_editor/app/Platform/Win32/Runtime/EditorWindowFrameOrchestrator.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowFrameOrchestrator.h rename to new_editor/app/Platform/Win32/Runtime/EditorWindowFrameOrchestrator.h diff --git a/new_editor/app/Platform/Win32/EditorWindowInputController.cpp b/new_editor/app/Platform/Win32/Runtime/EditorWindowInputController.cpp similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowInputController.cpp rename to new_editor/app/Platform/Win32/Runtime/EditorWindowInputController.cpp diff --git a/new_editor/app/Platform/Win32/EditorWindowInputController.h b/new_editor/app/Platform/Win32/Runtime/EditorWindowInputController.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowInputController.h rename to new_editor/app/Platform/Win32/Runtime/EditorWindowInputController.h diff --git a/new_editor/app/Platform/Win32/EditorWindowRuntimeController.cpp b/new_editor/app/Platform/Win32/Runtime/EditorWindowRuntimeController.cpp similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowRuntimeController.cpp rename to new_editor/app/Platform/Win32/Runtime/EditorWindowRuntimeController.cpp diff --git a/new_editor/app/Platform/Win32/EditorWindowRuntimeController.h b/new_editor/app/Platform/Win32/Runtime/EditorWindowRuntimeController.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowRuntimeController.h rename to new_editor/app/Platform/Win32/Runtime/EditorWindowRuntimeController.h diff --git a/new_editor/app/Platform/Win32/EditorWindowScreenshotController.cpp b/new_editor/app/Platform/Win32/Runtime/EditorWindowScreenshotController.cpp similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowScreenshotController.cpp rename to new_editor/app/Platform/Win32/Runtime/EditorWindowScreenshotController.cpp diff --git a/new_editor/app/Platform/Win32/EditorWindowScreenshotController.h b/new_editor/app/Platform/Win32/Runtime/EditorWindowScreenshotController.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowScreenshotController.h rename to new_editor/app/Platform/Win32/Runtime/EditorWindowScreenshotController.h diff --git a/new_editor/app/Platform/Win32/InputModifierTracker.h b/new_editor/app/Platform/Win32/Runtime/InputModifierTracker.h similarity index 100% rename from new_editor/app/Platform/Win32/InputModifierTracker.h rename to new_editor/app/Platform/Win32/Runtime/InputModifierTracker.h diff --git a/new_editor/app/Platform/Win32/Win32SystemInteractionHost.cpp b/new_editor/app/Platform/Win32/System/Win32SystemInteractionHost.cpp similarity index 100% rename from new_editor/app/Platform/Win32/Win32SystemInteractionHost.cpp rename to new_editor/app/Platform/Win32/System/Win32SystemInteractionHost.cpp diff --git a/new_editor/app/Platform/Win32/Win32SystemInteractionHost.h b/new_editor/app/Platform/Win32/System/Win32SystemInteractionHost.h similarity index 100% rename from new_editor/app/Platform/Win32/Win32SystemInteractionHost.h rename to new_editor/app/Platform/Win32/System/Win32SystemInteractionHost.h diff --git a/new_editor/app/Platform/Win32/EditorFloatingWindowPlacement.cpp b/new_editor/app/Platform/Win32/Windowing/EditorFloatingWindowPlacement.cpp similarity index 100% rename from new_editor/app/Platform/Win32/EditorFloatingWindowPlacement.cpp rename to new_editor/app/Platform/Win32/Windowing/EditorFloatingWindowPlacement.cpp diff --git a/new_editor/app/Platform/Win32/EditorFloatingWindowPlacement.h b/new_editor/app/Platform/Win32/Windowing/EditorFloatingWindowPlacement.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorFloatingWindowPlacement.h rename to new_editor/app/Platform/Win32/Windowing/EditorFloatingWindowPlacement.h diff --git a/new_editor/app/Platform/Win32/WindowManager/EditorUtilityWindowCoordinator.cpp b/new_editor/app/Platform/Win32/Windowing/EditorUtilityWindowCoordinator.cpp similarity index 100% rename from new_editor/app/Platform/Win32/WindowManager/EditorUtilityWindowCoordinator.cpp rename to new_editor/app/Platform/Win32/Windowing/EditorUtilityWindowCoordinator.cpp diff --git a/new_editor/app/Platform/Win32/WindowManager/EditorUtilityWindowCoordinator.h b/new_editor/app/Platform/Win32/Windowing/EditorUtilityWindowCoordinator.h similarity index 100% rename from new_editor/app/Platform/Win32/WindowManager/EditorUtilityWindowCoordinator.h rename to new_editor/app/Platform/Win32/Windowing/EditorUtilityWindowCoordinator.h diff --git a/new_editor/app/Platform/Win32/EditorWindow.cpp b/new_editor/app/Platform/Win32/Windowing/EditorWindow.cpp similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindow.cpp rename to new_editor/app/Platform/Win32/Windowing/EditorWindow.cpp diff --git a/new_editor/app/Platform/Win32/EditorWindow.h b/new_editor/app/Platform/Win32/Windowing/EditorWindow.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindow.h rename to new_editor/app/Platform/Win32/Windowing/EditorWindow.h diff --git a/new_editor/app/Platform/Win32/WindowManager/EditorWindowHostRuntime.cpp b/new_editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp similarity index 100% rename from new_editor/app/Platform/Win32/WindowManager/EditorWindowHostRuntime.cpp rename to new_editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp diff --git a/new_editor/app/Platform/Win32/WindowManager/EditorWindowHostRuntime.h b/new_editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.h similarity index 100% rename from new_editor/app/Platform/Win32/WindowManager/EditorWindowHostRuntime.h rename to new_editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.h diff --git a/new_editor/app/Platform/Win32/WindowManager/EditorWindowLifecycleCoordinator.cpp b/new_editor/app/Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.cpp similarity index 100% rename from new_editor/app/Platform/Win32/WindowManager/EditorWindowLifecycleCoordinator.cpp rename to new_editor/app/Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.cpp diff --git a/new_editor/app/Platform/Win32/WindowManager/EditorWindowLifecycleCoordinator.h b/new_editor/app/Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.h similarity index 100% rename from new_editor/app/Platform/Win32/WindowManager/EditorWindowLifecycleCoordinator.h rename to new_editor/app/Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.h diff --git a/new_editor/app/Platform/Win32/WindowManager/EditorWindowManager.cpp b/new_editor/app/Platform/Win32/Windowing/EditorWindowManager.cpp similarity index 100% rename from new_editor/app/Platform/Win32/WindowManager/EditorWindowManager.cpp rename to new_editor/app/Platform/Win32/Windowing/EditorWindowManager.cpp diff --git a/new_editor/app/Platform/Win32/EditorWindowManager.h b/new_editor/app/Platform/Win32/Windowing/EditorWindowManager.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowManager.h rename to new_editor/app/Platform/Win32/Windowing/EditorWindowManager.h diff --git a/new_editor/app/Platform/Win32/WindowManager/EditorWindowMessageDispatcher.cpp b/new_editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.cpp similarity index 100% rename from new_editor/app/Platform/Win32/WindowManager/EditorWindowMessageDispatcher.cpp rename to new_editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.cpp diff --git a/new_editor/app/Platform/Win32/WindowManager/EditorWindowMessageDispatcher.h b/new_editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.h similarity index 100% rename from new_editor/app/Platform/Win32/WindowManager/EditorWindowMessageDispatcher.h rename to new_editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.h diff --git a/new_editor/app/Platform/Win32/EditorWindowPointerCapture.h b/new_editor/app/Platform/Win32/Windowing/EditorWindowPointerCapture.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowPointerCapture.h rename to new_editor/app/Platform/Win32/Windowing/EditorWindowPointerCapture.h diff --git a/new_editor/app/Platform/Win32/EditorWindowState.h b/new_editor/app/Platform/Win32/Windowing/EditorWindowState.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowState.h rename to new_editor/app/Platform/Win32/Windowing/EditorWindowState.h diff --git a/new_editor/app/Platform/Win32/EditorWindowSupport.h b/new_editor/app/Platform/Win32/Windowing/EditorWindowSupport.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowSupport.h rename to new_editor/app/Platform/Win32/Windowing/EditorWindowSupport.h diff --git a/new_editor/app/Platform/Win32/EditorWindowTransferRequests.h b/new_editor/app/Platform/Win32/Windowing/EditorWindowTransferRequests.h similarity index 100% rename from new_editor/app/Platform/Win32/EditorWindowTransferRequests.h rename to new_editor/app/Platform/Win32/Windowing/EditorWindowTransferRequests.h diff --git a/new_editor/app/Platform/Win32/WindowManager/EditorWindowWorkspaceCoordinator.cpp b/new_editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.cpp similarity index 100% rename from new_editor/app/Platform/Win32/WindowManager/EditorWindowWorkspaceCoordinator.cpp rename to new_editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.cpp diff --git a/new_editor/app/Platform/Win32/WindowManager/EditorWindowWorkspaceCoordinator.h b/new_editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h similarity index 100% rename from new_editor/app/Platform/Win32/WindowManager/EditorWindowWorkspaceCoordinator.h rename to new_editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h diff --git a/new_editor/app/Scene/EditorSceneBridge.cpp b/new_editor/app/Scene/EditorSceneBridge.cpp index 43a90f9a..409c5852 100644 --- a/new_editor/app/Scene/EditorSceneBridge.cpp +++ b/new_editor/app/Scene/EditorSceneBridge.cpp @@ -145,6 +145,79 @@ bool WouldCreateCycle( return false; } +std::vector ResolveSiblingSequence( + Scene& scene, + GameObject* parent) { + return parent != nullptr + ? parent->GetChildren() + : scene.GetRootGameObjects(); +} + +std::optional FindSiblingIndex( + const std::vector& siblings, + const GameObject* gameObject) { + if (gameObject == nullptr) { + return std::nullopt; + } + + const auto it = + std::find(siblings.begin(), siblings.end(), gameObject); + if (it == siblings.end()) { + return std::nullopt; + } + + return static_cast(std::distance(siblings.begin(), it)); +} + +bool MoveGameObjectRelativeToTarget( + std::string_view itemId, + std::string_view targetItemId, + bool placeAfterTarget) { + Scene* scene = ResolvePrimaryScene(); + GameObject* gameObject = FindEditorGameObject(itemId); + GameObject* target = FindEditorGameObject(targetItemId); + if (scene == nullptr || + gameObject == nullptr || + target == nullptr || + gameObject == target) { + return false; + } + + GameObject* targetParent = target->GetParent(); + if (targetParent != nullptr && + WouldCreateCycle(*gameObject, *targetParent)) { + return false; + } + + const std::vector siblings = + ResolveSiblingSequence(*scene, targetParent); + const std::optional targetIndex = + FindSiblingIndex(siblings, target); + if (!targetIndex.has_value()) { + return false; + } + + std::size_t finalIndex = + targetIndex.value() + (placeAfterTarget ? 1u : 0u); + if (gameObject->GetParent() == targetParent) { + const std::optional sourceIndex = + FindSiblingIndex(siblings, gameObject); + if (sourceIndex.has_value()) { + if ((!placeAfterTarget && sourceIndex.value() < targetIndex.value()) || + (placeAfterTarget && sourceIndex.value() <= targetIndex.value())) { + --finalIndex; + } + + if (finalIndex == sourceIndex.value()) { + return false; + } + } + } + + gameObject->SetParent(targetParent, finalIndex, true); + return true; +} + } // namespace EditorStartupSceneResult EnsureEditorStartupScene( @@ -330,6 +403,18 @@ bool ReparentEditorGameObject( return true; } +bool MoveEditorGameObjectBefore( + std::string_view itemId, + std::string_view targetItemId) { + return MoveGameObjectRelativeToTarget(itemId, targetItemId, false); +} + +bool MoveEditorGameObjectAfter( + std::string_view itemId, + std::string_view targetItemId) { + return MoveGameObjectRelativeToTarget(itemId, targetItemId, true); +} + bool MoveEditorGameObjectToRoot(std::string_view itemId) { GameObject* gameObject = FindEditorGameObject(itemId); if (gameObject == nullptr || gameObject->GetParent() == nullptr) { diff --git a/new_editor/app/Scene/EditorSceneBridge.h b/new_editor/app/Scene/EditorSceneBridge.h index 215c5382..1c0f2bb3 100644 --- a/new_editor/app/Scene/EditorSceneBridge.h +++ b/new_editor/app/Scene/EditorSceneBridge.h @@ -39,6 +39,12 @@ std::string DuplicateEditorGameObject(std::string_view itemId); bool ReparentEditorGameObject( std::string_view itemId, std::string_view parentItemId); +bool MoveEditorGameObjectBefore( + std::string_view itemId, + std::string_view targetItemId); +bool MoveEditorGameObjectAfter( + std::string_view itemId, + std::string_view targetItemId); bool MoveEditorGameObjectToRoot(std::string_view itemId); } // namespace XCEngine::UI::Editor::App diff --git a/new_editor/app/Scene/EditorSceneRuntime.cpp b/new_editor/app/Scene/EditorSceneRuntime.cpp index f6085b74..3ebc3ca3 100644 --- a/new_editor/app/Scene/EditorSceneRuntime.cpp +++ b/new_editor/app/Scene/EditorSceneRuntime.cpp @@ -463,6 +463,32 @@ bool EditorSceneRuntime::ReparentGameObject( return reparented; } +bool EditorSceneRuntime::MoveGameObjectBefore( + std::string_view itemId, + std::string_view targetItemId) { + ResetTransformEditHistory(); + const bool moved = + MoveEditorGameObjectBefore(itemId, targetItemId); + if (moved) { + IncrementInspectorRevision(); + } + RefreshScene(); + return moved; +} + +bool EditorSceneRuntime::MoveGameObjectAfter( + std::string_view itemId, + std::string_view targetItemId) { + ResetTransformEditHistory(); + const bool moved = + MoveEditorGameObjectAfter(itemId, targetItemId); + if (moved) { + IncrementInspectorRevision(); + } + RefreshScene(); + return moved; +} + bool EditorSceneRuntime::MoveGameObjectToRoot(std::string_view itemId) { ResetTransformEditHistory(); const bool moved = MoveEditorGameObjectToRoot(itemId); diff --git a/new_editor/app/Scene/EditorSceneRuntime.h b/new_editor/app/Scene/EditorSceneRuntime.h index ddd613ed..c72fd5be 100644 --- a/new_editor/app/Scene/EditorSceneRuntime.h +++ b/new_editor/app/Scene/EditorSceneRuntime.h @@ -93,6 +93,12 @@ public: bool ReparentGameObject( std::string_view itemId, std::string_view parentItemId); + bool MoveGameObjectBefore( + std::string_view itemId, + std::string_view targetItemId); + bool MoveGameObjectAfter( + std::string_view itemId, + std::string_view targetItemId); bool MoveGameObjectToRoot(std::string_view itemId); bool AddComponentToSelectedGameObject(std::string_view componentTypeName); bool CanRemoveSelectedComponent(std::string_view componentId) const; diff --git a/new_editor/include/XCEditor/Collections/UIEditorTreeDragDrop.h b/new_editor/include/XCEditor/Collections/UIEditorTreeDragDrop.h index a1e72cc0..60603cd3 100644 --- a/new_editor/include/XCEditor/Collections/UIEditorTreeDragDrop.h +++ b/new_editor/include/XCEditor/Collections/UIEditorTreeDragDrop.h @@ -5,6 +5,8 @@ #include +#include +#include #include #include #include @@ -18,9 +20,12 @@ using Widgets::HitTestUIEditorTreeView; using Widgets::UIEditorTreeViewHitTarget; using Widgets::UIEditorTreeViewHitTargetKind; using Widgets::UIEditorTreeViewInvalidIndex; +using ::XCEngine::UI::Editor::UIEditorTreeViewDropPreviewPlacement; struct State : DragDropInteraction::State { bool dropToRoot = false; + UIEditorTreeViewDropPreviewPlacement dropPlacement = + UIEditorTreeViewDropPreviewPlacement::None; }; struct ProcessResult : DragDropInteraction::ProcessResult { @@ -42,6 +47,77 @@ inline bool ContainsPoint(const UIRect& rect, const UIPoint& point) { point.y <= rect.y + rect.height; } +template +inline constexpr bool SupportsSiblingInsertion() { + return requires( + Callbacks& callbacks, + std::string_view draggedItemId, + std::string_view targetItemId) { + { callbacks.CanInsertBeforeItem(draggedItemId, targetItemId) } -> std::convertible_to; + { callbacks.CanInsertAfterItem(draggedItemId, targetItemId) } -> std::convertible_to; + { callbacks.CommitInsertBeforeItem(draggedItemId, targetItemId) } -> std::convertible_to; + { callbacks.CommitInsertAfterItem(draggedItemId, targetItemId) } -> std::convertible_to; + }; +} + +inline UIEditorTreeViewDropPreviewPlacement ResolveRowDropPlacement( + const Widgets::UIEditorTreeViewLayout& layout, + const UIEditorTreeViewHitTarget& hitTarget, + const UIPoint& point) { + if (hitTarget.visibleIndex >= layout.rowRects.size()) { + return UIEditorTreeViewDropPreviewPlacement::None; + } + + const UIRect& rowRect = layout.rowRects[hitTarget.visibleIndex]; + const float edgeBand = + (std::min)((std::max)(rowRect.height * 0.25f, 4.0f), 8.0f); + if (point.y <= rowRect.y + edgeBand) { + return UIEditorTreeViewDropPreviewPlacement::BeforeItem; + } + if (point.y >= rowRect.y + rowRect.height - edgeBand) { + return UIEditorTreeViewDropPreviewPlacement::AfterItem; + } + + return UIEditorTreeViewDropPreviewPlacement::OnItem; +} + +inline const Widgets::UIEditorTreeViewItem* ResolveGapInsertionItem( + const Widgets::UIEditorTreeViewLayout& layout, + const std::vector& items, + const UIPoint& point, + UIEditorTreeViewDropPreviewPlacement& outPlacement) { + outPlacement = UIEditorTreeViewDropPreviewPlacement::None; + if (layout.rowRects.empty()) { + return nullptr; + } + + if (point.y < layout.rowRects.front().y) { + outPlacement = UIEditorTreeViewDropPreviewPlacement::BeforeItem; + const std::size_t itemIndex = layout.visibleItemIndices.front(); + return itemIndex < items.size() ? &items[itemIndex] : nullptr; + } + + for (std::size_t visibleIndex = 0u; visibleIndex + 1u < layout.rowRects.size(); ++visibleIndex) { + const UIRect& currentRow = layout.rowRects[visibleIndex]; + const UIRect& nextRow = layout.rowRects[visibleIndex + 1u]; + if (point.y > currentRow.y + currentRow.height && + point.y < nextRow.y) { + outPlacement = UIEditorTreeViewDropPreviewPlacement::BeforeItem; + const std::size_t itemIndex = layout.visibleItemIndices[visibleIndex + 1u]; + return itemIndex < items.size() ? &items[itemIndex] : nullptr; + } + } + + const UIRect& lastRow = layout.rowRects.back(); + if (point.y > lastRow.y + lastRow.height) { + outPlacement = UIEditorTreeViewDropPreviewPlacement::AfterItem; + const std::size_t itemIndex = layout.visibleItemIndices.back(); + return itemIndex < items.size() ? &items[itemIndex] : nullptr; + } + + return nullptr; +} + inline const Widgets::UIEditorTreeViewItem* ResolveHitItem( const Widgets::UIEditorTreeViewLayout& layout, const std::vector& items, @@ -177,6 +253,7 @@ ProcessResult ProcessInputEvents( void ResetDropTarget(State& state) const { state.dropTargetItemId.clear(); state.dropToRoot = false; + state.dropPlacement = UIEditorTreeViewDropPreviewPlacement::None; state.validDropTarget = false; } @@ -190,14 +267,71 @@ ProcessResult ProcessInputEvents( if (hitItem != nullptr && (hitTarget.kind == UIEditorTreeViewHitTargetKind::Row || hitTarget.kind == UIEditorTreeViewHitTargetKind::Disclosure)) { + UIEditorTreeViewDropPreviewPlacement placement = + UIEditorTreeViewDropPreviewPlacement::OnItem; + if constexpr (SupportsSiblingInsertion()) { + placement = + hitTarget.kind == UIEditorTreeViewHitTargetKind::Disclosure + ? UIEditorTreeViewDropPreviewPlacement::OnItem + : ResolveRowDropPlacement(layout, hitTarget, point); + } + state.dropTargetItemId = hitItem->itemId; - state.validDropTarget = - callbacks.CanDropOnItem(draggedItemId, state.dropTargetItemId); + state.dropPlacement = placement; + switch (placement) { + case UIEditorTreeViewDropPreviewPlacement::BeforeItem: + if constexpr (SupportsSiblingInsertion()) { + state.validDropTarget = + callbacks.CanInsertBeforeItem( + draggedItemId, + state.dropTargetItemId); + } + break; + + case UIEditorTreeViewDropPreviewPlacement::AfterItem: + if constexpr (SupportsSiblingInsertion()) { + state.validDropTarget = + callbacks.CanInsertAfterItem( + draggedItemId, + state.dropTargetItemId); + } + break; + + case UIEditorTreeViewDropPreviewPlacement::OnItem: + state.validDropTarget = + callbacks.CanDropOnItem(draggedItemId, state.dropTargetItemId); + break; + + default: + break; + } return; } + if constexpr (SupportsSiblingInsertion()) { + UIEditorTreeViewDropPreviewPlacement gapPlacement = + UIEditorTreeViewDropPreviewPlacement::None; + const Widgets::UIEditorTreeViewItem* gapItem = + ResolveGapInsertionItem(layout, items, point, gapPlacement); + if (gapItem != nullptr && + gapPlacement != UIEditorTreeViewDropPreviewPlacement::None) { + state.dropTargetItemId = gapItem->itemId; + state.dropPlacement = gapPlacement; + state.validDropTarget = + gapPlacement == UIEditorTreeViewDropPreviewPlacement::BeforeItem + ? callbacks.CanInsertBeforeItem( + draggedItemId, + state.dropTargetItemId) + : callbacks.CanInsertAfterItem( + draggedItemId, + state.dropTargetItemId); + return; + } + } + if (ContainsPoint(bounds, point)) { state.dropToRoot = true; + state.dropPlacement = UIEditorTreeViewDropPreviewPlacement::Root; state.validDropTarget = callbacks.CanDropToRoot(draggedItemId); } } @@ -212,11 +346,35 @@ ProcessResult ProcessInputEvents( bool CommitDrop(State& state, DragDropInteraction::ProcessResult&) { droppedToRoot = state.dropToRoot; - return state.dropToRoot - ? callbacks.CommitDropToRoot(state.draggedItemId) - : callbacks.CommitDropOnItem( + switch (state.dropPlacement) { + case UIEditorTreeViewDropPreviewPlacement::BeforeItem: + if constexpr (SupportsSiblingInsertion()) { + return callbacks.CommitInsertBeforeItem( + state.draggedItemId, + state.dropTargetItemId); + } + return false; + + case UIEditorTreeViewDropPreviewPlacement::AfterItem: + if constexpr (SupportsSiblingInsertion()) { + return callbacks.CommitInsertAfterItem( + state.draggedItemId, + state.dropTargetItemId); + } + return false; + + case UIEditorTreeViewDropPreviewPlacement::Root: + return callbacks.CommitDropToRoot(state.draggedItemId); + + case UIEditorTreeViewDropPreviewPlacement::OnItem: + return callbacks.CommitDropOnItem( state.draggedItemId, state.dropTargetItemId); + + case UIEditorTreeViewDropPreviewPlacement::None: + default: + return false; + } } } adaptedCallbacks{ layout, items, bounds, callbacks }; diff --git a/new_editor/include/XCEditor/Collections/UIEditorTreeView.h b/new_editor/include/XCEditor/Collections/UIEditorTreeView.h index f49e44f2..931b2ea0 100644 --- a/new_editor/include/XCEditor/Collections/UIEditorTreeView.h +++ b/new_editor/include/XCEditor/Collections/UIEditorTreeView.h @@ -164,6 +164,14 @@ void AppendUIEditorTreeView( namespace XCEngine::UI::Editor { +enum class UIEditorTreeViewDropPreviewPlacement : std::uint8_t { + None = 0, + OnItem, + BeforeItem, + AfterItem, + Root +}; + bool HasUIEditorTreeViewValidBounds( const ::XCEngine::UI::UIRect& bounds); @@ -200,6 +208,17 @@ void UpdateUIEditorTreeViewInlineRenameSession( const ::XCEngine::UI::UIRect& bounds, const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents); +void AppendUIEditorTreeViewDropPreview( + ::XCEngine::UI::UIDrawList& drawList, + const Widgets::UIEditorTreeViewLayout& layout, + const std::vector& items, + bool active, + UIEditorTreeViewDropPreviewPlacement placement, + std::string_view dropTargetItemId, + const ::XCEngine::UI::UIColor& previewColor, + float borderThickness = 1.0f, + float cornerRounding = 0.0f); + void AppendUIEditorTreeViewDropPreview( ::XCEngine::UI::UIDrawList& drawList, const Widgets::UIEditorTreeViewLayout& layout, diff --git a/new_editor/src/Collections/UIEditorTreeView.cpp b/new_editor/src/Collections/UIEditorTreeView.cpp index c941661d..decd7fde 100644 --- a/new_editor/src/Collections/UIEditorTreeView.cpp +++ b/new_editor/src/Collections/UIEditorTreeView.cpp @@ -516,7 +516,7 @@ void AppendUIEditorTreeViewDropPreview( const Widgets::UIEditorTreeViewLayout& layout, const std::vector& items, bool active, - bool dropToRoot, + UIEditorTreeViewDropPreviewPlacement placement, std::string_view dropTargetItemId, const ::XCEngine::UI::UIColor& previewColor, float borderThickness, @@ -525,7 +525,7 @@ void AppendUIEditorTreeViewDropPreview( return; } - if (dropToRoot) { + if (placement == UIEditorTreeViewDropPreviewPlacement::Root) { drawList.AddRectOutline( layout.bounds, previewColor, @@ -541,8 +541,58 @@ void AppendUIEditorTreeViewDropPreview( return; } - drawList.AddRectOutline( - layout.rowRects[visibleIndex], + const UIRect& rowRect = layout.rowRects[visibleIndex]; + switch (placement) { + case UIEditorTreeViewDropPreviewPlacement::OnItem: + drawList.AddRectOutline( + rowRect, + previewColor, + borderThickness, + cornerRounding); + break; + + case UIEditorTreeViewDropPreviewPlacement::BeforeItem: + drawList.AddLine( + ::XCEngine::UI::UIPoint(rowRect.x, rowRect.y), + ::XCEngine::UI::UIPoint(rowRect.x + rowRect.width, rowRect.y), + previewColor, + borderThickness); + break; + + case UIEditorTreeViewDropPreviewPlacement::AfterItem: + drawList.AddLine( + ::XCEngine::UI::UIPoint(rowRect.x, rowRect.y + rowRect.height), + ::XCEngine::UI::UIPoint(rowRect.x + rowRect.width, rowRect.y + rowRect.height), + previewColor, + borderThickness); + break; + + case UIEditorTreeViewDropPreviewPlacement::None: + case UIEditorTreeViewDropPreviewPlacement::Root: + default: + break; + } +} + +void AppendUIEditorTreeViewDropPreview( + ::XCEngine::UI::UIDrawList& drawList, + const Widgets::UIEditorTreeViewLayout& layout, + const std::vector& items, + bool active, + bool dropToRoot, + std::string_view dropTargetItemId, + const ::XCEngine::UI::UIColor& previewColor, + float borderThickness, + float cornerRounding) { + AppendUIEditorTreeViewDropPreview( + drawList, + layout, + items, + active, + dropToRoot + ? UIEditorTreeViewDropPreviewPlacement::Root + : UIEditorTreeViewDropPreviewPlacement::OnItem, + dropTargetItemId, previewColor, borderThickness, cornerRounding);