Update editor
@@ -1,6 +0,0 @@
|
|||||||
namespace Ghost.Data.Repository;
|
|
||||||
|
|
||||||
internal class AssetsRepository
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
using Ghost.Core;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.AppState;
|
|
||||||
|
|
||||||
internal partial class AppStateMachine : IDisposable, IAsyncDisposable
|
|
||||||
{
|
|
||||||
private Dictionary<StateKey, Lazy<IAppState>> _states = new();
|
|
||||||
private IAppState? _current;
|
|
||||||
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
public void RegisterState(StateKey key, Func<IAppState> stateFactory)
|
|
||||||
{
|
|
||||||
_states[key] = new(stateFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Result> TransitionToAsync(StateKey stateKey, object? parameter = null)
|
|
||||||
{
|
|
||||||
var previous = _current;
|
|
||||||
if (!_states.TryGetValue(stateKey, out var next))
|
|
||||||
{
|
|
||||||
return Result.Failure($"State '{stateKey}' not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Result result;
|
|
||||||
if (previous != null)
|
|
||||||
{
|
|
||||||
result = await previous.OnExitingAsync();
|
|
||||||
if (result.IsFailure)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result = await next.Value.OnEnteringAsync(parameter);
|
|
||||||
if (result.IsFailure)
|
|
||||||
{
|
|
||||||
if (previous != null)
|
|
||||||
{
|
|
||||||
await previous.OnEnteredAsync(parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (previous != null)
|
|
||||||
{
|
|
||||||
result = await previous.OnExitedAsync();
|
|
||||||
if (result.IsFailure)
|
|
||||||
{
|
|
||||||
await next.Value.OnExitedAsync();
|
|
||||||
await previous.OnEnteredAsync(parameter);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result = await next.Value.OnEnteredAsync(parameter);
|
|
||||||
if (result.IsFailure)
|
|
||||||
{
|
|
||||||
await next.Value.OnExitedAsync();
|
|
||||||
|
|
||||||
if (previous != null)
|
|
||||||
{
|
|
||||||
await previous.OnEnteredAsync(parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
_current = next.Value;
|
|
||||||
|
|
||||||
return Result.Success();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
DisposeAsync().AsTask().Wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_states.Clear();
|
|
||||||
if (_current != null)
|
|
||||||
{
|
|
||||||
await _current.OnExitingAsync();
|
|
||||||
await _current.OnExitedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
_current = null;
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using Ghost.Core;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.AppState;
|
|
||||||
|
|
||||||
internal interface IAppState
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Called when exiting the state.
|
|
||||||
/// </summary>
|
|
||||||
public ValueTask<Result> OnExitingAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when entering the state, right after OnEnteringAsync.
|
|
||||||
/// <paramref name="parameter">can be used to pass data into the state, such as a project to load.</summary>
|
|
||||||
/// </summary>
|
|
||||||
public ValueTask<Result> OnEnteringAsync(object? parameter);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when exiting the state, specifically for pose transitions.
|
|
||||||
/// </summary>
|
|
||||||
public ValueTask<Result> OnExitedAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when entered the state, specifically after the state has been fully initialized and is ready for interaction.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parameter">can be used to pass data into the state, such as a project to load.</param>
|
|
||||||
public ValueTask<Result> OnEnteredAsync(object? parameter);
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace Ghost.Editor.Core.AppState;
|
|
||||||
|
|
||||||
internal enum StateKey
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Landing,
|
|
||||||
EngineEditor,
|
|
||||||
}
|
|
||||||
@@ -208,7 +208,7 @@ public static partial class AssetDatabase
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Move an asset to a new location by path.
|
/// Move an asset to a new location by path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="oldPath">Current path of the asset.</param>
|
/// <param name="oldPath">CurrentApplication path of the asset.</param>
|
||||||
/// <param name="newPath">New path for the asset (relative or absolute).</param>
|
/// <param name="newPath">New path for the asset (relative or absolute).</param>
|
||||||
/// <returns>Result indicating success or failure.</returns>
|
/// <returns>Result indicating success or failure.</returns>
|
||||||
public static ValueTask<Result> MoveAssetAsync(string oldPath, string newPath, CancellationToken token = default)
|
public static ValueTask<Result> MoveAssetAsync(string oldPath, string newPath, CancellationToken token = default)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Data.Services;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
@@ -26,7 +25,7 @@ public static partial class AssetDatabase
|
|||||||
return Result<string>.Failure("AssetsDirectory not initialized");
|
return Result<string>.Failure("AssetsDirectory not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
var cacheDir = Path.Combine(AssetsDirectory.Parent!.FullName, ProjectService.CACHE_FOLDER, "ImportedAssets");
|
var cacheDir = Path.Combine(AssetsDirectory.Parent!.FullName, EditorApplication.CACHES_FOLDER_NAME, "ImportedAssets");
|
||||||
if (!Directory.Exists(cacheDir))
|
if (!Directory.Exists(cacheDir))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(cacheDir);
|
Directory.CreateDirectory(cacheDir);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Data.Services;
|
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ public static partial class AssetDatabase
|
|||||||
throw new InvalidOperationException("AssetsDirectory is not set. Initialize() must be called first.");
|
throw new InvalidOperationException("AssetsDirectory is not set. Initialize() must be called first.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbPath = Path.Combine(AssetsDirectory.Parent!.FullName, ProjectService.CACHE_FOLDER, "AssetDatabase.db");
|
var dbPath = Path.Combine(AssetsDirectory.Parent!.FullName, EditorApplication.CACHES_FOLDER_NAME, "AssetDatabase.db");
|
||||||
var cacheDir = Path.GetDirectoryName(dbPath);
|
var cacheDir = Path.GetDirectoryName(dbPath);
|
||||||
if (!Directory.Exists(cacheDir))
|
if (!Directory.Exists(cacheDir))
|
||||||
{
|
{
|
||||||
@@ -301,7 +300,7 @@ public static partial class AssetDatabase
|
|||||||
var sqlPattern = namePattern.Replace('*', '%').Replace('?', '_');
|
var sqlPattern = namePattern.Replace('*', '%').Replace('?', '_');
|
||||||
|
|
||||||
await using var cmd = s_dbConnection.CreateCommand();
|
await using var cmd = s_dbConnection.CreateCommand();
|
||||||
|
|
||||||
// Extract just the filename from the path for matching
|
// Extract just the filename from the path for matching
|
||||||
// SQLite doesn't have a built-in path manipulation, so we search in the full path
|
// SQLite doesn't have a built-in path manipulation, so we search in the full path
|
||||||
// and filter by checking if the pattern matches the filename part
|
// and filter by checking if the pattern matches the filename part
|
||||||
@@ -319,12 +318,12 @@ public static partial class AssetDatabase
|
|||||||
|
|
||||||
// Extract filename and check if it matches the pattern
|
// Extract filename and check if it matches the pattern
|
||||||
var fileName = Path.GetFileName(path);
|
var fileName = Path.GetFileName(path);
|
||||||
|
|
||||||
// Convert pattern to regex for proper matching
|
// Convert pattern to regex for proper matching
|
||||||
var regexPattern = "^" + System.Text.RegularExpressions.Regex.Escape(namePattern)
|
var regexPattern = "^" + System.Text.RegularExpressions.Regex.Escape(namePattern)
|
||||||
.Replace("\\*", ".*")
|
.Replace("\\*", ".*")
|
||||||
.Replace("\\?", ".") + "$";
|
.Replace("\\?", ".") + "$";
|
||||||
|
|
||||||
if (System.Text.RegularExpressions.Regex.IsMatch(fileName, regexPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase))
|
if (System.Text.RegularExpressions.Regex.IsMatch(fileName, regexPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase))
|
||||||
{
|
{
|
||||||
if (Guid.TryParse(guidStr, out var guid))
|
if (Guid.TryParse(guidStr, out var guid))
|
||||||
@@ -377,10 +376,10 @@ public static partial class AssetDatabase
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove orphaned entries
|
// Remove orphaned entries
|
||||||
foreach (var guid in orphanedGuids)
|
foreach (var guid in orphanedGuids)
|
||||||
{
|
{
|
||||||
await RemoveAssetFromDatabaseAsync(guid, token);
|
await RemoveAssetFromDatabaseAsync(guid, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Data.Services;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
@@ -92,12 +91,7 @@ public static partial class AssetDatabase
|
|||||||
s_initialized = true;
|
s_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ProjectService.CurrentProject.Metadata == null)
|
AssetsDirectory = new DirectoryInfo(Path.Combine(EditorApplication.CurrentProjectPath, EditorApplication.ASSETS_FOLDER_NAME));
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Project metadata is not initialized. Ensure that the project is loaded before accessing the AssetDatabase.");
|
|
||||||
}
|
|
||||||
|
|
||||||
AssetsDirectory = new DirectoryInfo(Path.Combine(Path.GetDirectoryName(ProjectService.CurrentProject.Path)!, ProjectService.ASSETS_FOLDER));
|
|
||||||
|
|
||||||
s_commandChannel = Channel.CreateUnbounded<AssetCommand>(new UnboundedChannelOptions
|
s_commandChannel = Channel.CreateUnbounded<AssetCommand>(new UnboundedChannelOptions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.Inspector;
|
namespace Ghost.Editor.Core.Contracts;
|
||||||
|
|
||||||
public interface IInspectable
|
public interface IInspectable
|
||||||
{
|
{
|
||||||
32
Ghost.Editor.Core/Contracts/IInspectorService.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
namespace Ghost.Editor.Core.Contracts;
|
||||||
|
|
||||||
|
public class InspectorSelectionChangedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public object? Source
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IInspectable? Selected
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InspectorSelectionChangedEventArgs(object? source, IInspectable? selected)
|
||||||
|
{
|
||||||
|
Source = source;
|
||||||
|
Selected = selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IInspectorService
|
||||||
|
{
|
||||||
|
IInspectable? Selected
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
event EventHandler<InspectorSelectionChangedEventArgs> OnSelectionChanged;
|
||||||
|
|
||||||
|
void SetSelected(IInspectable? inspectable, object? source);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using CommunityToolkit.WinUI.Behaviors;
|
using CommunityToolkit.WinUI.Behaviors;
|
||||||
|
using Ghost.Editor.Core.Notifications;
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.Notifications;
|
namespace Ghost.Editor.Core.Contracts;
|
||||||
|
|
||||||
public interface INotificationService
|
public interface INotificationService
|
||||||
{
|
{
|
||||||
12
Ghost.Editor.Core/Contracts/IPreviewService.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Ghost.Editor.Core.Contracts;
|
||||||
|
|
||||||
|
public enum IconSize
|
||||||
|
{
|
||||||
|
Small,
|
||||||
|
Large
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IPreviewService
|
||||||
|
{
|
||||||
|
string GetIconPath(string path, bool isDirectory, IconSize size);
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Ghost.Editor.Core.Progress;
|
namespace Ghost.Editor.Core.Contracts;
|
||||||
|
|
||||||
public interface IProgressService
|
public interface IProgressService
|
||||||
{
|
{
|
||||||
39
Ghost.Editor.Core/EditorApplication.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
|
namespace Ghost.Editor.Core;
|
||||||
|
|
||||||
|
public static class EditorApplication
|
||||||
|
{
|
||||||
|
public const string ASSETS_FOLDER_NAME = "Assets";
|
||||||
|
public const string CACHES_FOLDER_NAME = "Caches";
|
||||||
|
|
||||||
|
private static IServiceProvider? s_serviceProvider;
|
||||||
|
private static string s_currentProjectPath = string.Empty;
|
||||||
|
private static string s_currentProjectName = string.Empty;
|
||||||
|
|
||||||
|
internal static Application CurrentApplication => Application.Current;
|
||||||
|
internal static string CurrentProjectPath => s_currentProjectPath;
|
||||||
|
internal static string CurrentProjectName => s_currentProjectName;
|
||||||
|
|
||||||
|
internal static void Initialize(IServiceProvider serviceProvider, string projectPath, string projectName)
|
||||||
|
{
|
||||||
|
s_serviceProvider = serviceProvider;
|
||||||
|
s_currentProjectPath = projectPath;
|
||||||
|
s_currentProjectName = projectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T GetService<T>()
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
if (s_serviceProvider?.GetService(typeof(T)) is not T service)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void Shutdown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
28
Ghost.Editor.Core/EditorInjectionAttribute.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
namespace Ghost.Editor.Core;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
|
||||||
|
public class EditorInjectionAttribute : Attribute
|
||||||
|
{
|
||||||
|
public enum ServiceLifetime
|
||||||
|
{
|
||||||
|
Singleton,
|
||||||
|
Transient,
|
||||||
|
Scoped
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceLifetime Lifetime
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type? ImplementationType
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EditorInjectionAttribute(ServiceLifetime lifetime, Type? implementationType = null)
|
||||||
|
{
|
||||||
|
Lifetime = lifetime;
|
||||||
|
ImplementationType = implementationType;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ghost.Data\Ghost.Data.csproj" />
|
|
||||||
<ProjectReference Include="..\Ghost.Core\Ghost.Core.csproj" />
|
<ProjectReference Include="..\Ghost.Core\Ghost.Core.csproj" />
|
||||||
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
|
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace Ghost.Editor.Core.Inspector;
|
|
||||||
|
|
||||||
internal interface IInspectorService
|
|
||||||
{
|
|
||||||
public IInspectable? SelectedInspectable
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public event Action? OnSelectionChanged;
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
namespace Ghost.Editor.Core.Inspector;
|
|
||||||
|
|
||||||
public class InspectorService : IInspectorService
|
|
||||||
{
|
|
||||||
public IInspectable? SelectedInspectable
|
|
||||||
{
|
|
||||||
get => field;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (field != value)
|
|
||||||
{
|
|
||||||
field = value;
|
|
||||||
OnSelectionChanged?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public event Action? OnSelectionChanged;
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Ghost.Editor.Core.Inspector;
|
using Ghost.Editor.Core.Contracts;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|||||||
21
Ghost.Editor.Core/Services/InspectorService.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using Ghost.Editor.Core.Contracts;
|
||||||
|
|
||||||
|
namespace Ghost.Editor.Core.Services;
|
||||||
|
|
||||||
|
public class InspectorService : IInspectorService
|
||||||
|
{
|
||||||
|
private IInspectable? _selected;
|
||||||
|
|
||||||
|
public IInspectable? Selected => _selected;
|
||||||
|
|
||||||
|
public event EventHandler<InspectorSelectionChangedEventArgs>? OnSelectionChanged;
|
||||||
|
|
||||||
|
public void SetSelected(IInspectable? inspectable, object? source)
|
||||||
|
{
|
||||||
|
if (_selected != inspectable)
|
||||||
|
{
|
||||||
|
_selected = inspectable;
|
||||||
|
OnSelectionChanged?.Invoke(this, new InspectorSelectionChangedEventArgs(source, inspectable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
using CommunityToolkit.WinUI.Behaviors;
|
using CommunityToolkit.WinUI.Behaviors;
|
||||||
|
using Ghost.Editor.Core.Contracts;
|
||||||
|
using Ghost.Editor.Core.Notifications;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.Notifications;
|
namespace Ghost.Editor.Core.Services;
|
||||||
|
|
||||||
public class NotificationService : INotificationService
|
public class NotificationService : INotificationService
|
||||||
{
|
{
|
||||||
35
Ghost.Editor.Core/Services/PreviewService.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using Ghost.Editor.Core.Contracts;
|
||||||
|
|
||||||
|
namespace Ghost.Editor.Core.Services;
|
||||||
|
|
||||||
|
internal class PreviewService : IPreviewService
|
||||||
|
{
|
||||||
|
public string GetIconPath(string path, bool isDirectory, IconSize size)
|
||||||
|
{
|
||||||
|
string iconPath;
|
||||||
|
if (isDirectory)
|
||||||
|
{
|
||||||
|
iconPath = "ms-appx:///Assets/EditorIcons/folder-{0}.png";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: Generate preview icons dynamically for known file types like images, meshes, materials, etc.
|
||||||
|
var ext = Path.GetExtension(path);
|
||||||
|
iconPath = ext switch
|
||||||
|
{
|
||||||
|
".png" or ".jpg" or ".jpeg" or ".gif" or ".bmp" or ".tiff" or ".svg" => "ms-appx:///Assets/EditorIcons/image-{0}.png",
|
||||||
|
_ => "ms-appx:///Assets/EditorIcons/document-{0}.png",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var sizeIndex = size switch
|
||||||
|
{
|
||||||
|
IconSize.Small => "0",
|
||||||
|
IconSize.Large => "1",
|
||||||
|
_ => "0"
|
||||||
|
};
|
||||||
|
|
||||||
|
iconPath = string.Format(iconPath, sizeIndex);
|
||||||
|
return iconPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
using CommunityToolkit.WinUI;
|
using CommunityToolkit.WinUI;
|
||||||
|
using Ghost.Editor.Core.Contracts;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.Progress;
|
namespace Ghost.Editor.Core.Services;
|
||||||
|
|
||||||
public class ProgressService : IProgressService
|
public class ProgressService : IProgressService
|
||||||
{
|
{
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using Microsoft.UI.Xaml;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.Utilities;
|
|
||||||
|
|
||||||
public static class EditorApplication
|
|
||||||
{
|
|
||||||
private static IServiceProvider? _serviceProvider;
|
|
||||||
|
|
||||||
public static Application Current => Application.Current;
|
|
||||||
|
|
||||||
internal static void Initialize(IServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
_serviceProvider = serviceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T GetService<T>()
|
|
||||||
where T : class
|
|
||||||
{
|
|
||||||
if (_serviceProvider?.GetService(typeof(T)) is not T service)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
using Ghost.Data.Models;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.Utilities;
|
namespace Ghost.Editor.Core.Utilities;
|
||||||
|
|
||||||
internal static class FileExtensions
|
internal static class FileExtensions
|
||||||
{
|
{
|
||||||
public const string META_FILE_EXTENSION = ".gmeta";
|
public const string META_FILE_EXTENSION = ".gmeta";
|
||||||
|
|
||||||
public const string PROJECT_FILE_EXTENSION = "." + ProjectMetadata.PROJECT_FILE_EXTENSION_NAME;
|
public const string PROJECT_FILE_EXTENSION = ".gproj";
|
||||||
public const string TEMPLATE_FILE_EXTENSION = ".gtmpl";
|
public const string TEMPLATE_FILE_EXTENSION = ".gtmpl";
|
||||||
public const string SCENE_FILE_EXTENSION = ".gscene";
|
public const string SCENE_FILE_EXTENSION = ".gscene";
|
||||||
public const string ASSET_FILE_EXTENSION = ".gasset";
|
public const string ASSET_FILE_EXTENSION = ".gasset";
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ public static class TypeCache
|
|||||||
s_types = loadableTypes.Select(t => t.GetTypeInfo()).ToArray();
|
s_types = loadableTypes.Select(t => t.GetTypeInfo()).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void Init()
|
||||||
|
{
|
||||||
|
// Intentionally left blank.
|
||||||
|
// This method exists to force the static constructor to run.
|
||||||
|
}
|
||||||
|
|
||||||
public static Type[] GetTypes()
|
public static Type[] GetTypes()
|
||||||
{
|
{
|
||||||
return s_types;
|
return s_types;
|
||||||
|
|||||||
@@ -1,30 +1,67 @@
|
|||||||
using Ghost.Data.Resources;
|
using Ghost.Editor.Core;
|
||||||
using Ghost.Data.Services;
|
|
||||||
using Ghost.Editor.Core.Utilities;
|
using Ghost.Editor.Core.Utilities;
|
||||||
|
using Ghost.Editor.Models;
|
||||||
|
using Ghost.Engine;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Ghost.Editor;
|
namespace Ghost.Editor;
|
||||||
|
|
||||||
internal static class ActivationHandler
|
internal static class ActivationHandler
|
||||||
{
|
{
|
||||||
private static void FolderInitialization()
|
public static LaunchArguments ParseArguments(ReadOnlySpan<char> args)
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(DataPath.s_applicationDataFolder))
|
var arguments = new LaunchArguments();
|
||||||
|
var properties = typeof(LaunchArguments).GetProperties();
|
||||||
|
var split = args.Split(' ');
|
||||||
|
|
||||||
|
while (split.MoveNext())
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(DataPath.s_applicationDataFolder);
|
var range = split.Current;
|
||||||
|
var arg = args[range.Start..range.End];
|
||||||
|
if (arg.Length > 2)
|
||||||
|
{
|
||||||
|
if (arg[0] == '-' && arg[1] == '-')
|
||||||
|
{
|
||||||
|
var argName = arg[2..];
|
||||||
|
foreach (var property in properties)
|
||||||
|
{
|
||||||
|
var propName = property.Name;
|
||||||
|
var attr = property.GetCustomAttributes<ArgumentNameAttribute>(false).FirstOrDefault();
|
||||||
|
if (attr != null)
|
||||||
|
{
|
||||||
|
propName = attr.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argName.Equals(propName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (split.MoveNext())
|
||||||
|
{
|
||||||
|
var valueRange = split.Current;
|
||||||
|
var value = args[valueRange.Start..valueRange.End];
|
||||||
|
var convertedValue = Convert.ChangeType(value.ToString(), property.PropertyType);
|
||||||
|
|
||||||
|
property.SetValue(arguments, convertedValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Directory.Exists(DataPath.s_projectTemplateFolder))
|
return arguments;
|
||||||
{
|
|
||||||
Directory.CreateDirectory(DataPath.s_projectTemplateFolder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Handle(LaunchActivatedEventArgs args)
|
public static async Task HandleAsync(LaunchArguments args)
|
||||||
{
|
{
|
||||||
FolderInitialization();
|
await Task.Run(() =>
|
||||||
ProjectService.EnsureDefaultTemplate();
|
{
|
||||||
|
TypeCache.Init();
|
||||||
|
((EngineCore)App.GetService<IEngineContext>()).Init();
|
||||||
|
});
|
||||||
|
|
||||||
EditorApplication.Initialize(((App)(Application.Current)).Host.Services);
|
// TODO: Initialize other subsystems here.
|
||||||
|
// await Task.Delay(10000); // Wait 10 seconds to simulate work.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,9 +9,8 @@
|
|||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||||
<ResourceDictionary Source="ms-appx:///Microsoft.UI.Xaml/DensityStyles/Compact.xaml" />
|
<ResourceDictionary Source="/Themes/Generic.xaml" />
|
||||||
<core:ControlsDictionary />
|
<core:ControlsDictionary />
|
||||||
<ResourceDictionary Source="/Themes/Override.xaml" />
|
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Editor.Core.AppState;
|
using Ghost.Editor.Core;
|
||||||
using Ghost.Editor.Core.Inspector;
|
using Ghost.Editor.Core.Contracts;
|
||||||
using Ghost.Editor.Core.Notifications;
|
using Ghost.Editor.Core.Services;
|
||||||
using Ghost.Editor.Core.Progress;
|
using Ghost.Editor.View.Pages.EngineEditor;
|
||||||
using Ghost.Editor.Utilities;
|
using Ghost.Editor.View.Windows;
|
||||||
|
using Ghost.Editor.ViewModels.Controls;
|
||||||
|
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||||
|
using Ghost.Editor.ViewModels.Windows;
|
||||||
|
using Ghost.Engine;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
using System.Diagnostics;
|
||||||
// To learn more about WinUI, the WinUI project structure,
|
using WinUIEx;
|
||||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
|
||||||
|
|
||||||
namespace Ghost.Editor;
|
namespace Ghost.Editor;
|
||||||
|
|
||||||
@@ -53,13 +56,32 @@ public partial class App : Application
|
|||||||
UseContentRoot(AppContext.BaseDirectory).
|
UseContentRoot(AppContext.BaseDirectory).
|
||||||
ConfigureServices((context, services) =>
|
ConfigureServices((context, services) =>
|
||||||
{
|
{
|
||||||
HostHelper.AddLandingScope(context, services);
|
services.AddSingleton<IEngineContext, EngineCore>();
|
||||||
HostHelper.AddEngineScope(context, services);
|
|
||||||
|
|
||||||
services.AddSingleton<AppStateMachine>();
|
|
||||||
services.AddSingleton<INotificationService, NotificationService>();
|
services.AddSingleton<INotificationService, NotificationService>();
|
||||||
services.AddSingleton<IProgressService, ProgressService>();
|
services.AddSingleton<IProgressService, ProgressService>();
|
||||||
services.AddSingleton<IInspectorService, InspectorService>();
|
services.AddSingleton<IInspectorService, InspectorService>();
|
||||||
|
services.AddSingleton<IPreviewService, PreviewService>();
|
||||||
|
|
||||||
|
services.AddSingleton<EngineEditorViewModel>();
|
||||||
|
|
||||||
|
services.AddTransient<ProjectBrowserViewModel>();
|
||||||
|
|
||||||
|
#region Should be deleted
|
||||||
|
services.AddTransient<ScenePage>();
|
||||||
|
|
||||||
|
services.AddTransient<HierarchyPage>();
|
||||||
|
services.AddTransient<HierarchyViewModel>();
|
||||||
|
|
||||||
|
services.AddTransient<ProjectPage>();
|
||||||
|
services.AddTransient<ProjectViewModel>();
|
||||||
|
|
||||||
|
services.AddTransient<ConsolePage>();
|
||||||
|
services.AddTransient<ConsoleViewModel>();
|
||||||
|
|
||||||
|
services.AddTransient<InspectorPage>();
|
||||||
|
services.AddTransient<InspectorViewModel>();
|
||||||
|
#endregion
|
||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
@@ -81,32 +103,55 @@ public partial class App : Application
|
|||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invoked when the application is launched.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="args">Details about the launch request and process.</param>
|
|
||||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||||
{
|
{
|
||||||
base.OnLaunched(args);
|
base.OnLaunched(args);
|
||||||
|
|
||||||
|
var arguments = ActivationHandler.ParseArguments("--project-path F:/GhostProject/Test2 --project-name Test2"); // args.Arguments
|
||||||
|
if (!arguments.IsValid())
|
||||||
|
{
|
||||||
|
Exit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorApplication.Initialize(Host.Services, arguments.ProjectPath, arguments.ProjectName);
|
||||||
|
|
||||||
|
var splashWindow = new SplashWindow();
|
||||||
|
splashWindow.Activate();
|
||||||
|
Window = splashWindow;
|
||||||
|
|
||||||
await Host.StartAsync();
|
await Host.StartAsync();
|
||||||
ActivationHandler.Handle(args);
|
await ActivationHandler.HandleAsync(arguments);
|
||||||
|
|
||||||
var stateMachine = GetService<AppStateMachine>();
|
splashWindow.Hide();
|
||||||
stateMachine.RegisterState(StateKey.Landing, () => new LandingState());
|
|
||||||
stateMachine.RegisterState(StateKey.EngineEditor, () => new EditorState());
|
|
||||||
|
|
||||||
await stateMachine.TransitionToAsync(StateKey.Landing);
|
var editorWindow = new EngineEditorWindow();
|
||||||
|
editorWindow.Activate();
|
||||||
|
Window = editorWindow;
|
||||||
|
|
||||||
|
splashWindow.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClosed(object? sender, WindowEventArgs args)
|
private void OnClosed(object? sender, WindowEventArgs args)
|
||||||
{
|
{
|
||||||
Host.StopAsync().GetAwaiter().GetResult();
|
try
|
||||||
Host.Dispose();
|
{
|
||||||
|
Host.StopAsync().GetAwaiter().GetResult();
|
||||||
|
Host.Dispose();
|
||||||
|
|
||||||
|
EditorApplication.Shutdown();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debugger.BreakForUserUnhandledException(ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
Logger.LogError(e.Exception);
|
Logger.LogError(e.Exception);
|
||||||
|
#if DEBUG
|
||||||
|
Debugger.BreakForUserUnhandledException(e.Exception);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIN
Ghost.Editor/Assets/EditorIcons/document-0.png
Normal file
|
After Width: | Height: | Size: 453 B |
BIN
Ghost.Editor/Assets/EditorIcons/document-1.png
Normal file
|
After Width: | Height: | Size: 869 B |
BIN
Ghost.Editor/Assets/EditorIcons/folder-0.png
Normal file
|
After Width: | Height: | Size: 465 B |
BIN
Ghost.Editor/Assets/EditorIcons/folder-1.png
Normal file
|
After Width: | Height: | Size: 884 B |
BIN
Ghost.Editor/Assets/EditorIcons/image-0.png
Normal file
|
After Width: | Height: | Size: 727 B |
BIN
Ghost.Editor/Assets/EditorIcons/image-1.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 433 B After Width: | Height: | Size: 580 B |
|
Before Width: | Height: | Size: 583 B After Width: | Height: | Size: 825 B |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 852 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.6 KiB |
BIN
Ghost.Editor/Assets/icon.ico
Normal file
|
After Width: | Height: | Size: 170 KiB |
22
Ghost.Editor/Assets/icon.svg
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48" width="48px" height="48px">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: url(#Безымянный_градиент_199);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-2 {
|
||||||
|
fill: #f7c13a;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<linearGradient id="Безымянный_градиент_199" data-name="Безымянный градиент 199" x1="11.80415" y1="-22.237" x2="29.6085" y2="45.263" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" stop-color="#6d6d6d"/>
|
||||||
|
<stop offset="0.12552" stop-color="#626262"/>
|
||||||
|
<stop offset="0.987" stop-color="#464646"/>
|
||||||
|
<stop offset="0.998" stop-color="#454545"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="cls-1" x="6" y="6" width="36" height="36" rx="2"/>
|
||||||
|
<path class="cls-2" d="M22,27v1.5a.5.5,0,0,1-.5.5H17a8,8,0,0,1-8-8V19.5a.5.5,0,0,1,.5-.5h4.00612a.501.501,0,0,1,.49758.49688C14.05815,23.13441,14.70582,26,15.5,26c.73344,0,1.33761-2.43083,1.46835-5.65979a.50624.50624,0,0,1,.74437-.42711A7.99072,7.99072,0,0,1,22,27Z"/>
|
||||||
|
<path class="cls-2" d="M39,19.5V21a8,8,0,0,1-8,8H26.5a.5.5,0,0,1-.5-.5V27a7.99072,7.99072,0,0,1,4.28728-7.0869.50624.50624,0,0,1,.74437.42711C31.16239,23.56917,31.76656,26,32.5,26c.79418,0,1.44185-2.86559,1.4963-6.50312A.501.501,0,0,1,34.49388,19H38.5A.5.5,0,0,1,39,19.5Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 580 B After Width: | Height: | Size: 580 B |
|
Before Width: | Height: | Size: 580 B After Width: | Height: | Size: 580 B |
|
Before Width: | Height: | Size: 825 B After Width: | Height: | Size: 825 B |
|
Before Width: | Height: | Size: 825 B After Width: | Height: | Size: 825 B |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -1,38 +0,0 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using Ghost.Editor.Core.Contracts;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
using Microsoft.UI.Xaml.Navigation;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Controls;
|
|
||||||
|
|
||||||
public abstract partial class ViewModelPage<VM> : Page
|
|
||||||
where VM : ObservableObject
|
|
||||||
{
|
|
||||||
public VM ViewModel
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ViewModelPage(VM viewModel)
|
|
||||||
{
|
|
||||||
ViewModel = viewModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnNavigatedTo(e);
|
|
||||||
if (ViewModel is INavigationAware navigationAware)
|
|
||||||
{
|
|
||||||
navigationAware.OnNavigatedTo(e.Parameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnNavigatedFrom(e);
|
|
||||||
if (ViewModel is INavigationAware navigationAware)
|
|
||||||
{
|
|
||||||
navigationAware.OnNavigatedFrom();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
using Ghost.Core;
|
|
||||||
using Ghost.Data.Models;
|
|
||||||
using Ghost.Data.Services;
|
|
||||||
using Ghost.Editor.Core.AssetHandle;
|
|
||||||
using Ghost.Editor.View.Windows;
|
|
||||||
using Ghost.Engine;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.AppState;
|
|
||||||
|
|
||||||
internal class EditorState : IAppState
|
|
||||||
{
|
|
||||||
private EngineEditorWindow? _window;
|
|
||||||
private EngineCore? _engineCore;
|
|
||||||
|
|
||||||
public ValueTask<Result> OnExitingAsync()
|
|
||||||
{
|
|
||||||
if (App.Window == _window)
|
|
||||||
{
|
|
||||||
App.Window = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_engineCore?.Dispose();
|
|
||||||
|
|
||||||
return ValueTask.FromResult(Result.Success());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<Result> OnEnteringAsync(object? parameter)
|
|
||||||
{
|
|
||||||
if (parameter is not ProjectMetadataInfo metadataInfo)
|
|
||||||
{
|
|
||||||
return ValueTask.FromResult(Result.Failure("Invalid parameter for entering EditorState."));
|
|
||||||
}
|
|
||||||
|
|
||||||
ProjectService.CurrentProject = metadataInfo;
|
|
||||||
|
|
||||||
_engineCore = App.GetService<EngineCore>();
|
|
||||||
_engineCore.Init();
|
|
||||||
|
|
||||||
_window = App.GetService<EngineEditorWindow>();
|
|
||||||
_window.Activate();
|
|
||||||
|
|
||||||
App.Window = _window;
|
|
||||||
|
|
||||||
return ValueTask.FromResult(Result.Success());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<Result> OnExitedAsync()
|
|
||||||
{
|
|
||||||
_window?.Close();
|
|
||||||
_window = null;
|
|
||||||
|
|
||||||
return ValueTask.FromResult(Result.Success());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask<Result> OnEnteredAsync(object? parameter)
|
|
||||||
{
|
|
||||||
await AssetDatabase.Initialize();
|
|
||||||
return Result.Success();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using Ghost.Core;
|
|
||||||
using Ghost.Editor.View.Windows;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.AppState;
|
|
||||||
|
|
||||||
internal class LandingState : IAppState
|
|
||||||
{
|
|
||||||
private LandingWindow? _window;
|
|
||||||
|
|
||||||
public ValueTask<Result> OnExitingAsync()
|
|
||||||
{
|
|
||||||
if (App.Window == _window)
|
|
||||||
{
|
|
||||||
App.Window = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ValueTask.FromResult(Result.Success());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<Result> OnEnteringAsync(object? parameter)
|
|
||||||
{
|
|
||||||
_window = App.GetService<LandingWindow>();
|
|
||||||
_window.Activate();
|
|
||||||
|
|
||||||
App.Window = _window;
|
|
||||||
|
|
||||||
return ValueTask.FromResult(Result.Success());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<Result> OnExitedAsync()
|
|
||||||
{
|
|
||||||
_window?.Close();
|
|
||||||
_window = null;
|
|
||||||
|
|
||||||
return ValueTask.FromResult(Result.Success());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask<Result> OnEnteredAsync(object? parameter)
|
|
||||||
{
|
|
||||||
return ValueTask.FromResult(Result.Success());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -45,7 +45,106 @@
|
|||||||
<ProjectReference Include="..\Ghost.Entities\Ghost.Entities.csproj" />
|
<ProjectReference Include="..\Ghost.Entities\Ghost.Entities.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="View\Pages\Landing\CreateProjectPage.xaml">
|
<Content Update="Assets\EditorIcons\document-0.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\EditorIcons\document-1.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\EditorIcons\image-0.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\EditorIcons\image-1.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.ico">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.scale-100.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.scale-125.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.scale-150.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.scale-200.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.scale-400.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.svg">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-16.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-16_altform-lightunplated.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-16_altform-unplated.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-24.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-24_altform-lightunplated.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-24_altform-unplated.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-256.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-256_altform-lightunplated.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-256_altform-unplated.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-32.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-32_altform-lightunplated.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-32_altform-unplated.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-48.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-48_altform-lightunplated.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Assets\icon.targetsize-48_altform-unplated.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<None Update="Assets\icon.svg">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Assets\EditorIcons\folder-0.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Assets\EditorIcons\folder-1.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Assets\icon.ico">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Themes\Generic.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="View\Windows\SplashWindow.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -54,11 +153,6 @@
|
|||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Page Update="View\Pages\Landing\OpenProjectPage.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="View\Pages\EngineEditor\InspectorPage.xaml">
|
<Page Update="View\Pages\EngineEditor\InspectorPage.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
@@ -79,11 +173,6 @@
|
|||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Page Update="Themes\Override.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="View\Windows\EngineEditorWindow.xaml">
|
<Page Update="View\Windows\EngineEditorWindow.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
|||||||
35
Ghost.Editor/Models/LaunchArguments.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
namespace Ghost.Editor.Models;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
|
internal sealed class ArgumentNameAttribute : Attribute
|
||||||
|
{
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArgumentNameAttribute(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class LaunchArguments
|
||||||
|
{
|
||||||
|
[ArgumentName("project-path")]
|
||||||
|
public string ProjectPath
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
} = string.Empty;
|
||||||
|
|
||||||
|
[ArgumentName("project-name")]
|
||||||
|
public string ProjectName
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
} = string.Empty;
|
||||||
|
|
||||||
|
public bool IsValid()
|
||||||
|
{
|
||||||
|
return Directory.Exists(ProjectPath) && !string.IsNullOrWhiteSpace(ProjectName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<uap:VisualElements
|
<uap:VisualElements
|
||||||
DisplayName="GhostEngine"
|
DisplayName="GhostEngine"
|
||||||
Description="GhostEngine"
|
Description="GhostEngine"
|
||||||
Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Icon.png" BackgroundColor="transparent">
|
Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\icon.png" BackgroundColor="transparent">
|
||||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" >
|
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" >
|
||||||
</uap:DefaultTile >
|
</uap:DefaultTile >
|
||||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||||
|
|||||||
54
Ghost.Editor/Themes/Generic.xaml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<ResourceDictionary
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
|
||||||
|
xmlns:internal="using:Ghost.Editor.Controls.Internal"
|
||||||
|
xmlns:local="using:Ghost.Editor.Core">
|
||||||
|
|
||||||
|
<ResourceDictionary.ThemeDictionaries>
|
||||||
|
<ResourceDictionary x:Key="Dark">
|
||||||
|
<StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" />
|
||||||
|
<StaticResource x:Key="TabViewBackground" ResourceKey="AcrylicBackgroundFillColorBaseBrush" />
|
||||||
|
</ResourceDictionary>
|
||||||
|
<ResourceDictionary x:Key="Light">
|
||||||
|
<StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" />
|
||||||
|
<StaticResource x:Key="TabViewBackground" ResourceKey="AcrylicBackgroundFillColorBaseBrush" />
|
||||||
|
</ResourceDictionary>
|
||||||
|
</ResourceDictionary.ThemeDictionaries>
|
||||||
|
|
||||||
|
<!-- Compact sizing -->
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="12" />
|
||||||
|
</Style>
|
||||||
|
<x:Double x:Key="ControlContentThemeFontSize">12</x:Double>
|
||||||
|
<x:Double x:Key="ContentControlFontSize">12</x:Double>
|
||||||
|
<x:Double x:Key="TextControlThemeMinHeight">24</x:Double>
|
||||||
|
<Thickness x:Key="TextControlThemePadding">2,2,6,1</Thickness>
|
||||||
|
<x:Double x:Key="ListViewItemMinHeight">32</x:Double>
|
||||||
|
<x:Double x:Key="TreeViewItemMinHeight">24</x:Double>
|
||||||
|
<x:Double x:Key="TreeViewItemMultiSelectCheckBoxMinHeight">24</x:Double>
|
||||||
|
<x:Double x:Key="TreeViewItemPresenterMargin">0</x:Double>
|
||||||
|
<x:Double x:Key="TreeViewItemPresenterPadding">0</x:Double>
|
||||||
|
<Thickness x:Key="TimePickerHostPadding">0,1,0,2</Thickness>
|
||||||
|
<Thickness x:Key="DatePickerHostPadding">0,1,0,2</Thickness>
|
||||||
|
<Thickness x:Key="DatePickerHostMonthPadding">9,0,0,1</Thickness>
|
||||||
|
<Thickness x:Key="ComboBoxEditableTextPadding">10,0,30,0</Thickness>
|
||||||
|
<x:Double x:Key="ComboBoxMinHeight">24</x:Double>
|
||||||
|
<Thickness x:Key="ComboBoxPadding">12,1,0,3</Thickness>
|
||||||
|
<x:Double x:Key="NavigationViewItemOnLeftMinHeight">32</x:Double>
|
||||||
|
|
||||||
|
<!-- Control override -->
|
||||||
|
<Style TargetType="internal:NavigationTabView">
|
||||||
|
<Setter Property="TabWidthMode" Value="Compact" />
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="NumberBox" />
|
||||||
|
|
||||||
|
<!-- Named Style -->
|
||||||
|
<Style
|
||||||
|
x:Key="ToolbarButton"
|
||||||
|
BasedOn="{StaticResource SubtleButtonStyle}"
|
||||||
|
TargetType="Button" />
|
||||||
|
|
||||||
|
<!-- Named Resource -->
|
||||||
|
<x:Double x:Key="ToolbarIconSize">12</x:Double>
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<ResourceDictionary
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
|
|
||||||
xmlns:internal="using:Ghost.Editor.Controls.Internal">
|
|
||||||
<ResourceDictionary.ThemeDictionaries>
|
|
||||||
<ResourceDictionary x:Key="Dark">
|
|
||||||
<StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" />
|
|
||||||
</ResourceDictionary>
|
|
||||||
<ResourceDictionary x:Key="Light">
|
|
||||||
<StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" />
|
|
||||||
</ResourceDictionary>
|
|
||||||
</ResourceDictionary.ThemeDictionaries>
|
|
||||||
|
|
||||||
<Style TargetType="internal:NavigationTabView">
|
|
||||||
<Setter Property="TabWidthMode" Value="Compact" />
|
|
||||||
</Style>
|
|
||||||
<Style TargetType="NumberBox" />
|
|
||||||
</ResourceDictionary>
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using Ghost.Editor.Core.Contracts;
|
||||||
|
using Ghost.Editor.Models;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
|
||||||
|
namespace Ghost.Editor.Utilities.Converters;
|
||||||
|
|
||||||
|
public partial class ExplorerItemToIconUriConverter : IValueConverter
|
||||||
|
{
|
||||||
|
private readonly IPreviewService _previewService = App.GetService<IPreviewService>();
|
||||||
|
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
if (value is ExplorerItem item)
|
||||||
|
{
|
||||||
|
var path = _previewService.GetIconPath(item.FullName, item.IsDirectory, IconSize.Small);
|
||||||
|
return new Uri(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
using Ghost.Data.Services;
|
|
||||||
using Ghost.Editor.View.Pages.EngineEditor;
|
|
||||||
using Ghost.Editor.View.Pages.Landing;
|
|
||||||
using Ghost.Editor.View.Windows;
|
|
||||||
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
|
||||||
using Ghost.Editor.ViewModels.Pages.Landing;
|
|
||||||
using Ghost.Editor.ViewModels.Windows;
|
|
||||||
using Ghost.Engine;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Utilities;
|
|
||||||
|
|
||||||
internal static partial class HostHelper
|
|
||||||
{
|
|
||||||
public static void AddLandingScope(HostBuilderContext context, IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.AddSingleton<LandingWindow>();
|
|
||||||
|
|
||||||
services.AddTransient<CreateProjectPage>();
|
|
||||||
services.AddTransient<CreateProjectViewModel>();
|
|
||||||
|
|
||||||
services.AddTransient<OpenProjectPage>();
|
|
||||||
services.AddTransient<OpenProjectViewModel>();
|
|
||||||
|
|
||||||
services.AddTransient<ProjectService>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddEngineScope(HostBuilderContext context, IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.AddSingleton<EngineCore>();
|
|
||||||
|
|
||||||
services.AddSingleton<EngineEditorWindow>();
|
|
||||||
services.AddSingleton<EngineEditorViewModel>();
|
|
||||||
|
|
||||||
services.AddTransient<ScenePage>();
|
|
||||||
|
|
||||||
services.AddTransient<HierarchyPage>();
|
|
||||||
services.AddTransient<HierarchyViewModel>();
|
|
||||||
|
|
||||||
services.AddTransient<ProjectPage>();
|
|
||||||
services.AddTransient<ProjectViewModel>();
|
|
||||||
|
|
||||||
services.AddTransient<ConsolePage>();
|
|
||||||
services.AddTransient<ConsoleViewModel>();
|
|
||||||
|
|
||||||
services.AddTransient<InspectorPage>();
|
|
||||||
services.AddTransient<InspectorViewModel>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
224
Ghost.Editor/View/Controls/ProjectBrowser.xaml
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<UserControl
|
||||||
|
x:Class="Ghost.Editor.Controls.ProjectBrowser"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:converter="using:Ghost.Editor.Utilities.Converters"
|
||||||
|
xmlns:ctc="using:CommunityToolkit.WinUI.Controls"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="using:Ghost.Editor.Controls"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:model="using:Ghost.Editor.Models"
|
||||||
|
xmlns:sys="using:System"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<UserControl.Resources>
|
||||||
|
<converter:ExplorerItemToIconUriConverter x:Key="ExplorerItemToIconUriConverter" />
|
||||||
|
</UserControl.Resources>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Toolbar -->
|
||||||
|
<Grid
|
||||||
|
Height="36"
|
||||||
|
Padding="4"
|
||||||
|
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||||
|
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||||
|
BorderThickness="0,0,0,1">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||||
|
<Button Style="{StaticResource ToolbarButton}">
|
||||||
|
<Button.Content>
|
||||||
|
<FontIcon FontSize="{StaticResource ToolbarIconSize}" Glyph="" />
|
||||||
|
</Button.Content>
|
||||||
|
<Button.Flyout>
|
||||||
|
<MenuFlyout>
|
||||||
|
<MenuFlyoutItem Text="Folder" />
|
||||||
|
<MenuFlyoutItem Text="Script" />
|
||||||
|
<MenuFlyoutSubItem Text="Rendering">
|
||||||
|
<MenuFlyoutItem Text="Material" />
|
||||||
|
<MenuFlyoutItem Text="Volume Profile" />
|
||||||
|
</MenuFlyoutSubItem>
|
||||||
|
</MenuFlyout>
|
||||||
|
</Button.Flyout>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="2" Orientation="Horizontal">
|
||||||
|
<AutoSuggestBox Width="250" QueryIcon="Find" />
|
||||||
|
<AppBarSeparator />
|
||||||
|
<Button Style="{StaticResource ToolbarButton}">
|
||||||
|
<Button.Content>
|
||||||
|
<FontIcon FontSize="{StaticResource ToolbarIconSize}" Glyph="" />
|
||||||
|
</Button.Content>
|
||||||
|
<Button.Flyout>
|
||||||
|
<MenuFlyout>
|
||||||
|
<ToggleMenuFlyoutItem Text="Animation" />
|
||||||
|
<ToggleMenuFlyoutItem Text="Audio" />
|
||||||
|
<ToggleMenuFlyoutItem Text="Material" />
|
||||||
|
<ToggleMenuFlyoutItem Text="Script" />
|
||||||
|
<ToggleMenuFlyoutItem Text="Texture" />
|
||||||
|
</MenuFlyout>
|
||||||
|
</Button.Flyout>
|
||||||
|
</Button>
|
||||||
|
<Button Style="{StaticResource ToolbarButton}">
|
||||||
|
<Button.Content>
|
||||||
|
<FontIcon FontSize="{StaticResource ToolbarIconSize}" Glyph="" />
|
||||||
|
</Button.Content>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- Conent Viewer -->
|
||||||
|
<Grid Grid.Row="1">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Border
|
||||||
|
Grid.Column="0"
|
||||||
|
Width="200"
|
||||||
|
Padding="4,0,0,0"
|
||||||
|
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||||
|
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||||
|
BorderThickness="0,0,1,0">
|
||||||
|
<TreeView
|
||||||
|
x:Name="PART_DirectoriesView"
|
||||||
|
ItemsSource="{x:Bind ViewModel.Directories, Mode=OneWay}"
|
||||||
|
SelectionChanged="PART_DirectoriesView_SelectionChanged"
|
||||||
|
SelectionMode="Single">
|
||||||
|
<TreeView.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="model:ExplorerItem">
|
||||||
|
<TreeViewItem ItemsSource="{x:Bind Children}">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||||
|
<!-- TODO: Open/Close folder icon based on state -->
|
||||||
|
<FontIcon
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="12"
|
||||||
|
Glyph="" />
|
||||||
|
<TextBlock
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="{x:Bind Name}"
|
||||||
|
TextTrimming="CharacterEllipsis" />
|
||||||
|
</StackPanel>
|
||||||
|
</TreeViewItem>
|
||||||
|
</DataTemplate>
|
||||||
|
</TreeView.ItemTemplate>
|
||||||
|
</TreeView>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Grid Grid.Column="1">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<!--<RowDefinition Height="Auto" />-->
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!--<Border
|
||||||
|
Grid.Row="0"
|
||||||
|
Height="24"
|
||||||
|
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||||
|
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||||
|
BorderThickness="0,0,0,1">
|
||||||
|
<BreadcrumbBar />
|
||||||
|
</Border>-->
|
||||||
|
|
||||||
|
<ItemsView
|
||||||
|
x:Name="PART_FilesView"
|
||||||
|
Grid.Row="0"
|
||||||
|
Padding="8"
|
||||||
|
DoubleTapped="PART_FilesView_DoubleTapped"
|
||||||
|
IsDoubleTapEnabled="True"
|
||||||
|
ItemsSource="{x:Bind ViewModel.Files, Mode=OneWay}"
|
||||||
|
SelectionChanged="PART_FilesView_SelectionChanged"
|
||||||
|
SelectionMode="Single">
|
||||||
|
<ItemsView.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="model:ExplorerItem">
|
||||||
|
<ItemContainer>
|
||||||
|
<Grid
|
||||||
|
Padding="8"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
RowSpacing="4">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid.ContextFlyout>
|
||||||
|
<MenuFlyout>
|
||||||
|
<MenuFlyoutItem Text="Open" />
|
||||||
|
<MenuFlyoutItem Text="Rename" />
|
||||||
|
<MenuFlyoutItem Text="Delete" />
|
||||||
|
<MenuFlyoutItem Text="Show in Explorer" />
|
||||||
|
</MenuFlyout>
|
||||||
|
</Grid.ContextFlyout>
|
||||||
|
|
||||||
|
<ctc:ConstrainedBox Grid.Row="0" AspectRatio="1:1">
|
||||||
|
<Image HorizontalAlignment="Center">
|
||||||
|
<Image.Source>
|
||||||
|
<BitmapImage DecodePixelWidth="48" UriSource="{x:Bind Converter={StaticResource ExplorerItemToIconUriConverter}}" />
|
||||||
|
</Image.Source>
|
||||||
|
</Image>
|
||||||
|
</ctc:ConstrainedBox>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="{x:Bind Name}"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
TextWrapping="NoWrap" />
|
||||||
|
</Grid>
|
||||||
|
</ItemContainer>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsView.ItemTemplate>
|
||||||
|
<ItemsView.Layout>
|
||||||
|
<UniformGridLayout
|
||||||
|
ItemsStretch="Fill"
|
||||||
|
MinColumnSpacing="4"
|
||||||
|
MinItemWidth="72"
|
||||||
|
MinRowSpacing="4" />
|
||||||
|
</ItemsView.Layout>
|
||||||
|
<ItemsView.ContextFlyout>
|
||||||
|
<MenuFlyout>
|
||||||
|
<MenuFlyoutSubItem Text="Create">
|
||||||
|
<MenuFlyoutItem Text="Folder" />
|
||||||
|
<MenuFlyoutItem Text="Script" />
|
||||||
|
<MenuFlyoutSubItem Text="Rendering">
|
||||||
|
<MenuFlyoutItem Text="Material" />
|
||||||
|
<MenuFlyoutItem Text="Volume Profile" />
|
||||||
|
</MenuFlyoutSubItem>
|
||||||
|
</MenuFlyoutSubItem>
|
||||||
|
<MenuFlyoutItem Text="Show in Explorer" />
|
||||||
|
<MenuFlyoutSeparator />
|
||||||
|
<MenuFlyoutItem Text="Refresh" />
|
||||||
|
<MenuFlyoutItem Text="Reimport All" />
|
||||||
|
</MenuFlyout>
|
||||||
|
</ItemsView.ContextFlyout>
|
||||||
|
</ItemsView>
|
||||||
|
|
||||||
|
<Border
|
||||||
|
Grid.Row="1"
|
||||||
|
Height="24"
|
||||||
|
Padding="8,2"
|
||||||
|
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||||
|
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||||
|
BorderThickness="0,1,0,0">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="{x:Bind sys:String.Format('{0} items', ViewModel.Files.Count), Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
122
Ghost.Editor/View/Controls/ProjectBrowser.xaml.cs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
using Ghost.Editor.Core.Contracts;
|
||||||
|
using Ghost.Editor.Models;
|
||||||
|
using Ghost.Editor.ViewModels.Controls;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace Ghost.Editor.Controls;
|
||||||
|
|
||||||
|
internal sealed partial class ProjectBrowser : UserControl
|
||||||
|
{
|
||||||
|
private readonly IInspectorService _inspectorService;
|
||||||
|
private bool _isUpdatingSelection = false;
|
||||||
|
|
||||||
|
public ProjectBrowserViewModel ViewModel
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProjectBrowser()
|
||||||
|
{
|
||||||
|
_inspectorService = App.GetService<IInspectorService>();
|
||||||
|
ViewModel = App.GetService<ProjectBrowserViewModel>();
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
Loaded += ProjectBrowser_Loaded;
|
||||||
|
Unloaded += ProjectBrowser_Unloaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProjectBrowser_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_inspectorService.OnSelectionChanged += _inspectorService_OnSelectionChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProjectBrowser_Unloaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_inspectorService.OnSelectionChanged -= _inspectorService_OnSelectionChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _inspectorService_OnSelectionChanged(object? sender, InspectorSelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Source is not ProjectBrowserViewModel)
|
||||||
|
{
|
||||||
|
PART_FilesView.DeselectAll();
|
||||||
|
PART_DirectoriesView.SelectedNodes.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PART_DirectoriesView_SelectionChanged(TreeView sender, TreeViewSelectionChangedEventArgs args)
|
||||||
|
{
|
||||||
|
if (_isUpdatingSelection)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isUpdatingSelection = true;
|
||||||
|
|
||||||
|
PART_FilesView.DeselectAll();
|
||||||
|
if (args.AddedItems.Count > 0 && args.AddedItems[0] is ExplorerItem selectedItem)
|
||||||
|
{
|
||||||
|
ViewModel.SelectedItem = selectedItem;
|
||||||
|
ViewModel.NavigateToDirectory(selectedItem.FullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
_isUpdatingSelection = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PART_FilesView_SelectionChanged(ItemsView sender, ItemsViewSelectionChangedEventArgs args)
|
||||||
|
{
|
||||||
|
if (_isUpdatingSelection)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isUpdatingSelection = true;
|
||||||
|
|
||||||
|
PART_DirectoriesView.SelectedNodes.Clear();
|
||||||
|
if (PART_FilesView.SelectedItem is ExplorerItem selectedItem)
|
||||||
|
{
|
||||||
|
ViewModel.SelectedItem = selectedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isUpdatingSelection = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void PART_FilesView_DoubleTapped(object sender, Microsoft.UI.Xaml.Input.DoubleTappedRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (PART_FilesView.SelectedItem is ExplorerItem selectedItem)
|
||||||
|
{
|
||||||
|
_isUpdatingSelection = true;
|
||||||
|
|
||||||
|
PART_FilesView.DeselectAll();
|
||||||
|
PART_DirectoriesView.SelectedNodes.Clear();
|
||||||
|
|
||||||
|
// NOTE: There is bug that the hover state of the item may remain when navigating to another folder.
|
||||||
|
// Which causes incorrect selection (double click a folder -> directories view select target folder -> files view select the item that has the same index as the double clicked one and the hover visual stay remain from last level)
|
||||||
|
// Not sure if this is a WinUI bug or something else. This may because of the virtualization of the ItemsView.
|
||||||
|
// The core issue is not sure why PART_FilesView_SelectionChanged been triggered after NavigateToDirectory is already finished. And this only happens after the first double click navigation (first time is always fine).
|
||||||
|
|
||||||
|
// HACK: Wait a moment to let the ui clear it's state, otherwise the bug above will happen because of the virtualization.
|
||||||
|
await Task.Delay(100);
|
||||||
|
|
||||||
|
ViewModel.SelectedItem = selectedItem;
|
||||||
|
|
||||||
|
var navigatedItem = ViewModel.OpenSelected();
|
||||||
|
if (navigatedItem.Item1 != null)
|
||||||
|
{
|
||||||
|
if (navigatedItem.Item2 == 0)
|
||||||
|
{
|
||||||
|
PART_DirectoriesView.SelectedItem = navigatedItem.Item1;
|
||||||
|
}
|
||||||
|
else if (navigatedItem.Item2 == 1)
|
||||||
|
{
|
||||||
|
var index = ViewModel.Files.IndexOf(navigatedItem.Item1);
|
||||||
|
PART_FilesView.Select(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_isUpdatingSelection = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using Ghost.Editor.Controls.Internal;
|
using Ghost.Editor.Controls.Internal;
|
||||||
using Ghost.Editor.Core.Inspector;
|
using Ghost.Editor.Core.Contracts;
|
||||||
using Ghost.Editor.Core.SceneGraph;
|
using Ghost.Editor.Core.SceneGraph;
|
||||||
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
@@ -38,11 +38,11 @@ internal sealed partial class HierarchyPage : NavigationTabPage
|
|||||||
{
|
{
|
||||||
if (args.AddedItems.Count > 0 && args.AddedItems[0] is IInspectable inspectable)
|
if (args.AddedItems.Count > 0 && args.AddedItems[0] is IInspectable inspectable)
|
||||||
{
|
{
|
||||||
_inspectorService.SelectedInspectable = inspectable;
|
_inspectorService.SetSelected(inspectable, ViewModel);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_inspectorService.SelectedInspectable = null;
|
_inspectorService.SetSelected(null, ViewModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<Page
|
|
||||||
x:Class="Ghost.Editor.View.Pages.Landing.CreateProjectPage"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:data="using:Ghost.Data.Models"
|
|
||||||
xmlns:editor="using:Ghost.Editor.Core.Controls"
|
|
||||||
xmlns:local="using:Ghost.Editor.View.Pages.Landing"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
NavigationCacheMode="Enabled"
|
|
||||||
mc:Ignorable="d">
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<!-- Template Info -->
|
|
||||||
<Grid Grid.Column="0" Width="300">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="0"
|
|
||||||
Margin="0,0,0,24"
|
|
||||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
|
||||||
Text="Template" />
|
|
||||||
|
|
||||||
<ListView
|
|
||||||
Grid.Row="1"
|
|
||||||
ItemsSource="{x:Bind ViewModel.templates}"
|
|
||||||
SelectedItem="{x:Bind ViewModel.SelectedTemplate, Mode=TwoWay}">
|
|
||||||
<ListView.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="data:TemplateData">
|
|
||||||
<Grid>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<ImageIcon
|
|
||||||
Grid.Column="0"
|
|
||||||
Width="24"
|
|
||||||
Height="24">
|
|
||||||
<ImageIcon.Source>
|
|
||||||
<BitmapImage UriSource="{x:Bind GetIconURI()}" />
|
|
||||||
</ImageIcon.Source>
|
|
||||||
</ImageIcon>
|
|
||||||
<TextBlock
|
|
||||||
Grid.Column="1"
|
|
||||||
Margin="8,0"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{x:Bind Info.Name}" />
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
|
||||||
</ListView.ItemTemplate>
|
|
||||||
</ListView>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Project Info -->
|
|
||||||
<Grid
|
|
||||||
Grid.Column="1"
|
|
||||||
Margin="16,0,0,0"
|
|
||||||
Padding="16"
|
|
||||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
|
||||||
CornerRadius="{StaticResource OverlayCornerRadius}">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="300" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<Grid Grid.Row="0" CornerRadius="4">
|
|
||||||
<Image VerticalAlignment="Center" Stretch="UniformToFill">
|
|
||||||
<Image.Source>
|
|
||||||
<BitmapImage UriSource="{x:Bind ViewModel.SelectedTemplate.Value.GetPreviewURI(), Mode=OneWay}" />
|
|
||||||
</Image.Source>
|
|
||||||
</Image>
|
|
||||||
<Grid
|
|
||||||
MaxHeight="100"
|
|
||||||
VerticalAlignment="Bottom"
|
|
||||||
Background="{ThemeResource ControlOnImageFillColorDefaultBrush}">
|
|
||||||
<TextBlock
|
|
||||||
Margin="16"
|
|
||||||
VerticalAlignment="Bottom"
|
|
||||||
Foreground="{ThemeResource TextFillColorTertiaryBrush}"
|
|
||||||
Text="{x:Bind ViewModel.SelectedTemplate.Value.Info.Description, Mode=OneWay}" />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<StackPanel Grid.Row="1" Margin="8,0">
|
|
||||||
<TextBlock
|
|
||||||
Margin="0,16,0,8"
|
|
||||||
Style="{StaticResource TitleTextBlockStyle}"
|
|
||||||
Text="{x:Bind ViewModel.SelectedTemplate.Value.Info.Name, Mode=OneWay}" />
|
|
||||||
<TextBlock
|
|
||||||
Margin="0,8,0,16"
|
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
|
||||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
|
||||||
Text="Project Settings" />
|
|
||||||
|
|
||||||
<editor:PropertyField Label="Name">
|
|
||||||
<TextBox Text="{x:Bind ViewModel.ProjectName, Mode=TwoWay}" />
|
|
||||||
</editor:PropertyField>
|
|
||||||
<editor:PropertyField Label="Location">
|
|
||||||
<Grid>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<TextBox
|
|
||||||
Grid.Column="0"
|
|
||||||
IsReadOnly="True"
|
|
||||||
Text="{x:Bind ViewModel.ProjectLocation, Mode=TwoWay}" />
|
|
||||||
<Button
|
|
||||||
Grid.Column="1"
|
|
||||||
Margin="4,0,0,0"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Command="{x:Bind ViewModel.SelectionProjectLocationCommand}">
|
|
||||||
<FontIcon FontSize="16" Glyph="" />
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
</editor:PropertyField>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<Grid Grid.Row="2">
|
|
||||||
<Button
|
|
||||||
Width="150"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Command="{x:Bind ViewModel.CreateProjectCommand}"
|
|
||||||
Content="Create"
|
|
||||||
Style="{ThemeResource AccentButtonStyle}" />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
</Page>
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using Ghost.Editor.ViewModels.Pages.Landing;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
using Microsoft.UI.Xaml.Navigation;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.View.Pages.Landing;
|
|
||||||
|
|
||||||
internal sealed partial class CreateProjectPage : Page
|
|
||||||
{
|
|
||||||
public CreateProjectViewModel ViewModel
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CreateProjectPage()
|
|
||||||
{
|
|
||||||
ViewModel = App.GetService<CreateProjectViewModel>();
|
|
||||||
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnNavigatedTo(e);
|
|
||||||
ViewModel.OnNavigatedTo(e.Parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnNavigatedFrom(e);
|
|
||||||
ViewModel.OnNavigatedFrom();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<Page
|
|
||||||
x:Class="Ghost.Editor.View.Pages.Landing.OpenProjectPage"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:converters="using:Ghost.Editor.Utilities.Converters"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:data="using:Ghost.Data.Models"
|
|
||||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
|
||||||
xmlns:local="using:Ghost.Editor.View.Pages.Landing"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
NavigationCacheMode="Enabled"
|
|
||||||
mc:Ignorable="d">
|
|
||||||
|
|
||||||
<Page.Resources>
|
|
||||||
<converters:GetDirectoryNameConverter x:Key="DirNameConverter" />
|
|
||||||
</Page.Resources>
|
|
||||||
|
|
||||||
<Grid x:Name="MainContainer">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<Grid Grid.Row="0" Margin="16,4">
|
|
||||||
<TextBlock
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
|
||||||
Text="Projects" />
|
|
||||||
<AutoSuggestBox
|
|
||||||
Width="300"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
PlaceholderText="Search project by name"
|
|
||||||
QueryIcon="Find" />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Header for the ListView -->
|
|
||||||
<Grid Grid.Row="1" Margin="28,16,45,8">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
<ColumnDefinition Width="200" />
|
|
||||||
<ColumnDefinition Width="165" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
Grid.Column="0"
|
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
|
||||||
Text="NAME" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Column="1"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
|
||||||
Text="LAST OPEN" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Column="2"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
|
||||||
Text="ENGINE VERSION" />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Project ListView -->
|
|
||||||
<Grid
|
|
||||||
Grid.Row="2"
|
|
||||||
Padding="8"
|
|
||||||
AllowDrop="True"
|
|
||||||
DragEnter="ProjectContainer_DragEnter"
|
|
||||||
DragLeave="ProjectContainer_DragLeave"
|
|
||||||
DragOver="ProjectContainer_DragOver"
|
|
||||||
Drop="ProjectContainer_Drop">
|
|
||||||
<ListView
|
|
||||||
Padding="4,8"
|
|
||||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
|
||||||
CornerRadius="{StaticResource OverlayCornerRadius}"
|
|
||||||
IsItemClickEnabled="True"
|
|
||||||
ItemClick="ListView_ItemClick"
|
|
||||||
ItemsSource="{x:Bind ViewModel.projects}"
|
|
||||||
SelectionMode="None">
|
|
||||||
<ListView.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="data:ProjectMetadataInfo">
|
|
||||||
<Grid Height="64" Padding="4,8">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
<ColumnDefinition Width="200" />
|
|
||||||
<ColumnDefinition Width="100" />
|
|
||||||
<ColumnDefinition Width="65" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<Grid Grid.Column="0" VerticalAlignment="Center">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="0"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
FontSize="16"
|
|
||||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
|
||||||
Text="{x:Bind Metadata.Name}" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="1"
|
|
||||||
Margin="0,4,0,0"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
|
||||||
Text="{x:Bind Path, Converter={StaticResource DirNameConverter}}" />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
Grid.Column="1"
|
|
||||||
Margin="16,4"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{x:Bind Metadata.LastOpened}" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Column="2"
|
|
||||||
Margin="16,4"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{x:Bind Metadata.EngineVersion}" />
|
|
||||||
<Button
|
|
||||||
Grid.Column="3"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Background="Transparent"
|
|
||||||
BorderThickness="0">
|
|
||||||
<FontIcon Glyph="" />
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
|
||||||
</ListView.ItemTemplate>
|
|
||||||
</ListView>
|
|
||||||
|
|
||||||
<!-- Drag Visual -->
|
|
||||||
<Grid
|
|
||||||
x:Name="DragVisual"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Background="{ThemeResource CardStrokeColorDefaultBrush}"
|
|
||||||
BorderBrush="{ThemeResource ControlStrongStrokeColorDefaultBrush}"
|
|
||||||
BorderThickness="2"
|
|
||||||
CornerRadius="{StaticResource OverlayCornerRadius}"
|
|
||||||
Visibility="{x:Bind ViewModel.DragVisibility, Mode=OneWay}">
|
|
||||||
<TextBlock
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
|
||||||
Style="{StaticResource TitleTextBlockStyle}"
|
|
||||||
Text="Drage Project Folder Here" />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Empty Place Holder -->
|
|
||||||
<Grid
|
|
||||||
x:Name="EmptyPlaceHolder"
|
|
||||||
Grid.Row="2"
|
|
||||||
Visibility="{x:Bind ViewModel.EmptyVisibility, Mode=OneWay}">
|
|
||||||
<TextBlock
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{StaticResource TitleTextBlockStyle}"
|
|
||||||
Text="No projects found" />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Page>
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
using Ghost.Data.Models;
|
|
||||||
using Ghost.Editor.ViewModels.Pages.Landing;
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
using Microsoft.UI.Xaml.Navigation;
|
|
||||||
using Windows.ApplicationModel.DataTransfer;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.View.Pages.Landing;
|
|
||||||
|
|
||||||
internal sealed partial class OpenProjectPage : Page
|
|
||||||
{
|
|
||||||
public OpenProjectViewModel ViewModel
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OpenProjectPage()
|
|
||||||
{
|
|
||||||
ViewModel = App.GetService<OpenProjectViewModel>();
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnNavigatedTo(e);
|
|
||||||
ViewModel.OnNavigatedTo(e.Parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnNavigatedFrom(e);
|
|
||||||
ViewModel.OnNavigatedFrom();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProjectContainer_DragEnter(object sender, DragEventArgs e)
|
|
||||||
{
|
|
||||||
ViewModel.DragVisibility = Visibility.Visible;
|
|
||||||
ViewModel.EmptyVisibility = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProjectContainer_DragLeave(object sender, DragEventArgs e)
|
|
||||||
{
|
|
||||||
ViewModel.DragVisibility = Visibility.Collapsed;
|
|
||||||
ViewModel.UpdateEmptyPlaceHolderVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProjectContainer_DragOver(object sender, DragEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.DataView.Contains(StandardDataFormats.StorageItems))
|
|
||||||
{
|
|
||||||
e.AcceptedOperation = DataPackageOperation.Link;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
e.AcceptedOperation = DataPackageOperation.None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void ProjectContainer_Drop(object sender, DragEventArgs e)
|
|
||||||
{
|
|
||||||
await ViewModel.ContentDrop(e.DataView);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void ListView_ItemClick(object sender, ItemClickEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.ClickedItem is ProjectMetadataInfo project)
|
|
||||||
{
|
|
||||||
await Task.Yield();
|
|
||||||
await ViewModel.OpenProjectAsync(project);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors"
|
xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors"
|
||||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||||
|
xmlns:controls1="using:Ghost.Editor.Controls"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:ee="using:Ghost.Editor.View.Pages.EngineEditor"
|
xmlns:ee="using:Ghost.Editor.View.Pages.EngineEditor"
|
||||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||||
@@ -12,15 +13,13 @@
|
|||||||
xmlns:local="using:Ghost.Editor.View.Windows"
|
xmlns:local="using:Ghost.Editor.View.Windows"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:winex="using:WinUIEx"
|
xmlns:winex="using:WinUIEx"
|
||||||
Activated="WindowEx_Activated"
|
|
||||||
Closed="WindowEx_Closed"
|
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Window.SystemBackdrop>
|
<Window.SystemBackdrop>
|
||||||
<MicaBackdrop />
|
<MicaBackdrop />
|
||||||
</Window.SystemBackdrop>
|
</Window.SystemBackdrop>
|
||||||
|
|
||||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
<Grid Loaded="MainGrid_Loaded" Unloaded="MainGrid_Unloaded">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
@@ -29,29 +28,21 @@
|
|||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- Titlebar -->
|
<!-- Titlebar -->
|
||||||
<StackPanel
|
<TitleBar
|
||||||
|
x:Name="PART_TitleBar"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Padding="8"
|
Background="{ThemeResource AcrylicBackgroundFillColorBaseBrush}"
|
||||||
Orientation="Horizontal">
|
Subtitle="Ghost Engine">
|
||||||
<ImageIcon
|
<TitleBar.IconSource>
|
||||||
Width="24"
|
<ImageIconSource ImageSource="ms-appx:///Assets/icon.targetsize-48.png" />
|
||||||
Height="24"
|
</TitleBar.IconSource>
|
||||||
VerticalAlignment="Center"
|
</TitleBar>
|
||||||
Source="ms-appx:///Assets/Icon.targetsize-32.png" />
|
|
||||||
<TextBlock
|
|
||||||
Margin="8,0,0,0"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
|
||||||
Text="{x:Bind ViewModel.engineVersionDescriptor}" />
|
|
||||||
<TextBlock
|
|
||||||
Margin="8,0,0,0"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
|
||||||
Text="{x:Bind ViewModel.CurrentProject.Metadata.Name, Mode=OneWay}" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Toolbar -->
|
<!-- Toolbar -->
|
||||||
<Grid Grid.Row="1" Margin="4,4">
|
<Grid
|
||||||
|
Grid.Row="1"
|
||||||
|
Padding="4,0,4,4"
|
||||||
|
Background="{ThemeResource AcrylicBackgroundFillColorBaseBrush}">
|
||||||
<controls:TabbedCommandBar>
|
<controls:TabbedCommandBar>
|
||||||
<controls:TabbedCommandBar.MenuItems>
|
<controls:TabbedCommandBar.MenuItems>
|
||||||
<controls:TabbedCommandBarItem Header="Home">
|
<controls:TabbedCommandBarItem Header="Home">
|
||||||
@@ -132,7 +123,7 @@
|
|||||||
<TabViewItem.IconSource>
|
<TabViewItem.IconSource>
|
||||||
<FontIconSource Glyph="" />
|
<FontIconSource Glyph="" />
|
||||||
</TabViewItem.IconSource>
|
</TabViewItem.IconSource>
|
||||||
<ee:ProjectPage />
|
<controls1:ProjectBrowser />
|
||||||
</TabViewItem>
|
</TabViewItem>
|
||||||
<TabViewItem Header="Console">
|
<TabViewItem Header="Console">
|
||||||
<TabViewItem.IconSource>
|
<TabViewItem.IconSource>
|
||||||
@@ -148,7 +139,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
Grid.Row="3"
|
Grid.Row="3"
|
||||||
Height="25"
|
Height="25"
|
||||||
Background="{ThemeResource SolidBackgroundFillColorBaseAltBrush}">
|
Background="{ThemeResource SmokeFillColorDefaultBrush}">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
@@ -201,6 +192,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<!-- Info and Progress -->
|
||||||
<Grid Grid.Row="0" Grid.RowSpan="4">
|
<Grid Grid.Row="0" Grid.RowSpan="4">
|
||||||
<InfoBar
|
<InfoBar
|
||||||
x:Name="InfoBar"
|
x:Name="InfoBar"
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
using Ghost.Data.Resources;
|
using Ghost.Editor.Core;
|
||||||
using Ghost.Editor.Core.Notifications;
|
using Ghost.Editor.Core.Contracts;
|
||||||
using Ghost.Editor.Core.Progress;
|
using Ghost.Editor.Core.Services;
|
||||||
using Ghost.Editor.ViewModels.Windows;
|
using Ghost.Editor.ViewModels.Windows;
|
||||||
using Ghost.Engine.Resources;
|
using System.Diagnostics;
|
||||||
|
using Windows.ApplicationModel;
|
||||||
using WinUIEx;
|
using WinUIEx;
|
||||||
|
|
||||||
// To learn more about WinUI, the WinUI project structure,
|
|
||||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
|
||||||
|
|
||||||
namespace Ghost.Editor.View.Windows;
|
namespace Ghost.Editor.View.Windows;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An empty window that can be used on its own or navigated to within a Frame.
|
/// An empty window that can be used on its own or navigated to within a Frame.
|
||||||
@@ -29,24 +27,26 @@ internal sealed partial class EngineEditorWindow : WindowEx
|
|||||||
_notificationService = (NotificationService)App.GetService<INotificationService>();
|
_notificationService = (NotificationService)App.GetService<INotificationService>();
|
||||||
_progressService = (ProgressService)App.GetService<IProgressService>();
|
_progressService = (ProgressService)App.GetService<IProgressService>();
|
||||||
|
|
||||||
AppWindow.SetIcon(AssetsPath.s_appIconPath);
|
|
||||||
Title = EngineData.ENGINE_NAME;
|
|
||||||
ExtendsContentIntoTitleBar = true;
|
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
AppWindow.SetIcon(Path.Combine(AppContext.BaseDirectory, "Assets/icon.ico"));
|
||||||
|
Title = "Ghost Engine";
|
||||||
|
ExtendsContentIntoTitleBar = true;
|
||||||
|
|
||||||
|
SetTitleBar(PART_TitleBar);
|
||||||
this.CenterOnScreen();
|
this.CenterOnScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WindowEx_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args)
|
private void MainGrid_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Bindings.Update();
|
PART_TitleBar.Title = EditorApplication.CurrentProjectName;
|
||||||
|
PART_TitleBar.Subtitle = $"Ghost Engine {Package.Current.Id.Version.Major}.{Package.Current.Id.Version.Minor}.{Package.Current.Id.Version.Build}";
|
||||||
|
|
||||||
_notificationService.SetReference(InfoBar, NotificationQueue);
|
_notificationService.SetReference(InfoBar, NotificationQueue);
|
||||||
_progressService.SetReference(ProgressBarContainer);
|
_progressService.SetReference(ProgressBarContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
|
private void MainGrid_Unloaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
_notificationService.ClearReference();
|
_notificationService.ClearReference();
|
||||||
_progressService.ClearReference();
|
_progressService.ClearReference();
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<winex:WindowEx
|
|
||||||
x:Class="Ghost.Editor.View.Windows.LandingWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
|
||||||
xmlns:local="using:Ghost.Editor.View.Windows"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:winex="using:WinUIEx"
|
|
||||||
Activated="WindowEx_Activated"
|
|
||||||
Closed="WindowEx_Closed"
|
|
||||||
IsResizable="False"
|
|
||||||
mc:Ignorable="d">
|
|
||||||
|
|
||||||
<Window.SystemBackdrop>
|
|
||||||
<MicaBackdrop />
|
|
||||||
</Window.SystemBackdrop>
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="32" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<Grid Grid.Row="0">
|
|
||||||
<TextBlock
|
|
||||||
Margin="24,0,0,0"
|
|
||||||
VerticalAlignment="Bottom"
|
|
||||||
Style="{StaticResource BodyTextBlockStyle}"
|
|
||||||
Text="Ghost Engine" />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid Grid.Row="1" Padding="24,0,24,18">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<SelectorBar
|
|
||||||
Grid.Row="0"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
SelectionChanged="SelectorBar_SelectionChanged">
|
|
||||||
<SelectorBarItem IsSelected="True" Text="Open">
|
|
||||||
<SelectorBarItem.Icon>
|
|
||||||
<FontIcon Glyph="" />
|
|
||||||
</SelectorBarItem.Icon>
|
|
||||||
</SelectorBarItem>
|
|
||||||
<SelectorBarItem Text="Create">
|
|
||||||
<SelectorBarItem.Icon>
|
|
||||||
<FontIcon Glyph="" />
|
|
||||||
</SelectorBarItem.Icon>
|
|
||||||
</SelectorBarItem>
|
|
||||||
</SelectorBar>
|
|
||||||
|
|
||||||
<Frame
|
|
||||||
x:Name="ContentFrame"
|
|
||||||
Grid.Row="1"
|
|
||||||
Padding="8"
|
|
||||||
CacheMode="BitmapCache"
|
|
||||||
CacheSize="10" />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid Grid.Row="1" Padding="16">
|
|
||||||
<InfoBar
|
|
||||||
x:Name="InfoBar"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
VerticalAlignment="Bottom">
|
|
||||||
<interactivity:Interaction.Behaviors>
|
|
||||||
<behaviors:StackedNotificationsBehavior x:Name="NotificationQueue" />
|
|
||||||
</interactivity:Interaction.Behaviors>
|
|
||||||
</InfoBar>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</winex:WindowEx>
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
using Ghost.Data.Resources;
|
|
||||||
using Ghost.Editor.Core.Notifications;
|
|
||||||
using Ghost.Editor.View.Pages.Landing;
|
|
||||||
using Ghost.Engine.Resources;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
using Microsoft.UI.Xaml.Media.Animation;
|
|
||||||
using WinUIEx;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.View.Windows;
|
|
||||||
|
|
||||||
internal sealed partial class LandingWindow : WindowEx
|
|
||||||
{
|
|
||||||
private readonly NotificationService _notificationService;
|
|
||||||
|
|
||||||
private int _previousSelectedIndex;
|
|
||||||
|
|
||||||
public LandingWindow()
|
|
||||||
{
|
|
||||||
_notificationService = (NotificationService)App.GetService<INotificationService>();
|
|
||||||
|
|
||||||
AppWindow.SetIcon(AssetsPath.s_appIconPath);
|
|
||||||
Title = EngineData.ENGINE_NAME;
|
|
||||||
|
|
||||||
InitializeComponent();
|
|
||||||
|
|
||||||
this.SetWindowSize(1000, 750);
|
|
||||||
this.CenterOnScreen();
|
|
||||||
|
|
||||||
ExtendsContentIntoTitleBar = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WindowEx_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args)
|
|
||||||
{
|
|
||||||
_notificationService.SetReference(InfoBar, NotificationQueue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
|
|
||||||
{
|
|
||||||
_notificationService.ClearReference();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
var selectedItem = sender.SelectedItem;
|
|
||||||
var currentSelectedIndex = sender.Items.IndexOf(selectedItem);
|
|
||||||
var pageType = currentSelectedIndex switch
|
|
||||||
{
|
|
||||||
1 => typeof(CreateProjectPage),
|
|
||||||
_ => typeof(OpenProjectPage),
|
|
||||||
};
|
|
||||||
|
|
||||||
var slideNavigationTransitionEffect = currentSelectedIndex - _previousSelectedIndex > 0 ?
|
|
||||||
SlideNavigationTransitionEffect.FromRight : SlideNavigationTransitionEffect.FromLeft;
|
|
||||||
|
|
||||||
ContentFrame.Navigate(pageType, null, new SlideNavigationTransitionInfo() { Effect = slideNavigationTransitionEffect });
|
|
||||||
|
|
||||||
_previousSelectedIndex = currentSelectedIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
79
Ghost.Editor/View/Windows/SplashWindow.xaml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<winex:WindowEx
|
||||||
|
x:Class="Ghost.Editor.View.Windows.SplashWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="using:Ghost.Editor.View.Windows"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:winex="using:WinUIEx"
|
||||||
|
Title="SplashWindow"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<winex:WindowEx.SystemBackdrop>
|
||||||
|
<MicaBackdrop />
|
||||||
|
</winex:WindowEx.SystemBackdrop>
|
||||||
|
|
||||||
|
<Grid Margin="32,32,32,16" Loaded="MainGrid_Loaded">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Row="0" ColumnSpacing="24">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="1.2*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Column="0" CornerRadius="8">
|
||||||
|
<Image
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Source="C:\Users\Misaki\Downloads\Screenshot 2024-07-20 035047.png"
|
||||||
|
Stretch="UniformToFill" />
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Column="1">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Style="{StaticResource TitleLargeTextBlockStyle}"
|
||||||
|
Text="Ghost Engine" />
|
||||||
|
<TextBlock
|
||||||
|
x:Name="VersionTextBlock"
|
||||||
|
Grid.Row="1"
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
|
Style="{StaticResource BodyLargeStrongTextBlockStyle}" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="2"
|
||||||
|
Margin="0,32,0,0"
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
|
Text="An open-source, modern game engine designed for flexibility and performance."
|
||||||
|
TextWrapping="WrapWholeWords" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<ProgressBar
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="0,24,0,0"
|
||||||
|
IsIndeterminate="True" />
|
||||||
|
<TextBlock
|
||||||
|
x:Name="LoadingTextBlock"
|
||||||
|
Grid.Row="2"
|
||||||
|
Margin="0,4,0,0"
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||||
|
<TextBlock
|
||||||
|
x:Name="CopyrightTextBlock"
|
||||||
|
Grid.Row="3"
|
||||||
|
Margin="0,24,0,0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Foreground="{ThemeResource TextFillColorTertiaryBrush}"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}" />
|
||||||
|
</Grid>
|
||||||
|
</winex:WindowEx>
|
||||||
33
Ghost.Editor/View/Windows/SplashWindow.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using Ghost.Editor.Core;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Windows.ApplicationModel;
|
||||||
|
using Windows.Storage;
|
||||||
|
using WinUIEx;
|
||||||
|
|
||||||
|
namespace Ghost.Editor.View.Windows;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An empty window that can be used on its own or navigated to within a Frame.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed partial class SplashWindow : WindowEx
|
||||||
|
{
|
||||||
|
public SplashWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
IsResizable = false;
|
||||||
|
IsMaximizable = false;
|
||||||
|
IsMinimizable = false;
|
||||||
|
ExtendsContentIntoTitleBar = true;
|
||||||
|
|
||||||
|
//this.CenterOnScreen(750, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MainGrid_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var version = Package.Current.Id.Version;
|
||||||
|
VersionTextBlock.Text = $"Version {version.Major}.{version.Minor}.{version.Build}";
|
||||||
|
LoadingTextBlock.Text = $"Loading {EditorApplication.CurrentProjectName}...";
|
||||||
|
CopyrightTextBlock.Text = $"Copyright © {DateTime.Now.Year} Ghost Engine. All rights reserved.";
|
||||||
|
}
|
||||||
|
}
|
||||||
108
Ghost.Editor/ViewModels/Controls/ProjectBrowserViewModel.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using Ghost.Editor.Core;
|
||||||
|
using Ghost.Editor.Core.AssetHandle;
|
||||||
|
using Ghost.Editor.Core.Contracts;
|
||||||
|
using Ghost.Editor.Core.Utilities;
|
||||||
|
using Ghost.Editor.Models;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace Ghost.Editor.ViewModels.Controls;
|
||||||
|
|
||||||
|
internal partial class ProjectBrowserViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IInspectorService _inspectorService;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, ExplorerItem> _pathToDirectoryItemMap = new();
|
||||||
|
private ExplorerItem? _selectedItem;
|
||||||
|
|
||||||
|
public ObservableCollection<ExplorerItem> Directories
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
} = new();
|
||||||
|
|
||||||
|
public ObservableCollection<ExplorerItem> Files
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
} = new();
|
||||||
|
|
||||||
|
public ExplorerItem? SelectedItem
|
||||||
|
{
|
||||||
|
get => _selectedItem;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
// TODO: Resolve inspector by reading metadata from selected asset
|
||||||
|
_selectedItem = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProjectBrowserViewModel(IInspectorService inspectorService)
|
||||||
|
{
|
||||||
|
_inspectorService = inspectorService;
|
||||||
|
|
||||||
|
var assetsRootItem = new ExplorerItem(EditorApplication.ASSETS_FOLDER_NAME, Path.Combine(EditorApplication.CurrentProjectPath, EditorApplication.ASSETS_FOLDER_NAME), true);
|
||||||
|
LoadSubFolderRecursive(assetsRootItem);
|
||||||
|
|
||||||
|
Directories.Add(assetsRootItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadSubFolderRecursive(ExplorerItem parentItem)
|
||||||
|
{
|
||||||
|
foreach (var directory in Directory.EnumerateDirectories(parentItem.FullName))
|
||||||
|
{
|
||||||
|
var item = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
||||||
|
LoadSubFolderRecursive(item);
|
||||||
|
|
||||||
|
_pathToDirectoryItemMap[directory] = item;
|
||||||
|
|
||||||
|
parentItem.Children ??= new();
|
||||||
|
parentItem.Children.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void NavigateToDirectory(string? path)
|
||||||
|
{
|
||||||
|
Files.Clear();
|
||||||
|
|
||||||
|
if (!Directory.Exists(path))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var directory in Directory.EnumerateDirectories(path))
|
||||||
|
{
|
||||||
|
var directoryItem = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
||||||
|
Files.Add(directoryItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var file in Directory.EnumerateFiles(path))
|
||||||
|
{
|
||||||
|
if (Path.GetExtension(file) == FileExtensions.META_FILE_EXTENSION)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileItem = new ExplorerItem(Path.GetFileName(file), file, false);
|
||||||
|
Files.Add(fileItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal (ExplorerItem?, int) OpenSelected()
|
||||||
|
{
|
||||||
|
if (SelectedItem == null)
|
||||||
|
{
|
||||||
|
return (null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SelectedItem.IsDirectory)
|
||||||
|
{
|
||||||
|
NavigateToDirectory(SelectedItem.FullName);
|
||||||
|
SelectedItem = _pathToDirectoryItemMap[SelectedItem.FullName];
|
||||||
|
return (SelectedItem, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AssetDatabase.OpenAsset(SelectedItem.FullName);
|
||||||
|
return (null, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Ghost.Editor.Core.Contracts;
|
using Ghost.Editor.Core.Contracts;
|
||||||
using Ghost.Editor.Core.Inspector;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||||
|
|
||||||
@@ -16,7 +15,7 @@ internal partial class InspectorViewModel(IInspectorService inspectorService) :
|
|||||||
public void OnNavigatedTo(object? parameter)
|
public void OnNavigatedTo(object? parameter)
|
||||||
{
|
{
|
||||||
inspectorService.OnSelectionChanged += OnSelectionChanged;
|
inspectorService.OnSelectionChanged += OnSelectionChanged;
|
||||||
Inspectable = inspectorService.SelectedInspectable;
|
Inspectable = inspectorService.Selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnNavigatedFrom()
|
public void OnNavigatedFrom()
|
||||||
@@ -25,8 +24,8 @@ internal partial class InspectorViewModel(IInspectorService inspectorService) :
|
|||||||
Inspectable = null;
|
Inspectable = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSelectionChanged()
|
private void OnSelectionChanged(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Inspectable = inspectorService.SelectedInspectable;
|
Inspectable = inspectorService.Selected;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Ghost.Data.Services;
|
using Ghost.Editor.Core;
|
||||||
using Ghost.Editor.Core.AssetHandle;
|
using Ghost.Editor.Core.AssetHandle;
|
||||||
using Ghost.Editor.Models;
|
using Ghost.Editor.Models;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
@@ -36,12 +36,7 @@ internal partial class ProjectViewModel : ObservableObject
|
|||||||
|
|
||||||
public ProjectViewModel()
|
public ProjectViewModel()
|
||||||
{
|
{
|
||||||
if (ProjectService.CurrentProject.Metadata == null)
|
var assetsRootItem = new ExplorerItem("Assets", Path.Combine(EditorApplication.CurrentProjectPath, EditorApplication.ASSETS_FOLDER_NAME), true);
|
||||||
{
|
|
||||||
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);
|
LoadSubFolderRecursive(ref assetsRootItem);
|
||||||
|
|
||||||
SubDirectories.Add(assetsRootItem);
|
SubDirectories.Add(assetsRootItem);
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CommunityToolkit.Mvvm.Input;
|
|
||||||
using Ghost.Data.Models;
|
|
||||||
using Ghost.Data.Services;
|
|
||||||
using Ghost.Editor.Core.AppState;
|
|
||||||
using Ghost.Editor.Core.Contracts;
|
|
||||||
using Ghost.Editor.Core.Notifications;
|
|
||||||
using Ghost.Editor.Utilities;
|
|
||||||
using Ghost.Engine.Resources;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
|
||||||
|
|
||||||
internal partial class CreateProjectViewModel(INotificationService notificationService, ProjectService projectService, AppStateMachine stateService) : ObservableObject, INavigationAware
|
|
||||||
{
|
|
||||||
public ObservableCollection<TemplateData> templates = new();
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
public partial TemplateData? SelectedTemplate
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
public partial string? ProjectName
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
public partial string? ProjectLocation
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void OnNavigatedTo(object? parameter)
|
|
||||||
{
|
|
||||||
templates.Clear();
|
|
||||||
await foreach (var (path, info) in ProjectService.GetProjectTemplatesAsync())
|
|
||||||
{
|
|
||||||
templates.Add(new(path, info));
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectedTemplate = templates.FirstOrDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnNavigatedFrom()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private async Task SelectionProjectLocation()
|
|
||||||
{
|
|
||||||
var folder = await SystemUtilities.OpenFolderPickerAsync();
|
|
||||||
if (folder != null)
|
|
||||||
{
|
|
||||||
ProjectLocation = folder.Path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private async Task CreateProject()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(ProjectName)
|
|
||||||
|| !Directory.Exists(ProjectLocation)
|
|
||||||
|| !SelectedTemplate.HasValue)
|
|
||||||
{
|
|
||||||
notificationService.ShowNotification("Incorrect project info", MessageType.Error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await projectService.CreateProjectAsync(ProjectName, ProjectLocation, EngineData.EngineVersion, SelectedTemplate.Value.directory);
|
|
||||||
if (result.IsFailure)
|
|
||||||
{
|
|
||||||
notificationService.ShowNotification(result.Message, MessageType.Error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await stateService.TransitionToAsync(StateKey.EngineEditor, result.Value);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
notificationService.ShowNotification($"Failed to load project: {e.Message}", MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using Ghost.Data.Models;
|
|
||||||
using Ghost.Data.Services;
|
|
||||||
using Ghost.Editor.Core.AppState;
|
|
||||||
using Ghost.Editor.Core.Contracts;
|
|
||||||
using Ghost.Editor.Core.Notifications;
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using Windows.ApplicationModel.DataTransfer;
|
|
||||||
using Windows.Storage;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
|
||||||
|
|
||||||
internal partial class OpenProjectViewModel(ProjectService projectService, INotificationService _notificationService, AppStateMachine _stateService) : ObservableObject, INavigationAware
|
|
||||||
{
|
|
||||||
public readonly ObservableCollection<ProjectMetadataInfo> projects = new();
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
public partial Visibility EmptyVisibility
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
public partial Visibility DragVisibility
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateEmptyPlaceHolderVisibility()
|
|
||||||
{
|
|
||||||
EmptyVisibility = projects.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void OnNavigatedTo(object? parameter)
|
|
||||||
{
|
|
||||||
await foreach (var projectInfo in projectService.GetAllProjectAsync())
|
|
||||||
{
|
|
||||||
var metadata = await ProjectService.LoadMetadataAsync(projectInfo.MetadataPath);
|
|
||||||
if (metadata == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
projects.Add(new(projectInfo.MetadataPath, metadata));
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateEmptyPlaceHolderVisibility();
|
|
||||||
DragVisibility = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnNavigatedFrom()
|
|
||||||
{
|
|
||||||
projects.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ContentDrop(DataPackageView dataView)
|
|
||||||
{
|
|
||||||
var errorMessage = string.Empty;
|
|
||||||
if (dataView.Contains(StandardDataFormats.StorageItems))
|
|
||||||
{
|
|
||||||
var items = await dataView.GetStorageItemsAsync();
|
|
||||||
var rootFolder = items.OfType<StorageFolder>().FirstOrDefault();
|
|
||||||
if (rootFolder != null)
|
|
||||||
{
|
|
||||||
var result = await projectService.AddProjectFromDirectoryAsync(rootFolder.Path);
|
|
||||||
if (result.IsSuccess)
|
|
||||||
{
|
|
||||||
projects.Add(result.Value);
|
|
||||||
goto CloseDropPanel;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errorMessage = result.Message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errorMessage = "Unsupported data format. Please drop a folder containing a project.";
|
|
||||||
}
|
|
||||||
|
|
||||||
_notificationService.ShowNotification(errorMessage, MessageType.Error);
|
|
||||||
|
|
||||||
CloseDropPanel:
|
|
||||||
DragVisibility = Visibility.Collapsed;
|
|
||||||
UpdateEmptyPlaceHolderVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task OpenProjectAsync(ProjectMetadataInfo project)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
project.Metadata.LastOpened = DateTime.Now;
|
|
||||||
await ProjectService.CreateMetadataFileAsync(project.Path, project.Metadata);
|
|
||||||
|
|
||||||
await _stateService.TransitionToAsync(StateKey.EngineEditor, project);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_notificationService.ShowNotification($"Failed to load project: {e.Message}", MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Ghost.Data.Models;
|
|
||||||
using Ghost.Data.Services;
|
|
||||||
using Ghost.Engine.Resources;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.ViewModels.Windows;
|
namespace Ghost.Editor.ViewModels.Windows;
|
||||||
|
|
||||||
internal partial class EngineEditorViewModel : ObservableRecipient
|
internal partial class EngineEditorViewModel : ObservableRecipient
|
||||||
{
|
{
|
||||||
public string engineVersionDescriptor = $"{EngineData.ENGINE_NAME} - {EngineData.EngineVersion}";
|
|
||||||
|
|
||||||
public ProjectMetadataInfo CurrentProject => ProjectService.CurrentProject;
|
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ using Misaki.HighPerformance.Jobs;
|
|||||||
|
|
||||||
namespace Ghost.Engine;
|
namespace Ghost.Engine;
|
||||||
|
|
||||||
public interface IEngineContext
|
public interface IEngineContext : IDisposable
|
||||||
{
|
{
|
||||||
JobScheduler JobScheduler { get; }
|
JobScheduler JobScheduler { get; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
<Platform Name="x86" />
|
<Platform Name="x86" />
|
||||||
</Configurations>
|
</Configurations>
|
||||||
<Folder Name="/Editor/">
|
<Folder Name="/Editor/">
|
||||||
<Project Path="Ghost.Data/Ghost.Data.csproj" />
|
|
||||||
<Project Path="Ghost.Editor.Core/Ghost.Editor.Core.csproj" />
|
<Project Path="Ghost.Editor.Core/Ghost.Editor.Core.csproj" />
|
||||||
<Project Path="Ghost.Editor/Ghost.Editor.csproj">
|
<Project Path="Ghost.Editor/Ghost.Editor.csproj">
|
||||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||||
|
|||||||