Files
XCEngine/new_editor/app/Features/Project/ProjectPanel.h

282 lines
11 KiB
C++

#pragma once
#include "Project/EditorProjectRuntime.h"
#include "ProjectBrowserModel.h"
#include "Features/PanelInputContext.h"
#include "Commands/EditorEditCommandRoute.h"
#include <XCEditor/Collections/UIEditorGridDragDrop.h>
#include <XCEditor/Collections/UIEditorInlineRenameSession.h>
#include <XCEditor/Collections/UIEditorTreeDragDrop.h>
#include <XCEditor/Collections/UIEditorTreeViewInteraction.h>
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEditor/Menu/UIEditorMenuPopup.h>
#include <XCEditor/Panels/UIEditorPanelContentHost.h>
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
#include <chrono>
#include <cstdint>
#include <filesystem>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor::App {
class BuiltInIcons;
class EditorCommandFocusService;
} // namespace XCEngine::UI::Editor::App
namespace XCEngine::UI::Editor::Ports {
class SystemInteractionPort;
} // namespace XCEngine::UI::Editor::Ports
namespace XCEngine::UI::Editor::App {
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 SetSystemInteractionHost(Ports::SystemInteractionPort* systemInteractionHost);
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,
const PanelInputContext& inputContext);
void Append(::XCEngine::UI::UIDrawList& drawList) const;
CursorKind GetCursorKind() const;
bool WantsHostPointerCapture() const;
bool WantsHostPointerRelease() const;
bool HasActivePointerCapture() const;
const std::vector<Event>& 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<Widgets::UIEditorMenuPopupItem> 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<std::size_t>(-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<BreadcrumbItemLayout> breadcrumbItems = {};
std::vector<AssetTileLayout> 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<EditCommandTarget> 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,
const PanelInputContext& inputContext) 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<EditorProjectRuntime> m_ownedProjectRuntime = {};
EditorProjectRuntime* m_projectRuntime = nullptr;
EditorCommandFocusService* m_commandFocusService = nullptr;
Ports::SystemInteractionPort* m_systemInteractionHost = 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<Event> 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::chrono::steady_clock::time_point m_lastPrimaryClickTime = {};
std::size_t m_hoveredBreadcrumbIndex = static_cast<std::size_t>(-1);
std::size_t m_pressedBreadcrumbIndex = static_cast<std::size_t>(-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