diff --git a/docs/plan/NewEditor_TreeResidualDuplicationClosurePlan_2026-04-22.md b/docs/plan/NewEditor_TreeResidualDuplicationClosurePlan_2026-04-22.md index 4015eb38..1f73a7f3 100644 --- a/docs/plan/NewEditor_TreeResidualDuplicationClosurePlan_2026-04-22.md +++ b/docs/plan/NewEditor_TreeResidualDuplicationClosurePlan_2026-04-22.md @@ -1,7 +1,7 @@ # NewEditor Tree Residual Duplication Closure Plan Date: `2026-04-22` -Status: `Active` +Status: `In Progress` ## Goal @@ -60,6 +60,8 @@ Notes: ### Phase A. Hosted Panel Command Focus Dedup +Status: `Completed` + Target: - Consolidate hosted-panel command focus claiming into an existing app-level module. @@ -75,8 +77,16 @@ Validation: - Build `XCUIEditorApp` - Smoke test `XCUIEditor.exe` +Completed result: + +- Consolidated hosted panel command focus claiming into `EditorCommandFocusService.h` +- Removed duplicated `ClaimCommandFocus(...)` implementations from `HierarchyPanel`, `ProjectPanel`, and `InspectorPanel` +- Verified by build and smoke test + ### Phase B. Tree Rename Host Skeleton Dedup +Status: `Pending` + Target: - Reduce duplicated tree rename session host code while preserving local business commit behavior. @@ -96,6 +106,8 @@ Validation: ### Phase C. Final Re-Audit Of Tree Skeleton Duplication +Status: `Pending` + Target: - Re-check whether any remaining shared tree call sequence should still be extracted. diff --git a/new_editor/app/Features/Hierarchy/HierarchyPanel.cpp b/new_editor/app/Features/Hierarchy/HierarchyPanel.cpp index c58c3d7b..d6210e1d 100644 --- a/new_editor/app/Features/Hierarchy/HierarchyPanel.cpp +++ b/new_editor/app/Features/Hierarchy/HierarchyPanel.cpp @@ -296,31 +296,6 @@ void HierarchyPanel::SyncTreeFocusState( } } -void HierarchyPanel::ClaimCommandFocus( - const std::vector& inputEvents, - const UIRect& bounds, - bool allowInteraction) { - if (m_commandFocusService == nullptr) { - return; - } - - for (const UIInputEvent& event : inputEvents) { - if (event.type == ::XCEngine::UI::UIInputEventType::FocusGained) { - m_commandFocusService->ClaimFocus(EditorActionRoute::Hierarchy); - return; - } - - if (!allowInteraction || - event.type != ::XCEngine::UI::UIInputEventType::PointerButtonDown || - !ContainsPoint(bounds, event.position)) { - continue; - } - - m_commandFocusService->ClaimFocus(EditorActionRoute::Hierarchy); - return; - } -} - UIRect HierarchyPanel::BuildRenameBounds( std::string_view itemId, const Widgets::UIEditorTreeViewLayout& layout) const { @@ -554,7 +529,9 @@ void HierarchyPanel::Update( dispatchEntry.focusGained, dispatchEntry.focusLost); SyncTreeFocusState(filteredEvents); - ClaimCommandFocus( + TryClaimHostedPanelCommandFocus( + m_commandFocusService, + EditorActionRoute::Hierarchy, filteredEvents, dispatchEntry.bounds, dispatchEntry.allowInteraction); diff --git a/new_editor/app/Features/Hierarchy/HierarchyPanel.h b/new_editor/app/Features/Hierarchy/HierarchyPanel.h index 7aebff4e..8f9431c0 100644 --- a/new_editor/app/Features/Hierarchy/HierarchyPanel.h +++ b/new_editor/app/Features/Hierarchy/HierarchyPanel.h @@ -81,10 +81,6 @@ private: const Widgets::UIEditorTreeViewLayout& layout); void SyncTreeFocusState( const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents); - void ClaimCommandFocus( - const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, - const ::XCEngine::UI::UIRect& bounds, - bool allowInteraction); ::XCEngine::UI::UIRect BuildRenameBounds( std::string_view itemId, const Widgets::UIEditorTreeViewLayout& layout) const; diff --git a/new_editor/app/Features/Inspector/InspectorPanel.cpp b/new_editor/app/Features/Inspector/InspectorPanel.cpp index 4c281c1e..a457e3a3 100644 --- a/new_editor/app/Features/Inspector/InspectorPanel.cpp +++ b/new_editor/app/Features/Inspector/InspectorPanel.cpp @@ -196,30 +196,6 @@ UIRect InspectorPanel::BuildGridBounds() const { return UIRect(x, y, width, height); } -void InspectorPanel::ClaimCommandFocus( - const std::vector& inputEvents, - bool allowInteraction) { - if (m_commandFocusService == nullptr) { - return; - } - - for (const UIInputEvent& event : inputEvents) { - if (event.type == UIInputEventType::FocusGained) { - m_commandFocusService->ClaimFocus(EditorActionRoute::Inspector); - return; - } - - if (!allowInteraction || - event.type != UIInputEventType::PointerButtonDown || - !ContainsPoint(m_bounds, event.position)) { - continue; - } - - m_commandFocusService->ClaimFocus(EditorActionRoute::Inspector); - return; - } -} - const InspectorPresentationComponentBinding* InspectorPanel::FindSelectedComponentBinding() const { if (!m_fieldSelection.HasSelection()) { return nullptr; @@ -525,7 +501,12 @@ void InspectorPanel::Update( }, dispatchEntry.focusGained, dispatchEntry.focusLost); - ClaimCommandFocus(filteredEvents, dispatchEntry.allowInteraction); + TryClaimHostedPanelCommandFocus( + m_commandFocusService, + EditorActionRoute::Inspector, + filteredEvents, + m_bounds, + dispatchEntry.allowInteraction); m_gridFrame = UpdateUIEditorPropertyGridInteraction( m_interactionState, m_fieldSelection, diff --git a/new_editor/app/Features/Inspector/InspectorPanel.h b/new_editor/app/Features/Inspector/InspectorPanel.h index 9cda1008..91be4ac5 100644 --- a/new_editor/app/Features/Inspector/InspectorPanel.h +++ b/new_editor/app/Features/Inspector/InspectorPanel.h @@ -45,9 +45,6 @@ private: std::string BuildSubjectKey() const; float ResolveHeaderHeight() const; ::XCEngine::UI::UIRect BuildGridBounds() const; - void ClaimCommandFocus( - const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, - bool allowInteraction); const InspectorPresentationComponentBinding* FindSelectedComponentBinding() const; const Widgets::UIEditorPropertyGridField* FindField(std::string_view fieldId) const; Widgets::UIEditorPropertyGridField* FindMutableField(std::string_view fieldId); diff --git a/new_editor/app/Features/Project/ProjectPanel.cpp b/new_editor/app/Features/Project/ProjectPanel.cpp index 1106370c..d87a1f08 100644 --- a/new_editor/app/Features/Project/ProjectPanel.cpp +++ b/new_editor/app/Features/Project/ProjectPanel.cpp @@ -1404,31 +1404,6 @@ void ProjectPanel::ResetTransientFrames() { m_splitterDragging = false; } -void ProjectPanel::ClaimCommandFocus( - const std::vector& inputEvents, - const UIRect& bounds, - bool allowInteraction) { - if (m_commandFocusService == nullptr) { - return; - } - - for (const UIInputEvent& event : inputEvents) { - if (event.type == UIInputEventType::FocusGained) { - m_commandFocusService->ClaimFocus(EditorActionRoute::Project); - return; - } - - if (!allowInteraction || - event.type != UIInputEventType::PointerButtonDown || - !ContainsPoint(bounds, event.position)) { - continue; - } - - m_commandFocusService->ClaimFocus(EditorActionRoute::Project); - return; - } -} - void ProjectPanel::Update( const UIEditorHostedPanelDispatchEntry& dispatchEntry, const std::vector& inputEvents) { @@ -1489,7 +1464,9 @@ void ProjectPanel::Update( }, dispatchEntry.focusGained, dispatchEntry.focusLost); - ClaimCommandFocus( + TryClaimHostedPanelCommandFocus( + m_commandFocusService, + EditorActionRoute::Project, filteredEvents, dispatchEntry.bounds, dispatchEntry.allowInteraction); diff --git a/new_editor/app/Features/Project/ProjectPanel.h b/new_editor/app/Features/Project/ProjectPanel.h index 053a3d08..3b1102fd 100644 --- a/new_editor/app/Features/Project/ProjectPanel.h +++ b/new_editor/app/Features/Project/ProjectPanel.h @@ -215,10 +215,6 @@ private: const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, const ::XCEngine::UI::UIRect& bounds, const UIEditorHostedPanelDispatchEntry& dispatchEntry) const; - void ClaimCommandFocus( - const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, - const ::XCEngine::UI::UIRect& bounds, - bool allowInteraction); UIEditorHostCommandEvaluationResult EvaluateAssetCommand( std::string_view commandId, std::string_view explicitItemId, diff --git a/new_editor/app/State/EditorCommandFocusService.h b/new_editor/app/State/EditorCommandFocusService.h index 5c0300b9..159ef0e6 100644 --- a/new_editor/app/State/EditorCommandFocusService.h +++ b/new_editor/app/State/EditorCommandFocusService.h @@ -2,6 +2,10 @@ #include "State/EditorSession.h" +#include + +#include + namespace XCEngine::UI::Editor::App { class EditorCommandFocusService { @@ -39,4 +43,43 @@ private: EditorActionRoute m_explicitRoute = EditorActionRoute::None; }; +inline bool ShouldClaimHostedPanelCommandFocus( + const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, + const ::XCEngine::UI::UIRect& bounds, + bool allowInteraction) { + for (const ::XCEngine::UI::UIInputEvent& event : inputEvents) { + if (event.type == ::XCEngine::UI::UIInputEventType::FocusGained) { + return true; + } + + if (!allowInteraction || + event.type != ::XCEngine::UI::UIInputEventType::PointerButtonDown) { + continue; + } + + if (event.position.x >= bounds.x && + event.position.x <= bounds.x + bounds.width && + event.position.y >= bounds.y && + event.position.y <= bounds.y + bounds.height) { + return true; + } + } + + return false; +} + +inline bool TryClaimHostedPanelCommandFocus( + EditorCommandFocusService* service, + EditorActionRoute route, + const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, + const ::XCEngine::UI::UIRect& bounds, + bool allowInteraction) { + return service != nullptr && + ShouldClaimHostedPanelCommandFocus( + inputEvents, + bounds, + allowInteraction) && + service->ClaimFocus(route); +} + } // namespace XCEngine::UI::Editor::App