Added: - New `ProgressService` class for managing progress indicators. - New `AssetDatabase`, `AssetOpenHandlerAttribute`, and `AsyncAssetOpenHandlerAttribute` classes for asset handling. - `Ghost.UnitTest` project for unit testing with associated files and configurations. Changed: - `ActivationHandler` class to ensure correct handling of `LaunchActivatedEventArgs`. - `App.xaml.cs` to register `INotificationService` and `IProgressService`, replacing `StackedNotificationService`. - `OnLaunched` method in `App.xaml.cs` to correctly call `ActivationHandler.Handle(args)` and start the host. - `INavigationAware` interface from internal to public for broader access. - `EditorState.cs` to activate `EditorApplication` with the current service provider. - Property names in `AssetItem` and `ExplorerItem` structs to `Name` and `FullName`. - `NotificationService` class to implement `INotificationService` and refactor notification handling. - `AssetPathToGlyphConverter` to handle file extensions consistently. - Bindings in `ProjectPage.xaml` and `ProjectPage.xaml.cs` to use `FullName` instead of `Path`. - `EngineEditorWindow` and `LandingWindow` classes to utilize new notification and progress services. - `Logger` class to include a new method for logging errors with exceptions. Updated: - Manifest files and project files to reflect new structure and dependencies. - Solution file `GhostEngine.sln` to include the new unit test project. - Added several new test classes and methods in `UnitTests.cs`.
151 lines
3.9 KiB
C#
151 lines
3.9 KiB
C#
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using Ghost.App;
|
|
using Ghost.App.Models;
|
|
using Ghost.Data.Services;
|
|
using Ghost.Editor.AssetHandle;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
|
|
|
internal partial class ProjectViewModel : ObservableObject
|
|
{
|
|
public ObservableCollection<ExplorerItem> SubDirectories
|
|
{
|
|
get;
|
|
} = new();
|
|
|
|
[ObservableProperty]
|
|
public partial ObservableCollection<ExplorerItem> DirectoryAssets
|
|
{
|
|
get;
|
|
set;
|
|
} = new();
|
|
|
|
[ObservableProperty]
|
|
public partial ExplorerItem? SelectedDirectory
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
[ObservableProperty]
|
|
public partial ExplorerItem? SelectedAsset
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public ProjectViewModel()
|
|
{
|
|
if (ProjectService.CurrentProject.Metadata == null)
|
|
{
|
|
throw new InvalidOperationException("Current project is not set.");
|
|
}
|
|
|
|
var assetsRootItem = new ExplorerItem("Assets", Path.Combine(Path.GetDirectoryName(ProjectService.CurrentProject.Path)!, ProjectService.ASSETS_FOLDER), true);
|
|
LoadSubFolderRecursive(ref assetsRootItem);
|
|
|
|
SubDirectories.Add(assetsRootItem);
|
|
}
|
|
|
|
private static void LoadSubFolderRecursive(ref ExplorerItem parentItem)
|
|
{
|
|
foreach (var directory in Directory.EnumerateDirectories(parentItem.FullName))
|
|
{
|
|
var item = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
|
LoadSubFolderRecursive(ref item);
|
|
|
|
parentItem.Children ??= new();
|
|
parentItem.Children.Add(item);
|
|
}
|
|
}
|
|
|
|
public static Task<ExplorerItem?> FindNodeIterative(ExplorerItem root, Func<ExplorerItem, bool> predicate)
|
|
{
|
|
var stack = new Stack<ExplorerItem>();
|
|
stack.Push(root);
|
|
|
|
return Task.Run(() =>
|
|
{
|
|
while (stack.Count > 0)
|
|
{
|
|
var node = stack.Pop();
|
|
if (predicate(node))
|
|
{
|
|
return node;
|
|
}
|
|
|
|
if (node.Children == null || node.Children.Count == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (var i = node.Children.Count - 1; i >= 0; i--)
|
|
{
|
|
stack.Push(node.Children[i]);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
});
|
|
}
|
|
|
|
private void NavigateToDirectory(string? path)
|
|
{
|
|
GhostApplication.Window?.DispatcherQueue.TryEnqueue(async () =>
|
|
{
|
|
DirectoryAssets.Clear();
|
|
|
|
if (!Directory.Exists(path))
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (var directory in Directory.EnumerateDirectories(path))
|
|
{
|
|
var directoryItem = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
|
DirectoryAssets.Add(directoryItem);
|
|
}
|
|
|
|
foreach (var file in Directory.EnumerateFiles(path))
|
|
{
|
|
var fileItem = new ExplorerItem(Path.GetFileName(file), file, false);
|
|
DirectoryAssets.Add(fileItem);
|
|
}
|
|
|
|
SelectedDirectory = await FindNodeIterative(SubDirectories[0], x => x.FullName == path);
|
|
});
|
|
}
|
|
|
|
public async Task OpenSelected()
|
|
{
|
|
if (SelectedAsset == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (SelectedAsset.IsDirectory)
|
|
{
|
|
NavigateToDirectory(SelectedAsset.FullName);
|
|
}
|
|
else
|
|
{
|
|
await AssetDatabase.OpenAsset(SelectedAsset.FullName);
|
|
}
|
|
}
|
|
|
|
partial void OnSelectedDirectoryChanged(ExplorerItem? value)
|
|
{
|
|
if (value == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DirectoryAssets.Clear();
|
|
NavigateToDirectory(value.FullName);
|
|
}
|
|
} |