From 8bfca5e8f25d71a302ac52d22ec9b170e45ba527 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Wed, 22 Apr 2026 00:19:35 +0800 Subject: [PATCH] new_editor: isolate project panel state and harden runtime reset --- new_editor/app/Composition/EditorContext.cpp | 6 +- .../Features/Project/ProjectBrowserModel.cpp | 25 +++----- .../Features/Project/ProjectBrowserModel.h | 11 ++-- .../app/Features/Project/ProjectPanel.cpp | 59 +++++++++++-------- .../app/Features/Project/ProjectPanel.h | 3 + .../app/Project/EditorProjectRuntime.cpp | 15 ++--- new_editor/app/Project/EditorProjectRuntime.h | 8 ++- new_editor/app/Scene/EditorSceneRuntime.cpp | 17 ++++-- new_editor/app/Scene/EditorSceneRuntime.h | 7 +++ .../src/Collections/UIEditorTabStrip.cpp | 52 +++++++++++++++- .../UIEditorPropertyGridInteraction.cpp | 2 +- 11 files changed, 144 insertions(+), 61 deletions(-) diff --git a/new_editor/app/Composition/EditorContext.cpp b/new_editor/app/Composition/EditorContext.cpp index 37443716..2d70b5f1 100644 --- a/new_editor/app/Composition/EditorContext.cpp +++ b/new_editor/app/Composition/EditorContext.cpp @@ -60,12 +60,12 @@ bool EditorContext::Initialize(const std::filesystem::path& repoRoot) { m_session.projectRoot = (repoRoot / "project").lexically_normal(); m_commandFocusService = {}; m_selectionService = {}; - m_projectRuntime = {}; + m_projectRuntime.Reset(); AppendUIEditorRuntimeTrace("startup", "EditorProjectRuntime::Initialize begin"); m_projectRuntime.Initialize(repoRoot); AppendUIEditorRuntimeTrace("startup", "EditorProjectRuntime::Initialize end"); m_projectRuntime.BindSelectionService(&m_selectionService); - m_sceneRuntime = {}; + m_sceneRuntime.Reset(); AppendUIEditorRuntimeTrace("startup", "EditorSceneRuntime::Initialize begin"); m_sceneRuntime.Initialize(m_session.projectRoot); AppendUIEditorRuntimeTrace("startup", "EditorSceneRuntime::Initialize end"); @@ -80,6 +80,8 @@ bool EditorContext::Initialize(const std::filesystem::path& repoRoot) { m_shellServices = {}; m_shellServices.commandDispatcher = &m_shortcutManager.GetCommandDispatcher(); m_shellServices.shortcutManager = &m_shortcutManager; + m_lastStatus.clear(); + m_lastMessage.clear(); SetReadyStatus(); AppendUIEditorRuntimeTrace("startup", "EditorContext::Initialize end"); return true; diff --git a/new_editor/app/Features/Project/ProjectBrowserModel.cpp b/new_editor/app/Features/Project/ProjectBrowserModel.cpp index b858f436..dde41953 100644 --- a/new_editor/app/Features/Project/ProjectBrowserModel.cpp +++ b/new_editor/app/Features/Project/ProjectBrowserModel.cpp @@ -553,7 +553,16 @@ std::string RemapMovedItemId( } // namespace +void ProjectBrowserModel::Reset() { + m_assetsRootPath.clear(); + m_folderEntries.clear(); + m_treeItems.clear(); + m_assetEntries.clear(); + m_currentFolderId.clear(); +} + void ProjectBrowserModel::Initialize(const std::filesystem::path& repoRoot) { + Reset(); m_assetsRootPath = (repoRoot / "project/Assets").lexically_normal(); TraceProjectBrowser("ProjectBrowserModel::Initialize assetsRoot=" + PathToUtf8String(m_assetsRootPath)); std::error_code errorCode = {}; @@ -571,21 +580,6 @@ void ProjectBrowserModel::Initialize(const std::filesystem::path& repoRoot) { std::to_string(m_assetEntries.size())); } -void ProjectBrowserModel::SetFolderIcon(const ::XCEngine::UI::UITextureHandle& icon) { - if (m_folderIcon.nativeHandle == icon.nativeHandle && - m_folderIcon.width == icon.width && - m_folderIcon.height == icon.height && - m_folderIcon.kind == icon.kind && - m_folderIcon.resourceHandle == icon.resourceHandle) { - return; - } - - m_folderIcon = icon; - if (!m_assetsRootPath.empty()) { - RefreshFolderTree(); - } -} - void ProjectBrowserModel::Refresh() { TraceProjectBrowser("ProjectBrowserModel::Refresh begin"); RefreshFolderTree(); @@ -1307,7 +1301,6 @@ void ProjectBrowserModel::RefreshFolderTree() { item.label = folderEntry.label; item.depth = depth; item.forceLeaf = !HasChildDirectories(folderPath); - item.leadingIcon = m_folderIcon; m_treeItems.push_back(std::move(item)); const std::vector childFolders = diff --git a/new_editor/app/Features/Project/ProjectBrowserModel.h b/new_editor/app/Features/Project/ProjectBrowserModel.h index eec22796..3206d608 100644 --- a/new_editor/app/Features/Project/ProjectBrowserModel.h +++ b/new_editor/app/Features/Project/ProjectBrowserModel.h @@ -2,8 +2,6 @@ #include -#include - #include #include #include @@ -49,8 +47,14 @@ public: bool canPreview = false; }; + ProjectBrowserModel() = default; + ProjectBrowserModel(const ProjectBrowserModel&) = default; + ProjectBrowserModel& operator=(const ProjectBrowserModel&) = default; + ProjectBrowserModel(ProjectBrowserModel&&) noexcept = default; + ProjectBrowserModel& operator=(ProjectBrowserModel&&) noexcept = default; + + void Reset(); void Initialize(const std::filesystem::path& repoRoot); - void SetFolderIcon(const ::XCEngine::UI::UITextureHandle& icon); void Refresh(); bool Empty() const; @@ -110,7 +114,6 @@ private: std::vector m_folderEntries = {}; std::vector m_treeItems = {}; std::vector m_assetEntries = {}; - ::XCEngine::UI::UITextureHandle m_folderIcon = {}; std::string m_currentFolderId = {}; }; diff --git a/new_editor/app/Features/Project/ProjectPanel.cpp b/new_editor/app/Features/Project/ProjectPanel.cpp index 1c0b5785..40124b0d 100644 --- a/new_editor/app/Features/Project/ProjectPanel.cpp +++ b/new_editor/app/Features/Project/ProjectPanel.cpp @@ -306,12 +306,26 @@ const ProjectPanel::BrowserModel& ProjectPanel::GetBrowserModel() const { return ResolveProjectRuntime()->GetBrowserModel(); } +void ProjectPanel::RebuildWindowTreeItems() { + m_windowTreeItems.clear(); + if (!HasProjectRuntime()) { + return; + } + + m_windowTreeItems = GetBrowserModel().GetTreeItems(); + const UITextureHandle folderIcon = ResolveFolderIcon(m_icons); + for (Widgets::UIEditorTreeViewItem& item : m_windowTreeItems) { + item.leadingIcon = folderIcon; + } +} + +const std::vector& ProjectPanel::GetWindowTreeItems() const { + return m_windowTreeItems; +} + void ProjectPanel::Initialize(const std::filesystem::path& repoRoot) { m_ownedProjectRuntime = std::make_unique(); m_ownedProjectRuntime->Initialize(repoRoot); - if (m_icons != nullptr) { - m_ownedProjectRuntime->SetFolderIcon(ResolveFolderIcon(m_icons)); - } SyncCurrentFolderSelection(); SyncAssetSelectionFromRuntime(); } @@ -322,9 +336,6 @@ void ProjectPanel::SetProjectRuntime(EditorProjectRuntime* projectRuntime) { } m_projectRuntime = projectRuntime; - if (m_projectRuntime != nullptr && m_icons != nullptr) { - m_projectRuntime->SetFolderIcon(ResolveFolderIcon(m_icons)); - } SyncCurrentFolderSelection(); SyncAssetSelectionFromRuntime(); } @@ -341,10 +352,7 @@ void ProjectPanel::SetSystemInteractionHost( void ProjectPanel::SetBuiltInIcons(BuiltInIcons* icons) { m_icons = icons; - if (EditorProjectRuntime* runtime = ResolveProjectRuntime(); - runtime != nullptr) { - runtime->SetFolderIcon(ResolveFolderIcon(m_icons)); - } + RebuildWindowTreeItems(); } void ProjectPanel::SetTextMeasurer(const UIEditorTextMeasurer* textMeasurer) { @@ -490,7 +498,7 @@ UIRect ProjectPanel::BuildRenameBounds( if (surface == RenameSurface::Tree) { return BuildUIEditorTreePanelInlineRenameBounds( m_treeFrame.layout, - GetBrowserModel().GetTreeItems(), + GetWindowTreeItems(), itemId, hostedMetrics); } @@ -673,10 +681,12 @@ const UIEditorPanelContentHostPanelState* ProjectPanel::FindMountedProjectPanel( void ProjectPanel::SyncCurrentFolderSelection() { if (!HasProjectRuntime()) { + m_windowTreeItems.clear(); m_folderSelection.ClearSelection(); return; } + RebuildWindowTreeItems(); const std::string& currentFolderId = GetBrowserModel().GetCurrentFolderId(); if (currentFolderId.empty()) { m_folderSelection.ClearSelection(); @@ -1103,14 +1113,14 @@ std::vector ProjectPanel::BuildTreeInteractionInputEvents( ? m_treeFrame.layout : Widgets::BuildUIEditorTreeViewLayout( m_layout.treeRect, - GetBrowserModel().GetTreeItems(), + GetWindowTreeItems(), m_folderExpansion, ResolveUIEditorTreeViewMetrics(), m_treeInteractionState.verticalOffset); return BuildUIEditorTreePanelInteractionInputEvents( m_treeDragState, layout, - GetBrowserModel().GetTreeItems(), + GetWindowTreeItems(), rawEvents, m_splitterDragging || m_assetDragState.dragging); } @@ -1521,6 +1531,7 @@ void ProjectPanel::Update( SyncAssetSelectionFromRuntime(); } + RebuildWindowTreeItems(); m_visible = true; SyncAssetSelectionFromRuntime(); const std::vector filteredEvents = @@ -1551,7 +1562,7 @@ void ProjectPanel::Update( const Widgets::UIEditorTreeViewMetrics treeMetrics = ResolveUIEditorTreeViewMetrics(); m_treeFrame.layout = Widgets::BuildUIEditorTreeViewLayout( m_layout.treeRect, - GetBrowserModel().GetTreeItems(), + GetWindowTreeItems(), m_folderExpansion, treeMetrics, m_treeInteractionState.verticalOffset); @@ -1579,7 +1590,7 @@ void ProjectPanel::Update( m_folderSelection, m_folderExpansion, m_layout.treeRect, - GetBrowserModel().GetTreeItems(), + GetWindowTreeItems(), treeEvents, treeMetrics); @@ -1657,7 +1668,7 @@ void ProjectPanel::Update( TreeDrag::ProcessInputEvents( m_treeDragState, m_treeFrame.layout, - GetBrowserModel().GetTreeItems(), + GetWindowTreeItems(), FilterUIEditorTreePanelPointerInputEvents( filteredEvents, m_splitterDragging || m_assetDragState.dragging), @@ -1677,7 +1688,7 @@ void ProjectPanel::Update( m_layout = BuildLayout(panelState->bounds); m_treeFrame.layout = Widgets::BuildUIEditorTreeViewLayout( m_layout.treeRect, - GetBrowserModel().GetTreeItems(), + GetWindowTreeItems(), m_folderExpansion, treeMetrics, m_treeInteractionState.verticalOffset); @@ -1800,7 +1811,7 @@ void ProjectPanel::Update( m_layout = BuildLayout(panelState->bounds); m_treeFrame.layout = Widgets::BuildUIEditorTreeViewLayout( m_layout.treeRect, - GetBrowserModel().GetTreeItems(), + GetWindowTreeItems(), m_folderExpansion, treeMetrics, m_treeInteractionState.verticalOffset); @@ -2148,13 +2159,13 @@ std::string ProjectPanel::ResolveAssetDropTargetItemId( if (ContainsPoint(m_treeFrame.layout.bounds, point)) { Widgets::UIEditorTreeViewHitTarget hitTarget = Widgets::HitTestUIEditorTreeView(m_treeFrame.layout, point); - if (hitTarget.itemIndex < GetBrowserModel().GetTreeItems().size() && + if (hitTarget.itemIndex < GetWindowTreeItems().size() && (hitTarget.kind == Widgets::UIEditorTreeViewHitTargetKind::Row || hitTarget.kind == Widgets::UIEditorTreeViewHitTargetKind::Disclosure)) { if (surface != nullptr) { *surface = DropTargetSurface::Tree; } - return GetBrowserModel().GetTreeItems()[hitTarget.itemIndex].itemId; + return GetWindowTreeItems()[hitTarget.itemIndex].itemId; } } @@ -2205,7 +2216,7 @@ void ProjectPanel::Append(UIDrawList& drawList) const { AppendUIEditorTreeViewBackground( drawList, m_treeFrame.layout, - GetBrowserModel().GetTreeItems(), + GetWindowTreeItems(), m_folderSelection, m_treeInteractionState.treeViewState, treePalette, @@ -2213,7 +2224,7 @@ void ProjectPanel::Append(UIDrawList& drawList) const { AppendUIEditorTreeViewForeground( drawList, m_treeFrame.layout, - GetBrowserModel().GetTreeItems(), + GetWindowTreeItems(), treePalette, treeMetrics); @@ -2227,7 +2238,7 @@ void ProjectPanel::Append(UIDrawList& drawList) const { } else { const std::size_t visibleIndex = FindUIEditorTreePanelVisibleItemIndex( m_treeFrame.layout, - GetBrowserModel().GetTreeItems(), + GetWindowTreeItems(), m_treeDragState.dropTargetItemId); if (visibleIndex != Widgets::UIEditorTreeViewInvalidIndex && visibleIndex < m_treeFrame.layout.rowRects.size()) { @@ -2245,7 +2256,7 @@ void ProjectPanel::Append(UIDrawList& drawList) const { m_assetDropTargetSurface == DropTargetSurface::Tree) { const std::size_t visibleIndex = FindUIEditorTreePanelVisibleItemIndex( m_treeFrame.layout, - GetBrowserModel().GetTreeItems(), + GetWindowTreeItems(), m_assetDragState.dropTargetItemId); if (visibleIndex != Widgets::UIEditorTreeViewInvalidIndex && visibleIndex < m_treeFrame.layout.rowRects.size()) { diff --git a/new_editor/app/Features/Project/ProjectPanel.h b/new_editor/app/Features/Project/ProjectPanel.h index 114bc2e8..52d8d2c5 100644 --- a/new_editor/app/Features/Project/ProjectPanel.h +++ b/new_editor/app/Features/Project/ProjectPanel.h @@ -170,6 +170,8 @@ private: bool HasProjectRuntime() const; BrowserModel& GetBrowserModel(); const BrowserModel& GetBrowserModel() const; + void RebuildWindowTreeItems(); + const std::vector& GetWindowTreeItems() const; const FolderEntry* FindFolderEntry(std::string_view itemId) const; const AssetEntry* FindAssetEntry(std::string_view itemId) const; AssetCommandTarget ResolveAssetCommandTarget( @@ -246,6 +248,7 @@ private: Ports::SystemInteractionPort* m_systemInteractionHost = nullptr; BuiltInIcons* m_icons = nullptr; const ::XCEngine::UI::Editor::UIEditorTextMeasurer* m_textMeasurer = nullptr; + std::vector m_windowTreeItems = {}; ::XCEngine::UI::Widgets::UISelectionModel m_folderSelection = {}; ::XCEngine::UI::Widgets::UIExpansionModel m_folderExpansion = {}; ::XCEngine::UI::Widgets::UISelectionModel m_assetSelection = {}; diff --git a/new_editor/app/Project/EditorProjectRuntime.cpp b/new_editor/app/Project/EditorProjectRuntime.cpp index 4d1a038b..9b324647 100644 --- a/new_editor/app/Project/EditorProjectRuntime.cpp +++ b/new_editor/app/Project/EditorProjectRuntime.cpp @@ -77,11 +77,16 @@ EditCommandTarget BuildEditCommandTarget( } // namespace -bool EditorProjectRuntime::Initialize(const std::filesystem::path& repoRoot) { - m_browserModel.Initialize(repoRoot); - m_ownedSelectionService = {}; +void EditorProjectRuntime::Reset() { + m_browserModel.Reset(); + m_ownedSelectionService.ClearSelection(); m_selectionService = &m_ownedSelectionService; m_pendingSceneOpenPath.reset(); +} + +bool EditorProjectRuntime::Initialize(const std::filesystem::path& repoRoot) { + Reset(); + m_browserModel.Initialize(repoRoot); return true; } @@ -91,10 +96,6 @@ void EditorProjectRuntime::BindSelectionService( selectionService != nullptr ? selectionService : &m_ownedSelectionService; } -void EditorProjectRuntime::SetFolderIcon(const ::XCEngine::UI::UITextureHandle& icon) { - m_browserModel.SetFolderIcon(icon); -} - void EditorProjectRuntime::Refresh() { m_browserModel.Refresh(); RevalidateSelection(); diff --git a/new_editor/app/Project/EditorProjectRuntime.h b/new_editor/app/Project/EditorProjectRuntime.h index 76b8035f..4c5d6ce2 100644 --- a/new_editor/app/Project/EditorProjectRuntime.h +++ b/new_editor/app/Project/EditorProjectRuntime.h @@ -16,6 +16,12 @@ namespace XCEngine::UI::Editor::App { class EditorProjectRuntime { public: + EditorProjectRuntime() = default; + EditorProjectRuntime(const EditorProjectRuntime&) = delete; + EditorProjectRuntime& operator=(const EditorProjectRuntime&) = delete; + EditorProjectRuntime(EditorProjectRuntime&&) = delete; + EditorProjectRuntime& operator=(EditorProjectRuntime&&) = delete; + struct EditCommandTarget { std::string itemId = {}; std::filesystem::path absolutePath = {}; @@ -38,9 +44,9 @@ public: bool showInExplorerSelectTarget = false; }; + void Reset(); bool Initialize(const std::filesystem::path& repoRoot); void BindSelectionService(EditorSelectionService* selectionService); - void SetFolderIcon(const ::XCEngine::UI::UITextureHandle& icon); void Refresh(); const ProjectBrowserModel& GetBrowserModel() const; diff --git a/new_editor/app/Scene/EditorSceneRuntime.cpp b/new_editor/app/Scene/EditorSceneRuntime.cpp index 62663223..7c9815d9 100644 --- a/new_editor/app/Scene/EditorSceneRuntime.cpp +++ b/new_editor/app/Scene/EditorSceneRuntime.cpp @@ -170,15 +170,22 @@ std::string_view GetSceneToolInteractionLockName(SceneToolInteractionLock lock) } } -bool EditorSceneRuntime::Initialize(const std::filesystem::path& projectRoot) { - m_projectRoot = projectRoot; - m_ownedSelectionService = {}; +void EditorSceneRuntime::Reset() { + m_projectRoot.clear(); + m_startupSceneResult = {}; + m_ownedSelectionService.ClearSelection(); m_selectionService = &m_ownedSelectionService; + m_sceneViewCamera = {}; + m_toolState = {}; + ResetTransformEditHistory(); m_inspectorRevision = 0u; +} + +bool EditorSceneRuntime::Initialize(const std::filesystem::path& projectRoot) { + Reset(); + m_projectRoot = projectRoot; m_startupSceneResult = EnsureEditorStartupScene(projectRoot); EnsureSceneViewCamera(); - ResetTransformEditHistory(); - m_toolState = {}; RefreshScene(); return m_startupSceneResult.ready; } diff --git a/new_editor/app/Scene/EditorSceneRuntime.h b/new_editor/app/Scene/EditorSceneRuntime.h index abecd66f..7441bc5c 100644 --- a/new_editor/app/Scene/EditorSceneRuntime.h +++ b/new_editor/app/Scene/EditorSceneRuntime.h @@ -48,6 +48,13 @@ struct EditorSceneComponentDescriptor { class EditorSceneRuntime { public: + EditorSceneRuntime() = default; + EditorSceneRuntime(const EditorSceneRuntime&) = delete; + EditorSceneRuntime& operator=(const EditorSceneRuntime&) = delete; + EditorSceneRuntime(EditorSceneRuntime&&) = delete; + EditorSceneRuntime& operator=(EditorSceneRuntime&&) = delete; + + void Reset(); bool Initialize(const std::filesystem::path& projectRoot); void BindSelectionService(EditorSelectionService* selectionService); diff --git a/new_editor/src/Collections/UIEditorTabStrip.cpp b/new_editor/src/Collections/UIEditorTabStrip.cpp index c8e4a298..eb5cbb9c 100644 --- a/new_editor/src/Collections/UIEditorTabStrip.cpp +++ b/new_editor/src/Collections/UIEditorTabStrip.cpp @@ -1,6 +1,8 @@ #include #include +#include +#include #include #include #include @@ -65,7 +67,55 @@ float ResolveEstimatedLabelWidth( return item.desiredHeaderLabelWidth; } - return static_cast(item.title.size()) * ClampNonNegative(metrics.estimatedGlyphWidth); + const float baseGlyphWidth = ClampNonNegative(metrics.estimatedGlyphWidth); + if (item.title.empty() || baseGlyphWidth <= 0.0f) { + return 0.0f; + } + + const auto estimateAsciiAdvance = [baseGlyphWidth](unsigned char ch) { + if (ch == ' ') { + return baseGlyphWidth * 0.55f; + } + if (std::isdigit(ch) != 0) { + return baseGlyphWidth * 1.08f; + } + if (std::strchr(".,:;!|'`ijlft()[]{}", static_cast(ch)) != nullptr) { + return baseGlyphWidth * 0.58f; + } + if (std::strchr("mwMW@#%&QG", static_cast(ch)) != nullptr) { + return baseGlyphWidth * 1.45f; + } + if (std::isupper(ch) != 0) { + return baseGlyphWidth * 1.18f; + } + if (std::strchr("_-+=/\\\\<>*", static_cast(ch)) != nullptr) { + return baseGlyphWidth * 0.88f; + } + return baseGlyphWidth; + }; + + float width = 0.0f; + for (std::size_t index = 0; index < item.title.size();) { + const unsigned char ch = static_cast(item.title[index]); + if (ch < 0x80u) { + width += estimateAsciiAdvance(ch); + ++index; + continue; + } + + width += baseGlyphWidth * 1.95f; + if ((ch & 0xE0u) == 0xC0u) { + index += 2u; + } else if ((ch & 0xF0u) == 0xE0u) { + index += 3u; + } else if ((ch & 0xF8u) == 0xF0u) { + index += 4u; + } else { + ++index; + } + } + + return width; } float ResolveTabTextTop( diff --git a/new_editor/src/Fields/UIEditorPropertyGridInteraction.cpp b/new_editor/src/Fields/UIEditorPropertyGridInteraction.cpp index 79dee0b3..a63fabcf 100644 --- a/new_editor/src/Fields/UIEditorPropertyGridInteraction.cpp +++ b/new_editor/src/Fields/UIEditorPropertyGridInteraction.cpp @@ -583,7 +583,7 @@ UIEditorPropertyGridInteractionFrame UpdateUIEditorPropertyGridInteraction( !eventResult.committedFieldId.empty() || !eventResult.changedFieldId.empty() || !eventResult.requestedFieldId.empty()) { - interactionResult = std::move(eventResult); + Internal::MergeInteractionResult(interactionResult, eventResult); } }