Files
XCEngine/new_editor/app/Project/EditorProjectRuntime.cpp

463 lines
14 KiB
C++

#include "Project/EditorProjectRuntime.h"
namespace XCEngine::UI::Editor::App {
namespace {
using AssetCommandTarget = EditorProjectRuntime::AssetCommandTarget;
using EditCommandTarget = EditorProjectRuntime::EditCommandTarget;
bool IsSameOrDescendantItemId(
std::string_view candidateId,
std::string_view ancestorId) {
if (candidateId == ancestorId) {
return true;
}
if (candidateId.size() <= ancestorId.size() ||
candidateId.substr(0u, ancestorId.size()) != ancestorId) {
return false;
}
return candidateId[ancestorId.size()] == '/';
}
void PopulateAssetCommandTargetFromAsset(
AssetCommandTarget& target,
const ProjectBrowserModel& browserModel,
const ProjectBrowserModel::AssetEntry& asset,
const ProjectBrowserModel::FolderEntry* containerFolder) {
target.subjectAsset = &asset;
target.subjectItemId = asset.itemId;
target.subjectPath = asset.absolutePath;
target.subjectDisplayName = asset.displayName;
target.subjectRelativePath =
browserModel.BuildProjectRelativePath(asset.itemId);
target.showInExplorerSelectTarget = true;
target.containerFolder = containerFolder;
}
void PopulateAssetCommandTargetFromFolder(
AssetCommandTarget& target,
const ProjectBrowserModel& browserModel,
const ProjectBrowserModel::FolderEntry& folder) {
target.subjectFolder = &folder;
target.subjectItemId = folder.itemId;
target.subjectPath = folder.absolutePath;
target.subjectDisplayName = folder.label;
target.subjectRelativePath =
browserModel.BuildProjectRelativePath(folder.itemId);
target.showInExplorerSelectTarget = false;
target.containerFolder = &folder;
}
EditCommandTarget BuildEditCommandTarget(
const ProjectBrowserModel::AssetEntry& asset) {
EditCommandTarget target = {};
target.itemId = asset.itemId;
target.absolutePath = asset.absolutePath;
target.displayName = asset.displayName;
target.itemKind = asset.kind;
target.directory = asset.directory;
return target;
}
EditCommandTarget BuildEditCommandTarget(
const ProjectBrowserModel& browserModel,
const ProjectBrowserModel::FolderEntry& folder) {
EditCommandTarget target = {};
target.itemId = folder.itemId;
target.absolutePath = folder.absolutePath;
target.displayName = folder.label;
target.itemKind = ProjectBrowserModel::ItemKind::Folder;
target.directory = true;
target.assetsRoot = browserModel.IsAssetsRoot(folder.itemId);
return target;
}
} // namespace
bool EditorProjectRuntime::Initialize(const std::filesystem::path& repoRoot) {
m_browserModel.Initialize(repoRoot);
m_ownedSelectionService = {};
m_selectionService = &m_ownedSelectionService;
m_pendingSceneOpenPath.reset();
return true;
}
void EditorProjectRuntime::BindSelectionService(
EditorSelectionService* selectionService) {
m_selectionService =
selectionService != nullptr ? selectionService : &m_ownedSelectionService;
}
void EditorProjectRuntime::SetFolderIcon(const ::XCEngine::UI::UITextureHandle& icon) {
m_browserModel.SetFolderIcon(icon);
}
void EditorProjectRuntime::Refresh() {
m_browserModel.Refresh();
RevalidateSelection();
}
const ProjectBrowserModel& EditorProjectRuntime::GetBrowserModel() const {
return m_browserModel;
}
ProjectBrowserModel& EditorProjectRuntime::GetBrowserModel() {
return m_browserModel;
}
bool EditorProjectRuntime::HasSelection() const {
return SelectionService().HasSelectionKind(EditorSelectionKind::ProjectItem);
}
const EditorSelectionState& EditorProjectRuntime::GetSelection() const {
return SelectionService().GetSelection();
}
std::uint64_t EditorProjectRuntime::GetSelectionStamp() const {
return SelectionService().GetStamp();
}
bool EditorProjectRuntime::SetSelection(std::string_view itemId) {
const ProjectBrowserModel::AssetEntry* asset = FindAssetEntry(itemId);
if (asset == nullptr) {
return false;
}
const EditorSelectionState& selection = SelectionService().GetSelection();
const bool changed =
selection.kind != EditorSelectionKind::ProjectItem ||
selection.itemId != asset->itemId ||
selection.absolutePath != asset->absolutePath ||
selection.directory != asset->directory;
ApplySelection(*asset);
return changed;
}
void EditorProjectRuntime::ClearSelection() {
SelectionService().ClearSelection();
}
bool EditorProjectRuntime::NavigateToFolder(std::string_view itemId) {
if (!m_browserModel.NavigateToFolder(itemId)) {
return false;
}
ClearSelection();
return true;
}
bool EditorProjectRuntime::OpenItem(std::string_view itemId) {
const ProjectBrowserModel::AssetEntry* asset = FindAssetEntry(itemId);
if (asset == nullptr) {
return false;
}
if (asset->directory) {
return NavigateToFolder(asset->itemId);
}
if (!asset->canOpen) {
return false;
}
if (asset->kind == ProjectBrowserModel::ItemKind::Scene &&
!asset->absolutePath.empty()) {
m_pendingSceneOpenPath = asset->absolutePath;
return true;
}
return false;
}
std::optional<std::filesystem::path> EditorProjectRuntime::ConsumePendingSceneOpenPath() {
if (!m_pendingSceneOpenPath.has_value()) {
return std::nullopt;
}
std::optional<std::filesystem::path> result = m_pendingSceneOpenPath;
m_pendingSceneOpenPath.reset();
return result;
}
const ProjectBrowserModel::FolderEntry* EditorProjectRuntime::FindFolderEntry(
std::string_view itemId) const {
return m_browserModel.FindFolderEntry(itemId);
}
const ProjectBrowserModel::AssetEntry* EditorProjectRuntime::FindAssetEntry(
std::string_view itemId) const {
return m_browserModel.FindAssetEntry(itemId);
}
EditorProjectRuntime::AssetCommandTarget
EditorProjectRuntime::ResolveAssetCommandTarget(
std::string_view explicitItemId,
bool forceCurrentFolder) const {
AssetCommandTarget target = {};
target.currentFolder = FindFolderEntry(m_browserModel.GetCurrentFolderId());
const ProjectBrowserModel::AssetEntry* explicitAsset =
explicitItemId.empty() ? nullptr : FindAssetEntry(explicitItemId);
const ProjectBrowserModel::FolderEntry* explicitFolder =
explicitItemId.empty() ? nullptr : FindFolderEntry(explicitItemId);
if (!forceCurrentFolder && explicitAsset != nullptr) {
PopulateAssetCommandTargetFromAsset(
target,
m_browserModel,
*explicitAsset,
explicitAsset->directory
? FindFolderEntry(explicitAsset->itemId)
: target.currentFolder);
return target;
}
if (!forceCurrentFolder && explicitFolder != nullptr) {
PopulateAssetCommandTargetFromFolder(
target,
m_browserModel,
*explicitFolder);
return target;
}
if (forceCurrentFolder) {
if (target.currentFolder != nullptr) {
PopulateAssetCommandTargetFromFolder(
target,
m_browserModel,
*target.currentFolder);
}
return target;
}
if (HasSelection()) {
const EditorSelectionState& selection = GetSelection();
if (const ProjectBrowserModel::AssetEntry* selectedAsset =
FindAssetEntry(selection.itemId);
selectedAsset != nullptr) {
PopulateAssetCommandTargetFromAsset(
target,
m_browserModel,
*selectedAsset,
selectedAsset->directory
? FindFolderEntry(selectedAsset->itemId)
: target.currentFolder);
return target;
}
}
if (target.currentFolder != nullptr) {
PopulateAssetCommandTargetFromFolder(
target,
m_browserModel,
*target.currentFolder);
}
return target;
}
std::optional<EditorProjectRuntime::EditCommandTarget>
EditorProjectRuntime::ResolveEditCommandTarget(
std::string_view explicitItemId,
bool forceCurrentFolder) const {
if (forceCurrentFolder) {
return std::nullopt;
}
if (!explicitItemId.empty()) {
if (const ProjectBrowserModel::AssetEntry* asset =
FindAssetEntry(explicitItemId);
asset != nullptr) {
return BuildEditCommandTarget(*asset);
}
if (const ProjectBrowserModel::FolderEntry* folder =
FindFolderEntry(explicitItemId);
folder != nullptr) {
return BuildEditCommandTarget(m_browserModel, *folder);
}
return std::nullopt;
}
if (HasSelection()) {
const EditorSelectionState& selection = GetSelection();
if (const ProjectBrowserModel::AssetEntry* selectedAsset =
FindAssetEntry(selection.itemId);
selectedAsset != nullptr) {
return BuildEditCommandTarget(*selectedAsset);
}
}
if (const ProjectBrowserModel::FolderEntry* currentFolder =
FindFolderEntry(m_browserModel.GetCurrentFolderId());
currentFolder != nullptr) {
return BuildEditCommandTarget(m_browserModel, *currentFolder);
}
return std::nullopt;
}
bool EditorProjectRuntime::CreateFolder(
std::string_view parentFolderId,
std::string_view requestedName,
std::string* createdFolderId) {
const bool created =
m_browserModel.CreateFolder(parentFolderId, requestedName, createdFolderId);
RevalidateSelection();
return created;
}
bool EditorProjectRuntime::CreateMaterial(
std::string_view parentFolderId,
std::string_view requestedName,
std::string* createdItemId) {
const bool created =
m_browserModel.CreateMaterial(parentFolderId, requestedName, createdItemId);
RevalidateSelection();
return created;
}
bool EditorProjectRuntime::RenameItem(
std::string_view itemId,
std::string_view newName,
std::string* renamedItemId) {
std::string resolvedRenamedItemId = {};
std::string* renameOutput =
renamedItemId != nullptr ? renamedItemId : &resolvedRenamedItemId;
if (!m_browserModel.RenameItem(itemId, newName, renameOutput)) {
return false;
}
if (SelectionTargetsItem(itemId)) {
if (!renameOutput->empty() && !SetSelection(*renameOutput)) {
ClearSelection();
}
} else {
RevalidateSelection();
}
return true;
}
bool EditorProjectRuntime::DeleteItem(std::string_view itemId) {
const bool selectionAffected = SelectionTargetsItem(itemId);
if (!m_browserModel.DeleteItem(itemId)) {
return false;
}
if (selectionAffected) {
ClearSelection();
} else {
RevalidateSelection();
}
return true;
}
bool EditorProjectRuntime::CanMoveItemToFolder(
std::string_view itemId,
std::string_view targetFolderId) const {
return m_browserModel.CanMoveItemToFolder(itemId, targetFolderId);
}
bool EditorProjectRuntime::MoveItemToFolder(
std::string_view itemId,
std::string_view targetFolderId,
std::string* movedItemId) {
const bool selectionAffected = SelectionTargetsItem(itemId);
if (!m_browserModel.MoveItemToFolder(itemId, targetFolderId, movedItemId)) {
return false;
}
if (selectionAffected) {
ClearSelection();
} else {
RevalidateSelection();
}
return true;
}
std::optional<std::string> EditorProjectRuntime::GetParentFolderId(
std::string_view itemId) const {
return m_browserModel.GetParentFolderId(itemId);
}
bool EditorProjectRuntime::CanReparentFolder(
std::string_view sourceFolderId,
std::string_view targetParentId) const {
return m_browserModel.CanReparentFolder(sourceFolderId, targetParentId);
}
bool EditorProjectRuntime::ReparentFolder(
std::string_view sourceFolderId,
std::string_view targetParentId,
std::string* movedFolderId) {
const bool selectionAffected = SelectionTargetsItem(sourceFolderId);
if (!m_browserModel.ReparentFolder(sourceFolderId, targetParentId, movedFolderId)) {
return false;
}
if (selectionAffected) {
ClearSelection();
} else {
RevalidateSelection();
}
return true;
}
bool EditorProjectRuntime::MoveFolderToRoot(
std::string_view sourceFolderId,
std::string* movedFolderId) {
const bool selectionAffected = SelectionTargetsItem(sourceFolderId);
if (!m_browserModel.MoveFolderToRoot(sourceFolderId, movedFolderId)) {
return false;
}
if (selectionAffected) {
ClearSelection();
} else {
RevalidateSelection();
}
return true;
}
EditorSelectionService& EditorProjectRuntime::SelectionService() {
return *m_selectionService;
}
const EditorSelectionService& EditorProjectRuntime::SelectionService() const {
return *m_selectionService;
}
void EditorProjectRuntime::ApplySelection(
const ProjectBrowserModel::AssetEntry& asset) {
SelectionService().SetProjectSelection(
asset.itemId,
asset.displayName.empty() ? asset.nameWithExtension : asset.displayName,
asset.absolutePath,
asset.directory);
}
void EditorProjectRuntime::RevalidateSelection() {
if (!HasSelection()) {
return;
}
const ProjectBrowserModel::AssetEntry* asset =
FindAssetEntry(GetSelection().itemId);
if (asset == nullptr) {
ClearSelection();
return;
}
ApplySelection(*asset);
}
bool EditorProjectRuntime::SelectionTargetsItem(std::string_view itemId) const {
return HasSelection() &&
IsSameOrDescendantItemId(GetSelection().itemId, itemId);
}
} // namespace XCEngine::UI::Editor::App