#pragma once #include "Project/EditorProjectRuntime.h" #include "ProjectBrowserModel.h" #include "Commands/EditorEditCommandRoute.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace XCEngine::UI::Editor::App { class BuiltInIcons; class EditorCommandFocusService; class ProjectPanel final : public EditorEditCommandRoute { public: enum class CursorKind : std::uint8_t { Arrow = 0, ResizeEW }; enum class EventKind : std::uint8_t { None = 0, AssetSelected, AssetSelectionCleared, FolderNavigated, AssetOpened, RenameRequested, ContextMenuRequested }; enum class EventSource : std::uint8_t { None = 0, Tree, Breadcrumb, Command, GridPrimary, GridDoubleClick, GridSecondary, GridDrag, Background }; struct Event { EventKind kind = EventKind::None; EventSource source = EventSource::None; std::string itemId = {}; std::filesystem::path absolutePath = {}; std::string displayName = {}; ProjectBrowserModel::ItemKind itemKind = ProjectBrowserModel::ItemKind::File; bool directory = false; }; void Initialize(const std::filesystem::path& repoRoot); void SetProjectRuntime(EditorProjectRuntime* projectRuntime); void SetCommandFocusService(EditorCommandFocusService* commandFocusService); void SetBuiltInIcons(const BuiltInIcons* icons); void SetTextMeasurer(const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer); void ResetInteractionState(); void Update( const UIEditorPanelContentHostFrame& contentHostFrame, const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, bool allowInteraction, bool panelActive); void Append(::XCEngine::UI::UIDrawList& drawList) const; CursorKind GetCursorKind() const; bool WantsHostPointerCapture() const; bool WantsHostPointerRelease() const; bool HasActivePointerCapture() const; const std::vector& GetFrameEvents() const; UIEditorHostCommandEvaluationResult EvaluateAssetCommand( std::string_view commandId) const override; UIEditorHostCommandDispatchResult DispatchAssetCommand( std::string_view commandId) override; UIEditorHostCommandEvaluationResult EvaluateEditCommand( std::string_view commandId) const override; UIEditorHostCommandDispatchResult DispatchEditCommand( std::string_view commandId) override; private: using BrowserModel = ::XCEngine::UI::Editor::App::ProjectBrowserModel; using FolderEntry = BrowserModel::FolderEntry; using AssetEntry = BrowserModel::AssetEntry; using EditCommandTarget = EditorProjectRuntime::EditCommandTarget; using AssetCommandTarget = EditorProjectRuntime::AssetCommandTarget; enum class RenameSurface : std::uint8_t { None = 0, Tree, Grid }; enum class DropTargetSurface : std::uint8_t { None = 0, Tree, Grid }; struct ContextMenuState { bool open = false; bool forceCurrentFolder = false; ::XCEngine::UI::UIPoint anchorPosition = {}; std::string targetItemId = {}; std::vector items = {}; Widgets::UIEditorMenuPopupLayout layout = {}; Widgets::UIEditorMenuPopupState widgetState = {}; }; struct BreadcrumbItemLayout { std::string label = {}; std::string targetFolderId = {}; ::XCEngine::UI::UIRect rect = {}; bool separator = false; bool clickable = false; bool current = false; }; struct AssetTileLayout { std::size_t itemIndex = static_cast(-1); ::XCEngine::UI::UIRect tileRect = {}; ::XCEngine::UI::UIRect previewRect = {}; ::XCEngine::UI::UIRect labelRect = {}; }; struct Layout { ::XCEngine::UI::UIRect bounds = {}; ::XCEngine::UI::UIRect leftPaneRect = {}; ::XCEngine::UI::UIRect treeRect = {}; ::XCEngine::UI::UIRect dividerRect = {}; ::XCEngine::UI::UIRect rightPaneRect = {}; ::XCEngine::UI::UIRect browserHeaderRect = {}; ::XCEngine::UI::UIRect browserBodyRect = {}; ::XCEngine::UI::UIRect gridRect = {}; std::vector breadcrumbItems = {}; std::vector assetTiles = {}; }; EditorProjectRuntime* ResolveProjectRuntime(); const EditorProjectRuntime* ResolveProjectRuntime() const; bool HasProjectRuntime() const; BrowserModel& GetBrowserModel(); const BrowserModel& GetBrowserModel() const; const FolderEntry* FindFolderEntry(std::string_view itemId) const; const AssetEntry* FindAssetEntry(std::string_view itemId) const; AssetCommandTarget ResolveAssetCommandTarget( std::string_view explicitItemId = {}, bool forceCurrentFolder = false) const; std::optional ResolveEditCommandTarget( std::string_view explicitItemId = {}, bool forceCurrentFolder = false) const; const UIEditorPanelContentHostPanelState* FindMountedProjectPanel( const UIEditorPanelContentHostFrame& contentHostFrame) const; Layout BuildLayout(const ::XCEngine::UI::UIRect& bounds) const; std::size_t HitTestBreadcrumbItem(const ::XCEngine::UI::UIPoint& point) const; std::size_t HitTestAssetTile(const ::XCEngine::UI::UIPoint& point) const; std::string ResolveAssetDropTargetItemId( const ::XCEngine::UI::UIPoint& point, DropTargetSurface* surface = nullptr) const; void SyncCurrentFolderSelection(); bool NavigateToFolder(std::string_view itemId, EventSource source = EventSource::None); void EmitEvent(EventKind kind, EventSource source, const FolderEntry* folder); void EmitEvent(EventKind kind, EventSource source, const AssetEntry* asset); void EmitSelectionClearedEvent(EventSource source); bool OpenProjectItem(std::string_view itemId, EventSource source); void OpenContextMenu( const ::XCEngine::UI::UIPoint& anchorPosition, std::string_view targetItemId, bool forceCurrentFolder); void CloseContextMenu(); void RebuildContextMenu(); bool HandleContextMenuEvent(const ::XCEngine::UI::UIInputEvent& event); bool DispatchContextMenuItem(std::string_view itemId); void AppendContextMenu(::XCEngine::UI::UIDrawList& drawList) const; void ClearRenameState(); void SyncAssetSelectionFromRuntime(); void QueueRenameSession( std::string_view itemId, RenameSurface surface); bool TryStartQueuedRenameSession(); void UpdateRenameSession( const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents); ::XCEngine::UI::UIRect BuildRenameBounds( std::string_view itemId, RenameSurface surface) const; const AssetEntry* GetSelectedAssetEntry() const; const FolderEntry* GetSelectedFolderEntry() const; void ResetTransientFrames(); std::vector<::XCEngine::UI::UIInputEvent> BuildTreeInteractionInputEvents( const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, const ::XCEngine::UI::UIRect& bounds, bool allowInteraction, bool panelActive) 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, bool forceCurrentFolder) const; UIEditorHostCommandDispatchResult DispatchAssetCommand( std::string_view commandId, std::string_view explicitItemId, bool forceCurrentFolder); UIEditorHostCommandEvaluationResult EvaluateEditCommand( std::string_view commandId, std::string_view explicitItemId, bool forceCurrentFolder) const; UIEditorHostCommandDispatchResult DispatchEditCommand( std::string_view commandId, std::string_view explicitItemId, bool forceCurrentFolder); std::unique_ptr m_ownedProjectRuntime = {}; EditorProjectRuntime* m_projectRuntime = nullptr; EditorCommandFocusService* m_commandFocusService = nullptr; const BuiltInIcons* m_icons = nullptr; const ::XCEngine::UI::Editor::UIEditorTextMeasurer* m_textMeasurer = nullptr; ::XCEngine::UI::Widgets::UISelectionModel m_folderSelection = {}; ::XCEngine::UI::Widgets::UIExpansionModel m_folderExpansion = {}; ::XCEngine::UI::Widgets::UISelectionModel m_assetSelection = {}; Collections::GridDragDrop::State m_assetDragState = {}; Collections::TreeDragDrop::State m_treeDragState = {}; UIEditorTreeViewInteractionState m_treeInteractionState = {}; UIEditorTreeViewInteractionFrame m_treeFrame = {}; UIEditorInlineRenameSessionState m_renameState = {}; UIEditorInlineRenameSessionFrame m_renameFrame = {}; std::vector m_frameEvents = {}; Layout m_layout = {}; ContextMenuState m_contextMenu = {}; std::string m_pendingRenameItemId = {}; RenameSurface m_pendingRenameSurface = RenameSurface::None; RenameSurface m_activeRenameSurface = RenameSurface::None; DropTargetSurface m_assetDropTargetSurface = DropTargetSurface::None; std::string m_hoveredAssetItemId = {}; std::string m_lastPrimaryClickedAssetId = {}; float m_navigationWidth = 248.0f; std::uint64_t m_lastPrimaryClickTimeMs = 0u; std::size_t m_hoveredBreadcrumbIndex = static_cast(-1); std::size_t m_pressedBreadcrumbIndex = static_cast(-1); bool m_visible = false; bool m_splitterHovered = false; bool m_splitterDragging = false; bool m_requestPointerCapture = false; bool m_requestPointerRelease = false; }; } // namespace XCEngine::UI::Editor::App