More project base
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
namespace FileTime.App.Core.Command
|
||||
{
|
||||
public enum Commands
|
||||
{
|
||||
None,
|
||||
|
||||
AutoRefresh,
|
||||
ChangeTimelineMode,
|
||||
CloseTab,
|
||||
Compress,
|
||||
Copy,
|
||||
CopyHash,
|
||||
CopyPath,
|
||||
CreateContainer,
|
||||
CreateElement,
|
||||
Cut,
|
||||
Edit,
|
||||
EnterRapidTravel,
|
||||
FindByName,
|
||||
FindByNameRegex,
|
||||
GoToHome,
|
||||
GoToPath,
|
||||
GoToProvider,
|
||||
GoToRoot,
|
||||
GoUp,
|
||||
HardDelete,
|
||||
Mark,
|
||||
MoveCursorDown,
|
||||
MoveCursorDownPage,
|
||||
MoveCursorUp,
|
||||
MoveCursorUpPage,
|
||||
MoveToFirst,
|
||||
MoveToLast,
|
||||
NextTimelineBlock,
|
||||
NextTimelineCommand,
|
||||
Open,
|
||||
OpenInFileBrowser,
|
||||
OpenOrRun,
|
||||
PasteMerge,
|
||||
PasteOverwrite,
|
||||
PasteSkip,
|
||||
PinFavorite,
|
||||
PreviousTimelineBlock,
|
||||
PreviousTimelineCommand,
|
||||
Refresh,
|
||||
Rename,
|
||||
RunCommand,
|
||||
ScanContainerSize,
|
||||
ShowAllShotcut,
|
||||
SoftDelete,
|
||||
SwitchToLastTab,
|
||||
SwitchToTab1,
|
||||
SwitchToTab2,
|
||||
SwitchToTab3,
|
||||
SwitchToTab4,
|
||||
SwitchToTab5,
|
||||
SwitchToTab6,
|
||||
SwitchToTab7,
|
||||
SwitchToTab8,
|
||||
TimelinePause,
|
||||
TimelineRefresh,
|
||||
TimelineStart,
|
||||
ToggleAdvancedIcons,
|
||||
ToggleHidden,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
16
src/AppCommon/FileTime.App.Core.Abstraction/IAppState.cs
Normal file
16
src/AppCommon/FileTime.App.Core.Abstraction/IAppState.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using FileTime.App.Core.ViewModels;
|
||||
|
||||
namespace FileTime.App.Core
|
||||
{
|
||||
public interface IAppState
|
||||
{
|
||||
ObservableCollection<ITabViewModel> Tabs { get; }
|
||||
ITabViewModel? SelectedTab { get; }
|
||||
IObservable<string?> SearchText { get; }
|
||||
|
||||
void AddTab(ITabViewModel tabViewModel);
|
||||
void RemoveTab(ITabViewModel tabViewModel);
|
||||
void SetSearchText(string? searchText);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace FileTime.App.Core.Models.Enums
|
||||
{
|
||||
public enum ItemAttributeType
|
||||
{
|
||||
File,
|
||||
Element,
|
||||
Container,
|
||||
SizeContainer
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace FileTime.App.Core.Models.Enums
|
||||
{
|
||||
public enum ItemViewMode
|
||||
{
|
||||
Default,
|
||||
Alternative,
|
||||
Selected,
|
||||
Marked,
|
||||
MarkedSelected,
|
||||
MarkedAlternative
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace FileTime.App.Core.Models
|
||||
{
|
||||
public class ItemNamePart
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public bool IsSpecial { get; set; }
|
||||
|
||||
public ItemNamePart(string text, bool isSpecial = false)
|
||||
{
|
||||
Text = text;
|
||||
IsSpecial = isSpecial;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using FileTime.App.Core.Models;
|
||||
|
||||
namespace FileTime.App.Core.Services
|
||||
{
|
||||
public interface IItemNameConverterService
|
||||
{
|
||||
List<ItemNamePart> GetDisplayName(string name, string? searchText);
|
||||
string GetFileExtension(string fullName);
|
||||
string GetFileName(string fullName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FileTime.App.Core.ViewModels
|
||||
{
|
||||
public interface IContainerSizeContainerViewModel : IItemViewModel
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FileTime.App.Core.ViewModels
|
||||
{
|
||||
public interface IContainerViewModel : IItemViewModel
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FileTime.App.Core.ViewModels
|
||||
{
|
||||
public interface IElementViewModel : IItemViewModel
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FileTime.App.Core.ViewModels
|
||||
{
|
||||
public interface IFileViewModel : IElementViewModel
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using FileTime.App.Core.Models;
|
||||
using FileTime.App.Core.Models.Enums;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels
|
||||
{
|
||||
public interface IItemViewModel
|
||||
{
|
||||
IItem? Item { get; set; }
|
||||
IObservable<IReadOnlyList<ItemNamePart>>? DisplayName { get; set; }
|
||||
IObservable<bool>? IsSelected { get; set; }
|
||||
IObservable<bool>? IsMarked { get; set; }
|
||||
ItemViewMode ViewMode { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Services;
|
||||
using InitableService;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels
|
||||
{
|
||||
public interface ITabViewModel : IInitable<ITab>
|
||||
{
|
||||
IObservable<IContainer?>? CurrentLocation { get; }
|
||||
IObservable<IItemViewModel?>? CurrentSelectedItem { get; }
|
||||
IObservable<IReadOnlyList<IItemViewModel>>? CurrentItems { get; }
|
||||
IObservable<IReadOnlyList<FullName>> MarkedItems { get; }
|
||||
ITab? Tab { get; }
|
||||
}
|
||||
}
|
||||
49
src/AppCommon/FileTime.App.Core/AppStateBase.cs
Normal file
49
src/AppCommon/FileTime.App.Core/AppStateBase.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using FileTime.App.Core.ViewModels;
|
||||
using MvvmGen;
|
||||
|
||||
namespace FileTime.App.Core
|
||||
{
|
||||
[ViewModel]
|
||||
public abstract partial class AppStateBase : IAppState
|
||||
{
|
||||
private readonly BehaviorSubject<string?> _searchText = new(null);
|
||||
public ObservableCollection<ITabViewModel> Tabs { get; } = new();
|
||||
public IObservable<string?> SearchText { get; private set; }
|
||||
|
||||
[Property]
|
||||
private ITabViewModel? _selectedTab;
|
||||
|
||||
partial void OnInitialize()
|
||||
{
|
||||
SearchText = _searchText.AsObservable();
|
||||
}
|
||||
|
||||
public void AddTab(ITabViewModel tabViewModel)
|
||||
{
|
||||
Tabs.Add(tabViewModel);
|
||||
if (_selectedTab == null)
|
||||
{
|
||||
SelectedTab = Tabs.First();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveTab(ITabViewModel tabViewModel)
|
||||
{
|
||||
if (!Tabs.Contains(tabViewModel)) return;
|
||||
|
||||
Tabs.Remove(tabViewModel);
|
||||
if (_selectedTab == tabViewModel)
|
||||
{
|
||||
SelectedTab = Tabs.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSearchText(string? searchText)
|
||||
{
|
||||
_searchText.OnNext(searchText);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/AppCommon/FileTime.App.Core/FileTime.App.Core.csproj
Normal file
19
src/AppCommon/FileTime.App.Core/FileTime.App.Core.csproj
Normal file
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MvvmGen" Version="1.1.5" />
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,56 @@
|
||||
using FileTime.App.Core.Models;
|
||||
|
||||
namespace FileTime.App.Core.Services
|
||||
{
|
||||
public class ItemNameConverterService : IItemNameConverterService
|
||||
{
|
||||
public List<ItemNamePart> GetDisplayName(string name, string? searchText)
|
||||
{
|
||||
var nameParts = new List<ItemNamePart>();
|
||||
searchText = searchText?.ToLower();
|
||||
|
||||
if (!string.IsNullOrEmpty(searchText))
|
||||
{
|
||||
var nameLeft = name;
|
||||
|
||||
while (nameLeft.ToLower().IndexOf(searchText, StringComparison.Ordinal) is int rapidTextStart && rapidTextStart != -1)
|
||||
{
|
||||
var before = rapidTextStart > 0 ? nameLeft.Substring(0, rapidTextStart) : null;
|
||||
var rapidTravel = nameLeft.Substring(rapidTextStart, searchText.Length);
|
||||
|
||||
nameLeft = nameLeft.Substring(rapidTextStart + searchText.Length);
|
||||
|
||||
if (before != null)
|
||||
{
|
||||
nameParts.Add(new ItemNamePart(before));
|
||||
}
|
||||
|
||||
nameParts.Add(new ItemNamePart(rapidTravel, true));
|
||||
}
|
||||
|
||||
if (nameLeft.Length > 0)
|
||||
{
|
||||
nameParts.Add(new ItemNamePart(nameLeft));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nameParts.Add(new ItemNamePart(name));
|
||||
}
|
||||
return nameParts;
|
||||
}
|
||||
|
||||
public string GetFileName(string fullName)
|
||||
{
|
||||
var parts = fullName.Split('.');
|
||||
var fileName = string.Join('.', parts[..^1]);
|
||||
return string.IsNullOrEmpty(fileName) ? fullName : fileName;
|
||||
}
|
||||
|
||||
public string GetFileExtension(string fullName)
|
||||
{
|
||||
var parts = fullName.Split('.');
|
||||
return parts.Length == 1 || (parts.Length == 2 && string.IsNullOrEmpty(parts[0])) ? "" : parts[^1];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FileTime.App.Core.ViewModels
|
||||
{
|
||||
public class ContainerSizeContainerViewModel : ItemViewModel, IContainerSizeContainerViewModel
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace FileTime.App.Core.ViewModels
|
||||
{
|
||||
public class ContainerViewModel : ItemViewModel, IContainerViewModel
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FileTime.App.Core.ViewModels
|
||||
{
|
||||
public class ElementViewModel : ItemViewModel, IElementViewModel
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FileTime.App.Core.ViewModels
|
||||
{
|
||||
public class FileViewModel : ElementViewModel, IFileViewModel
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
26
src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs
Normal file
26
src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using FileTime.App.Core.Models;
|
||||
using FileTime.App.Core.Models.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using MvvmGen;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels
|
||||
{
|
||||
[ViewModel]
|
||||
public abstract partial class ItemViewModel : IItemViewModel
|
||||
{
|
||||
[Property]
|
||||
private IItem? _item;
|
||||
|
||||
[Property]
|
||||
private IObservable<IReadOnlyList<ItemNamePart>>? _displayName;
|
||||
|
||||
[Property]
|
||||
private IObservable<bool>? _isSelected;
|
||||
|
||||
[Property]
|
||||
private IObservable<bool>? _isMarked;
|
||||
|
||||
[Property]
|
||||
private ItemViewMode _viewMode;
|
||||
}
|
||||
}
|
||||
105
src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs
Normal file
105
src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using FileTime.App.Core.Services;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels
|
||||
{
|
||||
public class TabViewModel : ITabViewModel, IDisposable
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IItemNameConverterService _itemNameConverterService;
|
||||
private readonly IAppState _appState;
|
||||
private readonly BehaviorSubject<IEnumerable<FullName>> _markedItems = new(Enumerable.Empty<FullName>());
|
||||
private readonly List<IDisposable> _disposables = new();
|
||||
private bool disposed;
|
||||
|
||||
public IObservable<IContainer?>? CurrentLocation { get; private set; }
|
||||
public IObservable<IItemViewModel?>? CurrentSelectedItem { get; private set; }
|
||||
public IObservable<IReadOnlyList<IItemViewModel>>? CurrentItems { get; private set; }
|
||||
public IObservable<IReadOnlyList<FullName>> MarkedItems { get; }
|
||||
|
||||
public ITab? Tab { get; private set; }
|
||||
|
||||
public TabViewModel(
|
||||
IServiceProvider serviceProvider,
|
||||
IItemNameConverterService itemNameConverterService,
|
||||
IAppState appState)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_itemNameConverterService = itemNameConverterService;
|
||||
_appState = appState;
|
||||
|
||||
MarkedItems = _markedItems.Select(e => e.ToList()).AsObservable();
|
||||
}
|
||||
|
||||
public void Init(ITab tab)
|
||||
{
|
||||
CurrentLocation = tab.CurrentLocation.AsObservable();
|
||||
CurrentItems = tab.CurrentItems.Select(items => items.Select(MapItemToViewModel).ToList());
|
||||
CurrentSelectedItem = CurrentItems.CombineLatest(
|
||||
tab.CurrentSelectedItem,
|
||||
(currentItems, currentSelectedItemPath) => currentItems.FirstOrDefault(i => i.Item?.FullName == currentSelectedItemPath?.Path));
|
||||
tab.CurrentLocation.Subscribe((_) => _markedItems.OnNext(Enumerable.Empty<FullName>()));
|
||||
|
||||
Tab = tab;
|
||||
}
|
||||
|
||||
private IItemViewModel MapItemToViewModel(IItem item)
|
||||
{
|
||||
if (item is IContainer container)
|
||||
{
|
||||
var containerViewModel = _serviceProvider.GetRequiredService<IContainerViewModel>();
|
||||
InitIItemViewModel(containerViewModel, item);
|
||||
|
||||
return containerViewModel;
|
||||
}
|
||||
else if (item is IElement element)
|
||||
{
|
||||
var elementViewModel = _serviceProvider.GetRequiredService<IElementViewModel>();
|
||||
InitIItemViewModel(elementViewModel, item);
|
||||
|
||||
return elementViewModel;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"{nameof(item)} is not {nameof(IContainer)} neighter {nameof(IElement)}");
|
||||
|
||||
void InitIItemViewModel(IItemViewModel itemViewModel, IItem item)
|
||||
{
|
||||
itemViewModel.Item = item;
|
||||
itemViewModel.DisplayName = _appState.SearchText.Select(s => _itemNameConverterService.GetDisplayName(item.DisplayName, s));
|
||||
itemViewModel.IsMarked = MarkedItems.Select(m => m.Contains(item.FullName));
|
||||
itemViewModel.IsSelected = MarkedItems.Select(m => m.Contains(item.FullName));
|
||||
}
|
||||
}
|
||||
|
||||
~TabViewModel()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed && disposing)
|
||||
{
|
||||
foreach (var disposable in _disposables)
|
||||
{
|
||||
try
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,8 @@
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Library\InitableService\InitableService.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -3,5 +3,6 @@ namespace FileTime.Core.Models
|
||||
public static class Constants
|
||||
{
|
||||
public const char SeparatorChar = '/';
|
||||
public const int MaximumObservableMergeOperations = 4;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,14 @@
|
||||
using FileTime.Core.Models;
|
||||
using InitableService;
|
||||
|
||||
namespace FileTime.Core.Services
|
||||
{
|
||||
public interface ITab
|
||||
public interface ITab : IInitable<IContainer>
|
||||
{
|
||||
|
||||
IObservable<IContainer?> CurrentLocation { get; }
|
||||
IObservable<IAbsolutePath?> CurrentSelectedItem { get; }
|
||||
IObservable<IEnumerable<IItem>> CurrentItems { get; }
|
||||
|
||||
void ChangeLocation(IContainer newLocation);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,7 +1,65 @@
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.Core.Services
|
||||
{
|
||||
public class Tab : ITab
|
||||
{
|
||||
|
||||
private readonly BehaviorSubject<IContainer?> _currentLocation = new(null);
|
||||
private readonly BehaviorSubject<IAbsolutePath?> _currentSelectedItem = new(null);
|
||||
public IObservable<IContainer?> CurrentLocation { get; }
|
||||
public IObservable<IEnumerable<IItem>> CurrentItems { get; }
|
||||
public IObservable<IAbsolutePath?> CurrentSelectedItem { get; }
|
||||
|
||||
public Tab()
|
||||
{
|
||||
CurrentLocation = _currentLocation.AsObservable();
|
||||
CurrentItems = _currentLocation
|
||||
.Select(c =>
|
||||
Observable.FromAsync(async () =>
|
||||
c == null
|
||||
? Enumerable.Empty<IItem>()
|
||||
: await c.Items
|
||||
.ToAsyncEnumerable()
|
||||
.SelectAwait(
|
||||
async i =>
|
||||
{
|
||||
try
|
||||
{
|
||||
//TODO: force create by AbsolutePath name
|
||||
return await i.ContentProvider.GetItemByFullNameAsync(i.Path);
|
||||
}
|
||||
catch { return null!; }
|
||||
}
|
||||
|
||||
)
|
||||
.Where(i => i != null)
|
||||
.ToListAsync()
|
||||
)
|
||||
)
|
||||
.Merge(Constants.MaximumObservableMergeOperations);
|
||||
CurrentSelectedItem = CurrentLocation.Select(GetSelectedItemByLocation).Merge(_currentSelectedItem).Throttle(TimeSpan.FromMilliseconds(500));
|
||||
|
||||
CurrentItems.Subscribe(c =>
|
||||
{
|
||||
;
|
||||
});
|
||||
}
|
||||
|
||||
public void Init(IContainer currentLocation)
|
||||
{
|
||||
_currentLocation.OnNext(currentLocation);
|
||||
}
|
||||
|
||||
private IAbsolutePath? GetSelectedItemByLocation(IContainer? currentLocation)
|
||||
{
|
||||
return currentLocation?.Items[0];
|
||||
}
|
||||
|
||||
public void ChangeLocation(IContainer newLocation)
|
||||
{
|
||||
_currentLocation.OnNext(newLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AppCommon", "AppCommon", "{
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Avalonia", "Avalonia", "{01F231DE-4A65-435F-B4BB-77EE5221890C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.GuiApp", "GuiApp\Avalonia\FileTime.GuiApp\FileTime.GuiApp.csproj", "{C389087E-EB78-4DCD-96AF-F1E2A4DEE0B0}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.GuiApp.App", "GuiApp\Avalonia\FileTime.GuiApp.App\FileTime.GuiApp.App.csproj", "{C389087E-EB78-4DCD-96AF-F1E2A4DEE0B0}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Providers", "Providers", "{2FC40FE1-4446-44AB-BF77-00F94D995FA3}"
|
||||
EndProject
|
||||
@@ -31,6 +31,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.ConsoleUI", "Conso
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.ConsoleUI.App", "ConsoleApp\FileTime.ConsoleUI.App\FileTime.ConsoleUI.App.csproj", "{77ABB06A-43AF-49D6-A802-A389CCB53724}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Library", "Library", "{07CA18AA-B85D-4DEE-BB86-F569F6029853}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InitableService", "Library\InitableService\InitableService.csproj", "{C67E3453-9730-40B5-8D2D-5D4EEBE008E2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.App.Core", "AppCommon\FileTime.App.Core\FileTime.App.Core.csproj", "{FC4AE72E-E302-45A0-BB9D-E87BB3250F0D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.App.Core.Abstraction", "AppCommon\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj", "{3E9CCD7F-86AA-4BD8-A100-0CD71BDB1AA0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.GuiApp", "GuiApp\Avalonia\FileTime.GuiApp\FileTime.GuiApp.csproj", "{26331AB9-6E4D-40DB-8FF0-CB7133F67CA0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.GuiApp.CustomImpl", "GuiApp\Avalonia\FileTime.GuiApp.CustomImpl\FileTime.GuiApp.CustomImpl.csproj", "{4B742649-225F-4C73-B118-1B29FE2A5774}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.Providers.Local.Abstractions", "Providers\FileTime.Providers.Local.Abstractions\FileTime.Providers.Local.Abstractions.csproj", "{1500A537-2116-4111-B216-7632040619B0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -69,6 +83,30 @@ Global
|
||||
{77ABB06A-43AF-49D6-A802-A389CCB53724}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{77ABB06A-43AF-49D6-A802-A389CCB53724}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{77ABB06A-43AF-49D6-A802-A389CCB53724}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C67E3453-9730-40B5-8D2D-5D4EEBE008E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C67E3453-9730-40B5-8D2D-5D4EEBE008E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C67E3453-9730-40B5-8D2D-5D4EEBE008E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C67E3453-9730-40B5-8D2D-5D4EEBE008E2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FC4AE72E-E302-45A0-BB9D-E87BB3250F0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FC4AE72E-E302-45A0-BB9D-E87BB3250F0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FC4AE72E-E302-45A0-BB9D-E87BB3250F0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FC4AE72E-E302-45A0-BB9D-E87BB3250F0D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3E9CCD7F-86AA-4BD8-A100-0CD71BDB1AA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3E9CCD7F-86AA-4BD8-A100-0CD71BDB1AA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3E9CCD7F-86AA-4BD8-A100-0CD71BDB1AA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3E9CCD7F-86AA-4BD8-A100-0CD71BDB1AA0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{26331AB9-6E4D-40DB-8FF0-CB7133F67CA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{26331AB9-6E4D-40DB-8FF0-CB7133F67CA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{26331AB9-6E4D-40DB-8FF0-CB7133F67CA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{26331AB9-6E4D-40DB-8FF0-CB7133F67CA0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4B742649-225F-4C73-B118-1B29FE2A5774}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4B742649-225F-4C73-B118-1B29FE2A5774}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4B742649-225F-4C73-B118-1B29FE2A5774}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4B742649-225F-4C73-B118-1B29FE2A5774}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1500A537-2116-4111-B216-7632040619B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1500A537-2116-4111-B216-7632040619B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1500A537-2116-4111-B216-7632040619B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1500A537-2116-4111-B216-7632040619B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -83,6 +121,12 @@ Global
|
||||
{A55F4A86-03ED-4D25-807A-04619A1D9507} = {A5291117-3001-498B-AC8B-E14F71F72570}
|
||||
{67D4102C-8B92-451C-B4A9-5A82B68A7A5D} = {CAEEAD3C-41EB-405C-ACA9-BA1E4C352549}
|
||||
{77ABB06A-43AF-49D6-A802-A389CCB53724} = {CAEEAD3C-41EB-405C-ACA9-BA1E4C352549}
|
||||
{C67E3453-9730-40B5-8D2D-5D4EEBE008E2} = {07CA18AA-B85D-4DEE-BB86-F569F6029853}
|
||||
{FC4AE72E-E302-45A0-BB9D-E87BB3250F0D} = {A5291117-3001-498B-AC8B-E14F71F72570}
|
||||
{3E9CCD7F-86AA-4BD8-A100-0CD71BDB1AA0} = {A5291117-3001-498B-AC8B-E14F71F72570}
|
||||
{26331AB9-6E4D-40DB-8FF0-CB7133F67CA0} = {01F231DE-4A65-435F-B4BB-77EE5221890C}
|
||||
{4B742649-225F-4C73-B118-1B29FE2A5774} = {01F231DE-4A65-435F-B4BB-77EE5221890C}
|
||||
{1500A537-2116-4111-B216-7632040619B0} = {2FC40FE1-4446-44AB-BF77-00F94D995FA3}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {859FB3DF-C60A-46B1-82E5-90274905D1EF}
|
||||
|
||||
215
src/GuiApp/Avalonia/FileTime.GuiApp.App/App.axaml
Normal file
215
src/GuiApp/Avalonia/FileTime.GuiApp.App/App.axaml
Normal file
@@ -0,0 +1,215 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:FileTime.GuiApp"
|
||||
x:Class="FileTime.GuiApp.App"
|
||||
xmlns:converters="using:FileTime.GuiApp.Converters">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<Color x:Key="AppBackgroundColor">#E7073642</Color>
|
||||
<Color x:Key="ContainerBackgroundColor">#083e4c</Color>
|
||||
<Color x:Key="TransparentContainerBackgroundColor">#D0083e4c</Color>
|
||||
<Color x:Key="BarelyTransparentBackgroundColor">#80083e4c</Color>
|
||||
|
||||
<Color x:Key="ItemBackgroundColor">#00000000</Color>
|
||||
<Color x:Key="AlternativeItemBackgroundColor">#10000000</Color>
|
||||
<Color x:Key="SelectedItemBackgroundColor">#93a1a1</Color>
|
||||
<Color x:Key="MarkedItemBackgroundColor">#00000000</Color>
|
||||
<Color x:Key="MarkedAlternativeItemBackgroundColor">#10000000</Color>
|
||||
<Color x:Key="MarkedSelectedItemBackgroundColor">#b58900</Color>
|
||||
|
||||
<Color x:Key="ForegroundColor">#93a1a1</Color>
|
||||
<Color x:Key="AccentColor">#268bd2</Color>
|
||||
<Color x:Key="AccentComplementColor">#fdf6e3</Color>
|
||||
<Color x:Key="LightForegroundColor">#7793a1a1</Color>
|
||||
<Color x:Key="AlternativeItemForegroundColor">#93a1a1</Color>
|
||||
<Color x:Key="SelectedItemForegroundColor">#073642</Color>
|
||||
<Color x:Key="MarkedItemForegroundColor">#b58900</Color>
|
||||
<Color x:Key="MarkedAlternativeItemForegroundColor">#b58900</Color>
|
||||
<Color x:Key="MarkedSelectedItemForegroundColor">#002b36</Color>
|
||||
|
||||
<Color x:Key="ErrorColor">#dc322f</Color>
|
||||
|
||||
<LinearGradientBrush x:Key="ContentSeparatorBrush">
|
||||
<GradientStop Offset="0" Color="#0093a1a1" />
|
||||
<GradientStop Offset="0.5" Color="#93a1a1" />
|
||||
<GradientStop Offset="1" Color="#0093a1a1" />
|
||||
</LinearGradientBrush>
|
||||
|
||||
<LinearGradientBrush x:Key="ContainerGradientBackgroundBrush">
|
||||
<GradientStop Offset="0" Color="#00b58900" />
|
||||
<GradientStop Offset="0.4" Color="#CCb58900" />
|
||||
<GradientStop Offset="0.6" Color="#CCb58900" />
|
||||
<GradientStop Offset="1" Color="#00b58900" />
|
||||
</LinearGradientBrush>
|
||||
|
||||
|
||||
|
||||
<SolidColorBrush
|
||||
x:Key="AppBackgroundBrush"
|
||||
Color="{DynamicResource AppBackgroundColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="ContainerBackgroundBrush"
|
||||
Color="{DynamicResource ContainerBackgroundColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="TransparentContainerBackgroundBrush"
|
||||
Color="{DynamicResource TransparentContainerBackgroundColor}" />
|
||||
|
||||
<SolidColorBrush
|
||||
x:Key="ItemBackgroundBrush"
|
||||
Color="{DynamicResource ItemBackgroundColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="AlternativeItemBackgroundBrush"
|
||||
Color="{DynamicResource AlternativeItemBackgroundColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="SelectedItemBackgroundBrush"
|
||||
Color="{DynamicResource SelectedItemBackgroundColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="MarkedItemBackgroundBrush"
|
||||
Color="{DynamicResource MarkedItemBackgroundColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="MarkedSelectedItemBackgroundBrush"
|
||||
Color="{DynamicResource MarkedSelectedItemBackgroundColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="MarkedAlternativeItemBackgroundBrush"
|
||||
Color="{DynamicResource MarkedAlternativeItemBackgroundColor}" />
|
||||
|
||||
<SolidColorBrush
|
||||
x:Key="ForegroundBrush"
|
||||
Color="{DynamicResource ForegroundColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="AccentBrush"
|
||||
Color="{DynamicResource AccentColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="AccentComplementBrush"
|
||||
Color="{DynamicResource AccentComplementColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="LightForegroundBrush"
|
||||
Color="{DynamicResource LightForegroundColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="AlternativeItemForegroundBrush"
|
||||
Color="{DynamicResource AlternativeItemForegroundColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="SelectedItemForegroundBrush"
|
||||
Color="{DynamicResource SelectedItemForegroundColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="MarkedItemForegroundBrush"
|
||||
Color="{DynamicResource MarkedItemForegroundColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="MarkedAlternativeItemForegroundBrush"
|
||||
Color="{DynamicResource MarkedAlternativeItemForegroundColor}" />
|
||||
<SolidColorBrush
|
||||
x:Key="MarkedSelectedItemForegroundBrush"
|
||||
Color="{DynamicResource MarkedSelectedItemForegroundColor}" />
|
||||
|
||||
<SolidColorBrush
|
||||
x:Key="ErrorBrush"
|
||||
Color="{DynamicResource ErrorColor}" />
|
||||
|
||||
|
||||
<SolidColorBrush x:Key="SystemControlHighlightListAccentLowBrush" Color="{DynamicResource SelectedItemBackgroundColor}" />
|
||||
|
||||
|
||||
|
||||
<converters:ItemViewModeToBrushConverter
|
||||
x:Key="ItemViewModeToForegroundConverter"
|
||||
DefaultBrush="{StaticResource ForegroundBrush}"
|
||||
AlternativeBrush="{StaticResource AlternativeItemForegroundBrush}"
|
||||
SelectedBrush="{StaticResource SelectedItemForegroundBrush}"
|
||||
MarkedBrush="{StaticResource MarkedItemForegroundBrush}"
|
||||
MarkedAlternativeBrush="{StaticResource MarkedAlternativeItemForegroundBrush}"
|
||||
MarkedSelectedBrush="{StaticResource MarkedSelectedItemForegroundBrush}"/>
|
||||
<converters:ItemViewModeToBrushConverter
|
||||
x:Key="ItemViewModeToBackgroundConverter"
|
||||
DefaultBrush="{StaticResource ItemBackgroundBrush}"
|
||||
AlternativeBrush="{StaticResource AlternativeItemBackgroundBrush}"
|
||||
SelectedBrush="{StaticResource SelectedItemBackgroundBrush}"
|
||||
MarkedBrush="{StaticResource MarkedItemBackgroundBrush}"
|
||||
MarkedAlternativeBrush="{StaticResource MarkedAlternativeItemBackgroundBrush}"
|
||||
MarkedSelectedBrush="{StaticResource MarkedSelectedItemBackgroundBrush}"/>
|
||||
<converters:NamePartShrinkerConverter x:Key="NamePartShrinkerConverter"/>
|
||||
<converters:ItemViewModelIsAttributeTypeConverter x:Key="ItemViewModelIsAttributeTypeConverter"/>
|
||||
<converters:ItemViewModelIsAttributeTypeConverter x:Key="ItemViewModelIsNotAttributeTypeConverter" Invert="true"/>
|
||||
<converters:GetFileExtensionConverter x:Key="GetFileExtensionConverter"/>
|
||||
<converters:FormatSizeConverter x:Key="FormatSizeConverter"/>
|
||||
<converters:DateTimeConverter x:Key="DateTimeConverter"/>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme Mode="Dark"/>
|
||||
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="Foreground" Value="{DynamicResource ForegroundBrush}"/>
|
||||
<Setter Property="FontSize" Value="16"/>
|
||||
</Style>
|
||||
<Style Selector="TextBlock.SmallText">
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
</Style>
|
||||
<Style Selector="TextBlock.ExtraSmallText">
|
||||
<Setter Property="FontSize" Value="11"/>
|
||||
</Style>
|
||||
<Style Selector="TextBox">
|
||||
<Setter Property="Foreground" Value="{DynamicResource ForegroundBrush}"/>
|
||||
<Setter Property="Background" Value="{DynamicResource ContainerBackgroundBrush}"/>
|
||||
</Style>
|
||||
<Style Selector="ListBox.ContentListView">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
</Style>
|
||||
<Style Selector="ListBox.ContentListView > ListBoxItem">
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<!--Setter Property="ContextMenu">
|
||||
<ContextMenu Items="{Binding Converter={StaticResource ContextMenuGenerator}}"/>
|
||||
</Setter-->
|
||||
</Style>
|
||||
|
||||
<Style Selector="Grid.SidebarContainerPresenter">
|
||||
<Setter Property="Background" Value="#01000000"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Grid.SidebarContainerPresenter:pointerover">
|
||||
<Setter Property="Background" Value="{DynamicResource AppBackgroundColor}"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Border.SelectedTimelineCommand">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ForegroundBrush}"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="ListBox.RadioButtonListBox">
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
</Style>
|
||||
<Style Selector="ListBox.RadioButtonListBox ListBoxItem">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Border>
|
||||
<RadioButton
|
||||
Content="{TemplateBinding ContentPresenter.Content}"
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
|
||||
Foreground="{DynamicResource ForegroundBrush}"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Image.LoadingAnimation">
|
||||
<Style.Animations>
|
||||
<Animation Duration="0:0:2" IterationCount="INFINITE" Easing="QuadraticEaseInOut">
|
||||
<KeyFrame Cue="0%">
|
||||
<Setter Property="RotateTransform.Angle" Value="45"/>
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="100%">
|
||||
<Setter Property="RotateTransform.Angle" Value="405"/>
|
||||
</KeyFrame>
|
||||
</Animation>
|
||||
</Style.Animations>
|
||||
</Style>
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
@@ -1,13 +1,29 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using FileTime.App.DependencyInjection;
|
||||
using FileTime.GuiApp.ViewModels;
|
||||
using FileTime.GuiApp.Views;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FileTime.GuiApp
|
||||
{
|
||||
public partial class App : Application
|
||||
{
|
||||
static App()
|
||||
{
|
||||
DI.ServiceProvider ??= DependencyInjection
|
||||
.RegisterDefaultServices()
|
||||
.AddConfiguration()
|
||||
.RegisterLogging()
|
||||
.AddViewModels()
|
||||
.BuildServiceProvider()
|
||||
.InitSerilog();
|
||||
|
||||
var logger = DI.ServiceProvider.GetRequiredService<ILogger<App>>();
|
||||
logger.LogInformation("App initialization completed.");
|
||||
}
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
@@ -19,7 +35,7 @@ namespace FileTime.GuiApp
|
||||
{
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = new MainWindowViewModel(),
|
||||
DataContext = new MainWindowLoadingViewModel(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<!--Avalonia doesen't support TrimMode=link currently,but we are working on that https://github.com/AvaloniaUI/Avalonia/issues/6892 -->
|
||||
<TrimMode>copyused</TrimMode>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<ApplicationIcon>filetime.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="filetime.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<!--This helps with theme dll-s trimming.
|
||||
If you will publish your application in self-contained mode with p:PublishTrimmed=true and it will use Fluent theme Default theme will be trimmed from the output and vice versa.
|
||||
https://github.com/AvaloniaUI/Avalonia/issues/5593 -->
|
||||
<TrimmableAssembly Include="Avalonia.Themes.Fluent" />
|
||||
<TrimmableAssembly Include="Avalonia.Themes.Default" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.13" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.13" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.13" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.13" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.3.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="XamlNameReferenceGenerator" Version="1.3.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.Core\FileTime.App.Core.csproj" />
|
||||
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj" />
|
||||
<ProjectReference Include="..\FileTime.GuiApp.CustomImpl\FileTime.GuiApp.CustomImpl.csproj" />
|
||||
<ProjectReference Include="..\FileTime.GuiApp\FileTime.GuiApp.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
|
||||
<Content Include="appsettings.Development.json" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
62
src/GuiApp/Avalonia/FileTime.GuiApp.App/Program.cs
Normal file
62
src/GuiApp/Avalonia/FileTime.GuiApp.App/Program.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Avalonia;
|
||||
using FileTime.GuiApp;
|
||||
|
||||
namespace FileTime.GuiApp
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static string AppDataRoot { get; }
|
||||
public static string EnvironmentName { get; }
|
||||
|
||||
static Program()
|
||||
{
|
||||
|
||||
#if DEBUG
|
||||
EnvironmentName = "Development";
|
||||
|
||||
AppDataRoot = Path.Combine(Environment.CurrentDirectory, "appdata");
|
||||
#else
|
||||
EnvironmentName = "Release";
|
||||
|
||||
var possibleDataRootsPaths = new List<string>()
|
||||
{
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FileTime"),
|
||||
Path.Combine(Assembly.GetEntryAssembly()?.Location ?? ".", "fallbackDataRoot")
|
||||
};
|
||||
|
||||
string? appDataRoot = null;
|
||||
foreach (var possibleAppDataRoot in possibleDataRootsPaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
var appDataRootDirectory = new DirectoryInfo(possibleAppDataRoot);
|
||||
if (!appDataRootDirectory.Exists) appDataRootDirectory.Create();
|
||||
|
||||
//TODO write test
|
||||
appDataRoot = possibleAppDataRoot;
|
||||
break;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (appDataRoot == null) throw new UnauthorizedAccessException();
|
||||
AppDataRoot = appDataRoot;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
[STAThread]
|
||||
public static void Main(string[] args) => BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
=> AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
.LogToTrace();
|
||||
}
|
||||
}
|
||||
77
src/GuiApp/Avalonia/FileTime.GuiApp.App/Startup.cs
Normal file
77
src/GuiApp/Avalonia/FileTime.GuiApp.App/Startup.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using FileTime.App.Core;
|
||||
using FileTime.App.Core.Services;
|
||||
using FileTime.App.Core.ViewModels;
|
||||
using FileTime.Core.Services;
|
||||
using FileTime.GuiApp.Configuration;
|
||||
using FileTime.GuiApp.Logging;
|
||||
using FileTime.GuiApp.ViewModels;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog;
|
||||
using Serilog.Configuration;
|
||||
|
||||
namespace FileTime.GuiApp
|
||||
{
|
||||
public static class Startup
|
||||
{
|
||||
internal static IServiceCollection AddViewModels(this IServiceCollection serviceCollection)
|
||||
{
|
||||
return serviceCollection
|
||||
.AddSingleton<MainWindowViewModel>()
|
||||
.AddSingleton<AppState>()
|
||||
.AddSingleton<IAppState, AppState>(s => s.GetRequiredService<AppState>())
|
||||
//TODO: move??
|
||||
.AddTransient<ITab, Tab>()
|
||||
.AddTransient<ITabViewModel, TabViewModel>()
|
||||
.AddTransient<IContainerViewModel, ContainerViewModel>()
|
||||
.AddTransient<IElementViewModel, ElementViewModel>()
|
||||
.AddTransient<IItemNameConverterService, ItemNameConverterService>();
|
||||
}
|
||||
|
||||
internal static IServiceCollection RegisterLogging(this IServiceCollection serviceCollection)
|
||||
{
|
||||
return serviceCollection.AddLogging(loggingBuilder =>
|
||||
loggingBuilder.AddSerilog(dispose: true)
|
||||
);
|
||||
}
|
||||
|
||||
internal static IServiceCollection AddConfiguration(this IServiceCollection serviceCollection)
|
||||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(MainConfiguration.Configuration)
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{Program.EnvironmentName}.json", true, true)
|
||||
.Build();
|
||||
|
||||
return serviceCollection
|
||||
.Configure<ProgramsConfiguration>(configuration.GetSection(SectionNames.ProgramsSectionName))
|
||||
.Configure<KeyBindingConfiguration>(configuration.GetSection(SectionNames.KeybindingSectionName))
|
||||
.AddSingleton<IConfiguration>(configuration);
|
||||
}
|
||||
|
||||
internal static IServiceProvider InitSerilog(this IServiceProvider serviceProvider)
|
||||
{
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.ReadFrom.Configuration(serviceProvider.GetService<IConfiguration>())
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.File(
|
||||
Path.Combine(Program.AppDataRoot, "logs", "appLog.log"),
|
||||
fileSizeLimitBytes: 10 * 1024 * 1024,
|
||||
rollOnFileSizeLimit: true,
|
||||
rollingInterval: RollingInterval.Day)
|
||||
.WriteTo.MessageBoxSink(serviceProvider)
|
||||
.CreateLogger();
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
|
||||
internal static LoggerConfiguration MessageBoxSink(
|
||||
this LoggerSinkConfiguration loggerConfiguration,
|
||||
IServiceProvider serviceProvider)
|
||||
{
|
||||
return loggerConfiguration.Sink(serviceProvider.GetService<ToastMessageSink>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"Serilog": {
|
||||
"MinimumLevel": {
|
||||
"Default": "Debug",
|
||||
"Override": {
|
||||
"Microsoft": "Information",
|
||||
"System": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
src/GuiApp/Avalonia/FileTime.GuiApp.App/filetime.ico
Normal file
BIN
src/GuiApp/Avalonia/FileTime.GuiApp.App/filetime.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.Core\FileTime.App.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,6 @@
|
||||
using FileTime.App.Core;
|
||||
|
||||
namespace FileTime.GuiApp.ViewModels
|
||||
{
|
||||
public class AppState : AppStateBase { }
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:FileTime.GuiApp"
|
||||
x:Class="FileTime.GuiApp.App">
|
||||
<Application.DataTemplates>
|
||||
</Application.DataTemplates>
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme Mode="Light"/>
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Avalonia.Input;
|
||||
using FileTime.App.Core.Command;
|
||||
|
||||
namespace FileTime.GuiApp.Configuration
|
||||
{
|
||||
public class CommandBindingConfiguration
|
||||
{
|
||||
public List<KeyConfig> Keys { get; set; } = new List<KeyConfig>();
|
||||
|
||||
public Commands Command { get; set; } = Commands.None;
|
||||
|
||||
public string KeysDisplayText => GetKeysDisplayText();
|
||||
|
||||
public CommandBindingConfiguration() { }
|
||||
|
||||
public CommandBindingConfiguration(Commands command, IEnumerable<KeyConfig> keys)
|
||||
{
|
||||
Keys = new List<KeyConfig>(keys);
|
||||
Command = command;
|
||||
}
|
||||
|
||||
public CommandBindingConfiguration(Commands command, KeyConfig key)
|
||||
{
|
||||
Keys = new List<KeyConfig>() { key };
|
||||
Command = command;
|
||||
}
|
||||
|
||||
public CommandBindingConfiguration(Commands command, IEnumerable<Key> keys)
|
||||
{
|
||||
Keys = keys.Select(k => new KeyConfig(k)).ToList();
|
||||
Command = command;
|
||||
}
|
||||
|
||||
public CommandBindingConfiguration(Commands command, Key key)
|
||||
{
|
||||
Keys = new List<KeyConfig>() { new KeyConfig(key) };
|
||||
Command = command;
|
||||
}
|
||||
|
||||
public string GetKeysDisplayText()
|
||||
{
|
||||
var s = "";
|
||||
|
||||
foreach (var k in Keys)
|
||||
{
|
||||
var keyString = k.Key.ToString();
|
||||
|
||||
if (keyString.Length == 1)
|
||||
{
|
||||
s += AddKeyWithCtrlOrAlt(k, s, (_, _, _) => k.Shift ? keyString.ToUpper() : keyString.ToLower());
|
||||
}
|
||||
else
|
||||
{
|
||||
s += AddKeyWithCtrlOrAlt(k, s, AddSpecialKey);
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private static string AddKeyWithCtrlOrAlt(KeyConfig key, string currentText, Func<KeyConfig, string, bool, string> keyProcessor)
|
||||
{
|
||||
var s = "";
|
||||
|
||||
bool ctrlOrAlt = key.Ctrl || key.Alt;
|
||||
|
||||
if (ctrlOrAlt && currentText.Length > 0 && currentText.Last() != ' ') s += " ";
|
||||
|
||||
if (key.Ctrl) s += "CTRL+";
|
||||
if (key.Alt) s += "ALT+";
|
||||
s += keyProcessor(key, currentText, ctrlOrAlt);
|
||||
|
||||
if (ctrlOrAlt) s += " ";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private static string AddSpecialKey(KeyConfig key, string currentText, bool wasCtrlOrAlt)
|
||||
{
|
||||
var s = "";
|
||||
|
||||
if (currentText.Length > 0 && currentText.Last() != ' ' && !wasCtrlOrAlt) s += " ";
|
||||
s += key.Key.ToString();
|
||||
if (!wasCtrlOrAlt) s += " ";
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FileTime.GuiApp.Configuration
|
||||
{
|
||||
public class KeyBindingConfiguration
|
||||
{
|
||||
public bool UseDefaultBindings { get; set; } = true;
|
||||
public List<CommandBindingConfiguration> DefaultKeyBindings { get; set; } = new();
|
||||
public List<CommandBindingConfiguration> KeyBindings { get; set; } = new();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Avalonia.Input;
|
||||
|
||||
namespace FileTime.GuiApp.Configuration
|
||||
{
|
||||
public class KeyConfig
|
||||
{
|
||||
public Key Key { get; set; }
|
||||
public bool Shift { get; set; }
|
||||
public bool Alt { get; set; }
|
||||
public bool Ctrl { get; set; }
|
||||
|
||||
public KeyConfig() { }
|
||||
|
||||
public KeyConfig(Key key, bool shift = false, bool alt = false, bool ctrl = false)
|
||||
{
|
||||
Key = key;
|
||||
Shift = shift;
|
||||
Alt = alt;
|
||||
Ctrl = ctrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
using Avalonia.Input;
|
||||
using FileTime.App.Core.Command;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FileTime.GuiApp.Configuration
|
||||
{
|
||||
public static class MainConfiguration
|
||||
{
|
||||
private static readonly Lazy<List<CommandBindingConfiguration>> _defaultKeybindings = new(InitDefaultKeyBindings);
|
||||
|
||||
public static Dictionary<string, string> Configuration { get; }
|
||||
|
||||
static MainConfiguration()
|
||||
{
|
||||
Configuration = new();
|
||||
PopulateDefaultEditorPrograms(Configuration);
|
||||
PopulateDefaultKeyBindings(Configuration, _defaultKeybindings.Value, SectionNames.KeybindingSectionName + ":" + nameof(KeyBindingConfiguration.DefaultKeyBindings));
|
||||
}
|
||||
|
||||
private static void PopulateDefaultKeyBindings(Dictionary<string, string> configuration, List<CommandBindingConfiguration> commandBindingConfigs, string basePath)
|
||||
{
|
||||
for (var i = 0; i < commandBindingConfigs.Count; i++)
|
||||
{
|
||||
var baseKey = basePath + $":[{i}]:";
|
||||
var commandBindingConfig = commandBindingConfigs[i];
|
||||
configuration.Add(baseKey + nameof(CommandBindingConfiguration.Command), commandBindingConfig.Command.ToString());
|
||||
|
||||
for (var j = 0; j < commandBindingConfig.Keys.Count; j++)
|
||||
{
|
||||
var key = commandBindingConfig.Keys[j];
|
||||
var keyBaseKey = baseKey + $"keys:[{j}]:";
|
||||
configuration.Add(keyBaseKey + nameof(KeyConfig.Key), key.Key.ToString());
|
||||
configuration.Add(keyBaseKey + nameof(KeyConfig.Shift), key.Shift.ToString());
|
||||
configuration.Add(keyBaseKey + nameof(KeyConfig.Alt), key.Alt.ToString());
|
||||
configuration.Add(keyBaseKey + nameof(KeyConfig.Ctrl), key.Ctrl.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<CommandBindingConfiguration> InitDefaultKeyBindings()
|
||||
{
|
||||
return new List<CommandBindingConfiguration>()
|
||||
{
|
||||
new CommandBindingConfiguration(Commands.AutoRefresh, new KeyConfig(Key.R, shift: true)),
|
||||
new CommandBindingConfiguration(Commands.ChangeTimelineMode, new[] { Key.T, Key.M }),
|
||||
new CommandBindingConfiguration(Commands.CloseTab, Key.Q),
|
||||
new CommandBindingConfiguration(Commands.Compress, new[] { Key.Y, Key.C }),
|
||||
new CommandBindingConfiguration(Commands.Copy, new[] { Key.Y, Key.Y }),
|
||||
new CommandBindingConfiguration(Commands.CopyHash, new[] { Key.C, Key.H }),
|
||||
new CommandBindingConfiguration(Commands.CopyPath, new[] { Key.C, Key.P }),
|
||||
new CommandBindingConfiguration(Commands.CreateContainer, Key.F7),
|
||||
new CommandBindingConfiguration(Commands.CreateContainer, new[] { Key.C, Key.C }),
|
||||
new CommandBindingConfiguration(Commands.CreateElement, new[] { Key.C, Key.E }),
|
||||
new CommandBindingConfiguration(Commands.Cut, new[] { Key.D, Key.D }),
|
||||
new CommandBindingConfiguration(Commands.Edit, new KeyConfig(Key.F4)),
|
||||
new CommandBindingConfiguration(Commands.EnterRapidTravel, new KeyConfig(Key.OemComma, shift: true)),
|
||||
new CommandBindingConfiguration(Commands.FindByName, new[] { Key.F, Key.N }),
|
||||
new CommandBindingConfiguration(Commands.FindByNameRegex, new[] { Key.F, Key.R }),
|
||||
new CommandBindingConfiguration(Commands.GoToHome, new[] { Key.G, Key.H }),
|
||||
new CommandBindingConfiguration(Commands.GoToPath, new KeyConfig(Key.L, ctrl: true)),
|
||||
new CommandBindingConfiguration(Commands.GoToPath, new[] { Key.G, Key.P }),
|
||||
new CommandBindingConfiguration(Commands.GoToProvider, new[] { Key.G, Key.T }),
|
||||
new CommandBindingConfiguration(Commands.GoToRoot, new[] { Key.G, Key.R }),
|
||||
new CommandBindingConfiguration(Commands.HardDelete, new[] { new KeyConfig(Key.D,shift: true), new KeyConfig(Key.D, shift: true) }),
|
||||
new CommandBindingConfiguration(Commands.Mark, Key.Space),
|
||||
new CommandBindingConfiguration(Commands.MoveToLast, new KeyConfig(Key.G, shift: true)),
|
||||
new CommandBindingConfiguration(Commands.MoveToFirst, new[] { Key.G, Key.G }),
|
||||
new CommandBindingConfiguration(Commands.NextTimelineBlock, Key.L ),
|
||||
new CommandBindingConfiguration(Commands.NextTimelineCommand, Key.J ),
|
||||
new CommandBindingConfiguration(Commands.OpenInFileBrowser, new[] { Key.O, Key.E }),
|
||||
new CommandBindingConfiguration(Commands.PasteMerge, new[] { Key.P, Key.P }),
|
||||
new CommandBindingConfiguration(Commands.PasteOverwrite, new[] { Key.P, Key.O }),
|
||||
new CommandBindingConfiguration(Commands.PasteSkip, new[] { Key.P, Key.S }),
|
||||
new CommandBindingConfiguration(Commands.PinFavorite, new[] { Key.F, Key.P }),
|
||||
new CommandBindingConfiguration(Commands.PreviousTimelineBlock, Key.H ),
|
||||
new CommandBindingConfiguration(Commands.PreviousTimelineCommand, Key.K ),
|
||||
new CommandBindingConfiguration(Commands.Refresh, Key.R),
|
||||
new CommandBindingConfiguration(Commands.Rename, Key.F2),
|
||||
new CommandBindingConfiguration(Commands.Rename, new[] { Key.C, Key.W }),
|
||||
new CommandBindingConfiguration(Commands.RunCommand, new KeyConfig(Key.D4, shift: true)),
|
||||
new CommandBindingConfiguration(Commands.ScanContainerSize, new[] { Key.C, Key.S }),
|
||||
new CommandBindingConfiguration(Commands.ShowAllShotcut, Key.F1),
|
||||
new CommandBindingConfiguration(Commands.SoftDelete, new[] { new KeyConfig(Key.D), new KeyConfig(Key.D, shift: true) }),
|
||||
new CommandBindingConfiguration(Commands.SwitchToLastTab, Key.D9),
|
||||
new CommandBindingConfiguration(Commands.SwitchToTab1, Key.D1),
|
||||
new CommandBindingConfiguration(Commands.SwitchToTab2, Key.D2),
|
||||
new CommandBindingConfiguration(Commands.SwitchToTab3, Key.D3),
|
||||
new CommandBindingConfiguration(Commands.SwitchToTab4, Key.D4),
|
||||
new CommandBindingConfiguration(Commands.SwitchToTab5, Key.D5),
|
||||
new CommandBindingConfiguration(Commands.SwitchToTab6, Key.D6),
|
||||
new CommandBindingConfiguration(Commands.SwitchToTab7, Key.D7),
|
||||
new CommandBindingConfiguration(Commands.SwitchToTab8, Key.D8),
|
||||
new CommandBindingConfiguration(Commands.TimelinePause, new[] { Key.T, Key.P }),
|
||||
new CommandBindingConfiguration(Commands.TimelineRefresh, new[] { Key.T, Key.R }),
|
||||
new CommandBindingConfiguration(Commands.TimelineStart, new[] { Key.T, Key.S }),
|
||||
new CommandBindingConfiguration(Commands.ToggleAdvancedIcons, new[] { Key.Z, Key.I }),
|
||||
new CommandBindingConfiguration(Commands.GoUp, Key.Left),
|
||||
new CommandBindingConfiguration(Commands.Open, Key.Right),
|
||||
new CommandBindingConfiguration(Commands.OpenOrRun, Key.Enter),
|
||||
new CommandBindingConfiguration(Commands.MoveCursorUp, Key.Up),
|
||||
new CommandBindingConfiguration(Commands.MoveCursorDown, Key.Down),
|
||||
new CommandBindingConfiguration(Commands.MoveCursorUpPage, Key.PageUp),
|
||||
new CommandBindingConfiguration(Commands.MoveCursorDownPage, Key.PageDown),
|
||||
};
|
||||
}
|
||||
|
||||
private static void PopulateDefaultEditorPrograms(Dictionary<string, string> configuration)
|
||||
{
|
||||
var editorPrograms = new List<ProgramConfiguration>()
|
||||
{
|
||||
new ProgramConfiguration(@"c:\Program Files\Notepad++\notepad++.exe"),
|
||||
new ProgramConfiguration("notepad.exe"),
|
||||
};
|
||||
|
||||
for (var i = 0; i < editorPrograms.Count; i++)
|
||||
{
|
||||
if (editorPrograms[i].Path is not string path) continue;
|
||||
configuration.Add($"{SectionNames.ProgramsSectionName}:{nameof(ProgramsConfiguration.DefaultEditorPrograms)}:[{i}]:{nameof(ProgramConfiguration.Path)}", path);
|
||||
|
||||
if (editorPrograms[i].Arguments is string arguments)
|
||||
{
|
||||
configuration.Add($"{SectionNames.ProgramsSectionName}:{nameof(ProgramsConfiguration.DefaultEditorPrograms)}:[{i}]:{nameof(ProgramConfiguration.Arguments)}", arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace FileTime.GuiApp.Configuration
|
||||
{
|
||||
public class ProgramConfiguration
|
||||
{
|
||||
public string? Path { get; set; }
|
||||
public string? Arguments { get; set; }
|
||||
|
||||
public ProgramConfiguration() { }
|
||||
|
||||
public ProgramConfiguration(string? path, string? arguments = null)
|
||||
{
|
||||
Path = path;
|
||||
Arguments = arguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FileTime.GuiApp.Configuration
|
||||
{
|
||||
public class ProgramsConfiguration
|
||||
{
|
||||
public List<ProgramConfiguration> DefaultEditorPrograms { get; set; } = new();
|
||||
public List<ProgramConfiguration> EditorPrograms { get; set; } = new();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace FileTime.GuiApp.Configuration
|
||||
{
|
||||
public static class SectionNames
|
||||
{
|
||||
public const string KeybindingSectionName = "KeyBindings";
|
||||
public const string ProgramsSectionName = "Programs";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace FileTime.GuiApp.Converters
|
||||
{
|
||||
public class DateTimeConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
|
||||
value is DateTime dateTime && parameter is string parameterS
|
||||
? dateTime.ToString(parameterS)
|
||||
: value;
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace FileTime.GuiApp.Converters
|
||||
{
|
||||
public class FormatSizeConverter : IValueConverter
|
||||
{
|
||||
private const long OneKiloByte = 1024;
|
||||
private const long OneMegaByte = OneKiloByte * 1024;
|
||||
private const long OneGigaByte = OneMegaByte * 1024;
|
||||
private const long OneTerraByte = OneGigaByte * 1024;
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
return (value, int.TryParse(parameter?.ToString(), out var prec)) switch
|
||||
{
|
||||
(long size, true) => ToSizeString(size, prec),
|
||||
(long size, false) => ToSizeString(size),
|
||||
(null, _) => "...",
|
||||
_ => value
|
||||
};
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static string ToSizeString(long fileSize, int precision = 1)
|
||||
{
|
||||
var fileSizeD = (decimal)fileSize;
|
||||
var (size, suffix) = fileSize switch
|
||||
{
|
||||
> OneTerraByte => (fileSizeD / OneTerraByte, "T"),
|
||||
> OneGigaByte => (fileSizeD / OneGigaByte, "G"),
|
||||
> OneMegaByte => (fileSizeD / OneMegaByte, "M"),
|
||||
> OneKiloByte => (fileSizeD / OneKiloByte, "K"),
|
||||
_ => (fileSizeD, "B")
|
||||
};
|
||||
|
||||
var result = string.Format("{0:N" + precision + "}", size).Replace(',', '.');
|
||||
|
||||
if (result.Contains('.')) result = result.TrimEnd('0').TrimEnd('.');
|
||||
return result + " " + suffix;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using FileTime.App.Core.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace FileTime.GuiApp.Converters
|
||||
{
|
||||
public class GetFileExtensionConverter : IValueConverter
|
||||
{
|
||||
private IItemNameConverterService? _itemNameConverterService;
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is not string fullName) return value;
|
||||
_itemNameConverterService ??= DI.ServiceProvider.GetRequiredService<IItemNameConverterService>();
|
||||
|
||||
return _itemNameConverterService.GetFileExtension(fullName);
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
using FileTime.App.Core.Models.Enums;
|
||||
|
||||
namespace FileTime.GuiApp.Converters
|
||||
{
|
||||
public class ItemViewModeToBrushConverter : IValueConverter
|
||||
{
|
||||
public Brush? DefaultBrush { get; set; }
|
||||
public Brush? AlternativeBrush { get; set; }
|
||||
public Brush? SelectedBrush { get; set; }
|
||||
public Brush? MarkedBrush { get; set; }
|
||||
public Brush? MarkedSelectedBrush { get; set; }
|
||||
public Brush? MarkedAlternativeBrush { get; set; }
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is ItemViewMode viewMode)
|
||||
{
|
||||
return viewMode switch
|
||||
{
|
||||
ItemViewMode.Default => DefaultBrush,
|
||||
ItemViewMode.Alternative => AlternativeBrush,
|
||||
ItemViewMode.Selected => SelectedBrush,
|
||||
ItemViewMode.Marked => MarkedBrush,
|
||||
ItemViewMode.MarkedSelected => MarkedSelectedBrush,
|
||||
ItemViewMode.MarkedAlternative => MarkedAlternativeBrush,
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using FileTime.App.Core.Models.Enums;
|
||||
using FileTime.App.Core.ViewModels;
|
||||
|
||||
namespace FileTime.GuiApp.Converters
|
||||
{
|
||||
public class ItemViewModelIsAttributeTypeConverter : IValueConverter
|
||||
{
|
||||
public bool Invert { get; set; }
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
var attributeType = GetAttributeType(value);
|
||||
if (parameter == null) return attributeType;
|
||||
var result = parameter is ItemAttributeType targetAttribute && attributeType == targetAttribute;
|
||||
if (Invert && parameter is ItemAttributeType) result = !result;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ItemAttributeType? GetAttributeType(object? value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
IFileViewModel => ItemAttributeType.File,
|
||||
IContainerSizeContainerViewModel => ItemAttributeType.SizeContainer,
|
||||
IElementViewModel => ItemAttributeType.Element,
|
||||
IContainerViewModel => ItemAttributeType.Container,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
using FileTime.App.Core.Models;
|
||||
using FileTime.GuiApp.ViewModels;
|
||||
|
||||
namespace FileTime.GuiApp.Converters
|
||||
{
|
||||
public class NamePartShrinkerConverter : IMultiValueConverter
|
||||
{
|
||||
private const int PixelPerChar = 8;
|
||||
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (values.Count > 0 && values[0] is IList<ItemNamePart> nameParts)
|
||||
{
|
||||
var attributeWidth = values[2] is bool b && b ? 340 : 0;
|
||||
var newNameParts = nameParts;
|
||||
if (values.Count > 1 && values[1] is double width && width > 0)
|
||||
{
|
||||
newNameParts = GetNamePartsForWidth(nameParts, width - attributeWidth);
|
||||
}
|
||||
|
||||
return newNameParts.Select(p => new ItemNamePartViewModel(p.Text, p.IsSpecial ? TextDecorations.Underline : null)).ToList();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<ItemNamePart> GetNamePartsForWidth(IList<ItemNamePart> nameParts, double maxWidth)
|
||||
{
|
||||
//Best case, we are in the range
|
||||
var textLength = nameParts.Select(p => p.Text.Length).Sum();
|
||||
if (textLength * PixelPerChar <= maxWidth)
|
||||
{
|
||||
return nameParts.ToList();
|
||||
}
|
||||
|
||||
//Trying at least with the special parts
|
||||
var newNameParts = new ItemNamePart?[nameParts.Count];
|
||||
for (var i = 0; i < nameParts.Count; i++)
|
||||
{
|
||||
if (nameParts[i].IsSpecial)
|
||||
{
|
||||
newNameParts[i] = nameParts[i];
|
||||
}
|
||||
}
|
||||
|
||||
return GetNamePartsForWidthOptimistic(nameParts, newNameParts, maxWidth);
|
||||
}
|
||||
|
||||
private static List<ItemNamePart> GetNamePartsForWidthOptimistic(IList<ItemNamePart> nameParts, ItemNamePart?[] newNameParts, double maxWidth)
|
||||
{
|
||||
var trimmedIndexes = new List<int>();
|
||||
for (var i = 0; i < newNameParts.Length; i++)
|
||||
{
|
||||
if (newNameParts[i] == null)
|
||||
{
|
||||
trimmedIndexes.Add(i);
|
||||
newNameParts[i] = new ItemNamePart("...");
|
||||
}
|
||||
}
|
||||
|
||||
var textLength = newNameParts.Select(p => p?.Text.Length ?? 0).Sum();
|
||||
if (textLength * PixelPerChar > maxWidth)
|
||||
{
|
||||
return GetNamePartsForWidthPessimistic(nameParts, maxWidth);
|
||||
}
|
||||
|
||||
foreach (var trimmedIndex in trimmedIndexes)
|
||||
{
|
||||
var baseTextLength = newNameParts.Select((p, i) => i == trimmedIndex ? 0 : (p?.Text.Length ?? 0)).Sum();
|
||||
var proposedText = nameParts[trimmedIndex].Text;
|
||||
var trimmed = false;
|
||||
while ((baseTextLength + proposedText.Length + (trimmed ? 3 : 0)) * PixelPerChar > maxWidth)
|
||||
{
|
||||
proposedText = proposedText[0..^1];
|
||||
trimmed = true;
|
||||
}
|
||||
newNameParts[trimmedIndex] = new ItemNamePart(proposedText + (trimmed ? "..." : ""));
|
||||
if (trimmed) break;
|
||||
}
|
||||
|
||||
return newNameParts.OfType<ItemNamePart>().ToList();
|
||||
}
|
||||
|
||||
private static List<ItemNamePart> GetNamePartsForWidthPessimistic(IList<ItemNamePart> nameParts, double maxWidth)
|
||||
{
|
||||
var newNameParts = new List<ItemNamePart>(nameParts);
|
||||
foreach (var namePart in nameParts)
|
||||
{
|
||||
var baseTextLength = newNameParts.Select(p => p.Text.Length).Sum();
|
||||
var proposedText = namePart.Text;
|
||||
var trimmed = false;
|
||||
|
||||
while ((baseTextLength + 3) * PixelPerChar > maxWidth && proposedText != "")
|
||||
{
|
||||
proposedText = proposedText[0..^1];
|
||||
trimmed = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(proposedText)) newNameParts.Add(new ItemNamePart(proposedText, namePart.IsSpecial));
|
||||
if (trimmed) break;
|
||||
}
|
||||
if (newNameParts.Last().IsSpecial)
|
||||
{
|
||||
newNameParts.Add(new ItemNamePart("..."));
|
||||
}
|
||||
else
|
||||
{
|
||||
var last = newNameParts.Last();
|
||||
last.Text += "...";
|
||||
}
|
||||
return newNameParts;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FileTime.GuiApp
|
||||
{
|
||||
public static class DI
|
||||
{
|
||||
public static IServiceProvider ServiceProvider { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,47 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<!--Avalonia doesen't support TrimMode=link currently,but we are working on that https://github.com/AvaloniaUI/Avalonia/issues/6892 -->
|
||||
<TrimMode>copyused</TrimMode>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<ApplicationIcon>Assets\filetime.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\filetime.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
<None Remove=".gitignore" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\filetime.ico" />
|
||||
<AvaloniaXaml Update="Views\MainWindow.axaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</AvaloniaXaml>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<!--This helps with theme dll-s trimming.
|
||||
If you will publish your application in self-contained mode with p:PublishTrimmed=true and it will use Fluent theme Default theme will be trimmed from the output and vice versa.
|
||||
https://github.com/AvaloniaUI/Avalonia/issues/5593 -->
|
||||
<TrimmableAssembly Include="Avalonia.Themes.Fluent" />
|
||||
<TrimmableAssembly Include="Avalonia.Themes.Default" />
|
||||
<Compile Update="Views\MainWindow.axaml.cs">
|
||||
<DependentUpon>MainWindow.axaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.13" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.13" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.13" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.13" />
|
||||
<PackageReference Include="XamlNameReferenceGenerator" Version="1.3.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
<PackageReference Include="MvvmGen" Version="1.1.5" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.3.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="XamlNameReferenceGenerator" Version="1.3.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj" />
|
||||
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
|
||||
<ProjectReference Include="..\..\..\Providers\FileTime.Providers.Local.Abstractions\FileTime.Providers.Local.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace FileTime.GuiApp.Logging
|
||||
{
|
||||
public class ToastMessageSink : ILogEventSink
|
||||
{
|
||||
//private readonly IDialogService dialogService;
|
||||
|
||||
public ToastMessageSink(/*IDialogService dialogService*/)
|
||||
{
|
||||
//this.dialogService = dialogService;
|
||||
}
|
||||
|
||||
public void Emit(LogEvent logEvent)
|
||||
{
|
||||
/*if (logEvent.Level >= LogEventLevel.Error)
|
||||
{
|
||||
var message = logEvent.RenderMessage();
|
||||
dialogService.ShowToastMessage(message);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace FileTime.GuiApp.Models
|
||||
{
|
||||
public class InputElementWrapper
|
||||
{
|
||||
/*public InputElement InputElement { get; }
|
||||
|
||||
public string Value { get; set; }
|
||||
|
||||
public object? Option { get; set; }
|
||||
|
||||
public char? PasswordChar { get; set; }
|
||||
|
||||
public InputElementWrapper(InputElement inputElement, string? defaultValue = null)
|
||||
{
|
||||
InputElement = inputElement;
|
||||
Value = defaultValue ?? "";
|
||||
PasswordChar = inputElement.InputType == InputType.Password ? '*' : null;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace FileTime.GuiApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
[STAThread]
|
||||
public static void Main(string[] args) => BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
=> AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
.LogToTrace();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FileTime.GuiApp.ViewModels
|
||||
{
|
||||
public interface IMainWindowViewModelBase
|
||||
{
|
||||
bool Loading { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace FileTime.GuiApp.ViewModels
|
||||
{
|
||||
public class ItemNamePartViewModel
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public TextDecorationCollection? TextDecorations { get; set; }
|
||||
|
||||
public ItemNamePartViewModel(string text, TextDecorationCollection? textDecorations)
|
||||
{
|
||||
Text = text;
|
||||
TextDecorations = textDecorations;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FileTime.GuiApp.ViewModels
|
||||
{
|
||||
public class MainWindowLoadingViewModel : IMainWindowViewModelBase
|
||||
{
|
||||
public bool Loading => true;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Avalonia.Input;
|
||||
using FileTime.App.Core;
|
||||
using FileTime.App.Core.ViewModels;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Services;
|
||||
using FileTime.Providers.Local;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MvvmGen;
|
||||
|
||||
namespace FileTime.GuiApp.ViewModels
|
||||
{
|
||||
public class MainWindowViewModel
|
||||
[ViewModel]
|
||||
[Inject(typeof(IAppState), "_appState")]
|
||||
[Inject(typeof(ILocalContentProvider), "_localContentProvider")]
|
||||
[Inject(typeof(IServiceProvider), PropertyName = "_serviceProvider")]
|
||||
public partial class MainWindowViewModel : IMainWindowViewModelBase
|
||||
{
|
||||
public bool Loading => false;
|
||||
public IAppState AppState => _appState;
|
||||
|
||||
partial void OnInitialize()
|
||||
{
|
||||
if (AppState.Tabs.Count == 0)
|
||||
{
|
||||
var tab = _serviceProvider.GetInitableResolver<IContainer>(_localContentProvider).GetRequiredService<ITab>();
|
||||
var tabViewModel = _serviceProvider.GetInitableResolver(tab).GetRequiredService<ITabViewModel>();
|
||||
|
||||
_appState.AddTab(tabViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessKeyDown(Key key, KeyModifiers keyModifiers, Action<bool> setHandled)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
89
src/GuiApp/Avalonia/FileTime.GuiApp/Views/ItemView.axaml
Normal file
89
src/GuiApp/Avalonia/FileTime.GuiApp/Views/ItemView.axaml
Normal file
@@ -0,0 +1,89 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:appcoreenums="using:FileTime.App.Core.Models.Enums"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="FileTime.GuiApp.Views.ItemView"
|
||||
x:Name="ItemRoot"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{Binding ViewMode,Converter={StaticResource ItemViewModeToBackgroundConverter}}">
|
||||
<Grid ColumnDefinitions="20,*,Auto" Margin="3" x:Name="RootGrid">
|
||||
<Grid.Styles>
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="Foreground" Value="{Binding DataContext.ViewMode,Converter={StaticResource ItemViewModeToForegroundConverter},ElementName=ItemRoot}"/>
|
||||
</Style>
|
||||
</Grid.Styles>
|
||||
<!--Image
|
||||
Width="18"
|
||||
Height="18"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Source="{Binding Converter={StaticResource ItemToImageConverter}}" /-->
|
||||
|
||||
<ItemsControl
|
||||
Margin="5,0,0,0"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch">
|
||||
<ItemsControl.Items>
|
||||
<MultiBinding Converter="{StaticResource NamePartShrinkerConverter}">
|
||||
<MultiBinding.Bindings>
|
||||
<Binding Path="DisplayName^"/>
|
||||
<Binding ElementName="RootGrid" Path="Bounds.Width"/>
|
||||
<Binding ElementName="ItemRoot" Path="ShowAttributes"/>
|
||||
</MultiBinding.Bindings>
|
||||
</MultiBinding>
|
||||
</ItemsControl.Items>
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<TextBlock
|
||||
Text="{Binding Text}"
|
||||
TextDecorations="{Binding TextDecorations}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<Grid Grid.Column="2" IsVisible="{Binding ShowAttributes,ElementName=ItemRoot}">
|
||||
<Grid ColumnDefinitions="50,50,90,40,45"
|
||||
HorizontalAlignment="Right"
|
||||
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttributeTypeConverter},ConverterParameter={x:Static appcoreenums:ItemAttributeType.File}}">
|
||||
|
||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Text="{Binding BaseItem.DisplayName, Converter={StaticResource GetFileExtensionConverter}}"/>
|
||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="1" Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}"/>
|
||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="2" Text="{Binding BaseItem.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=yyyy-MM-dd}"/>
|
||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="3" Text="{Binding BaseItem.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=hh:mm}"/>
|
||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="4" Text="{Binding BaseItem.Attributes}"/>
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="90,40,45"
|
||||
HorizontalAlignment="Right"
|
||||
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttributeTypeConverter},ConverterParameter={x:Static appcoreenums:ItemAttributeType.Container}}">
|
||||
|
||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Text="{Binding Item.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=yyyy-MM-dd}"/>
|
||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="1" Text="{Binding BaseItem.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=hh:mm}"/>
|
||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="2" Text="{Binding BaseItem.Attributes}"/>
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="50,90,40,45"
|
||||
HorizontalAlignment="Right"
|
||||
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttributeTypeConverter},ConverterParameter={x:Static appcoreenums:ItemAttributeType.SizeContainer}}">
|
||||
|
||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}"/>
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="50,50"
|
||||
HorizontalAlignment="Right"
|
||||
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttributeTypeConverter},ConverterParameter={x:Static appcoreenums:ItemAttributeType.Element}}">
|
||||
|
||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Text="{Binding BaseItem.DisplayName, Converter={StaticResource GetFileExtensionConverter}}"/>
|
||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="1" Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
21
src/GuiApp/Avalonia/FileTime.GuiApp/Views/ItemView.axaml.cs
Normal file
21
src/GuiApp/Avalonia/FileTime.GuiApp/Views/ItemView.axaml.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace FileTime.GuiApp.Views
|
||||
{
|
||||
public partial class ItemView : UserControl
|
||||
{
|
||||
public static readonly StyledProperty<bool> ShowAttributesProperty = AvaloniaProperty.Register<ItemView, bool>(nameof(ShowAttributes), true);
|
||||
|
||||
public bool ShowAttributes
|
||||
{
|
||||
get => GetValue(ShowAttributesProperty);
|
||||
set => SetValue(ShowAttributesProperty, value);
|
||||
}
|
||||
|
||||
public ItemView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,54 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:FileTime.GuiApp.ViewModels"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:FileTime.GuiApp.ViewModels"
|
||||
xmlns:corevm="using:FileTime.App.Core.ViewModels"
|
||||
xmlns:local="using:FileTime.GuiApp.Views"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
InputElement.KeyDown="OnKeyDown"
|
||||
Opened="OnWindowOpened"
|
||||
x:Class="FileTime.GuiApp.Views.MainWindow"
|
||||
Icon="/Assets/filetime.ico"
|
||||
Title="FileTime.GuiApp">
|
||||
Title="FileTime"
|
||||
TransparencyLevelHint="Blur"
|
||||
Background="Transparent"
|
||||
ExtendClientAreaToDecorationsHint="True"
|
||||
x:DataType="vm:MainWindowViewModel"
|
||||
x:CompileBindings="True">
|
||||
|
||||
<Design.DataContext>
|
||||
<vm:MainWindowViewModel/>
|
||||
</Design.DataContext>
|
||||
<Design.DataContext>
|
||||
<vm:MainWindowViewModel/>
|
||||
</Design.DataContext>
|
||||
<Grid Background="{DynamicResource AppBackgroundBrush}">
|
||||
<Grid IsVisible="{Binding Loading, Converter={x:Static BoolConverters.Not}}">
|
||||
<Grid ColumnDefinitions="250,*" RowDefinitions="Auto,*">
|
||||
<ListBox
|
||||
Grid.Row="1"
|
||||
x:Name="CurrentItems"
|
||||
x:CompileBindings="False"
|
||||
AutoScrollToSelectedItem="True"
|
||||
IsTabStop="True"
|
||||
Items="{Binding AppState.SelectedTab.CurrentItems^}"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Visible"
|
||||
Classes="ContentListView">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="corevm:IItemViewModel">
|
||||
<local:ItemView HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch"/>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
<!--Borders-->
|
||||
|
||||
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
|
||||
<Grid IsVisible="{Binding Loading}">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Image Source="/Assets/filetime.ico" Width="128" Height="128"/>
|
||||
<TextBlock Text="Loading..." HorizontalAlignment="Center" Margin="50"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -1,12 +1,53 @@
|
||||
using System;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using FileTime.GuiApp.Models;
|
||||
using FileTime.GuiApp.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FileTime.GuiApp.Views
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private readonly ILogger<MainWindow>? _logger;
|
||||
private InputElementWrapper? _inputElementWrapper;
|
||||
|
||||
public MainWindowViewModel? ViewModel
|
||||
{
|
||||
get => DataContext as MainWindowViewModel;
|
||||
set
|
||||
{
|
||||
if (value != DataContext)
|
||||
{
|
||||
DataContext = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
_logger = DI.ServiceProvider.GetService<ILogger<MainWindow>>();
|
||||
_logger?.LogInformation($"Starting {nameof(MainWindow)} initialization...");
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnWindowOpened(object sender, EventArgs e)
|
||||
{
|
||||
if (DataContext is not MainWindowViewModel)
|
||||
{
|
||||
_logger?.LogInformation($"{nameof(MainWindow)} opened, starting {nameof(MainWindowViewModel)} initialization...");
|
||||
ViewModel = DI.ServiceProvider.GetRequiredService<MainWindowViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (_inputElementWrapper == null)
|
||||
{
|
||||
ViewModel?.ProcessKeyDown(e.Key, e.KeyModifiers, h => e.Handled = h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
148
src/Library/InitableService/Initable.cs
Normal file
148
src/Library/InitableService/Initable.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
// Autogenerated
|
||||
namespace InitableService;
|
||||
|
||||
public interface IInitable
|
||||
{
|
||||
void Init();
|
||||
}
|
||||
public interface IInitable<T1>
|
||||
{
|
||||
void Init(T1 obj1);
|
||||
}
|
||||
public interface IInitable<T1, T2>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2);
|
||||
}
|
||||
public interface IInitable<T1, T2, T3>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2, T3 obj3);
|
||||
}
|
||||
public interface IInitable<T1, T2, T3, T4>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2, T3 obj3, T4 obj4);
|
||||
}
|
||||
public interface IInitable<T1, T2, T3, T4, T5>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5);
|
||||
}
|
||||
public interface IInitable<T1, T2, T3, T4, T5, T6>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6);
|
||||
}
|
||||
public interface IInitable<T1, T2, T3, T4, T5, T6, T7>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7);
|
||||
}
|
||||
public interface IInitable<T1, T2, T3, T4, T5, T6, T7, T8>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8);
|
||||
}
|
||||
public interface IInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9);
|
||||
}
|
||||
public interface IInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9, T10 obj10);
|
||||
}
|
||||
public interface IInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9, T10 obj10, T11 obj11);
|
||||
}
|
||||
public interface IInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9, T10 obj10, T11 obj11, T12 obj12);
|
||||
}
|
||||
public interface IInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9, T10 obj10, T11 obj11, T12 obj12, T13 obj13);
|
||||
}
|
||||
public interface IInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9, T10 obj10, T11 obj11, T12 obj12, T13 obj13, T14 obj14);
|
||||
}
|
||||
public interface IInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>
|
||||
{
|
||||
void Init(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9, T10 obj10, T11 obj11, T12 obj12, T13 obj13, T14 obj14, T15 obj15);
|
||||
}
|
||||
public interface IAsyncInitable
|
||||
{
|
||||
Task InitAsync();
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1>
|
||||
{
|
||||
Task InitAsync(T1 obj1);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2, T3>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2, T3 obj3);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2, T3, T4>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2, T3 obj3, T4 obj4);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2, T3, T4, T5>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2, T3, T4, T5, T6>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2, T3, T4, T5, T6, T7>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2, T3, T4, T5, T6, T7, T8>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9, T10 obj10);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9, T10 obj10, T11 obj11);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9, T10 obj10, T11 obj11, T12 obj12);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9, T10 obj10, T11 obj11, T12 obj12, T13 obj13);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9, T10 obj10, T11 obj11, T12 obj12, T13 obj13, T14 obj14);
|
||||
}
|
||||
|
||||
public interface IAsyncInitable<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>
|
||||
{
|
||||
Task InitAsync(T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, T7 obj7, T8 obj8, T9 obj9, T10 obj10, T11 obj11, T12 obj12, T13 obj13, T14 obj14, T15 obj15);
|
||||
}
|
||||
|
||||
|
||||
1193
src/Library/InitableService/InitableExtensions.cs
Normal file
1193
src/Library/InitableService/InitableExtensions.cs
Normal file
File diff suppressed because it is too large
Load Diff
13
src/Library/InitableService/InitableService.csproj
Normal file
13
src/Library/InitableService/InitableService.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
180
src/Library/InitableService/generateExtensions.csx
Normal file
180
src/Library/InitableService/generateExtensions.csx
Normal file
@@ -0,0 +1,180 @@
|
||||
#!/usr/bin/env dotnet-script
|
||||
|
||||
{
|
||||
const string header =
|
||||
@"// Autogenerated
|
||||
using InitableService;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
public static class InitableExtensions
|
||||
{
|
||||
";
|
||||
|
||||
const string footer = "}";
|
||||
using var initableWriter = File.CreateText("InitableExtensions.cs");
|
||||
initableWriter.WriteLine(header);
|
||||
const int generateForParams = 15;
|
||||
|
||||
for (var i = 0; i <= generateForParams; i++)
|
||||
{
|
||||
WriteInitableExtension(initableWriter, false, i);
|
||||
initableWriter.WriteLine();
|
||||
}
|
||||
|
||||
for (var i = 0; i <= generateForParams; i++)
|
||||
{
|
||||
WriteInitableExtension(initableWriter, true, i);
|
||||
initableWriter.WriteLine();
|
||||
}
|
||||
|
||||
initableWriter.WriteLine(footer);
|
||||
|
||||
for (var i = 0; i <= generateForParams; i++)
|
||||
{
|
||||
WriteInitableResolver(initableWriter, false, i);
|
||||
initableWriter.WriteLine();
|
||||
}
|
||||
|
||||
for (var i = 0; i <= generateForParams; i++)
|
||||
{
|
||||
WriteInitableResolver(initableWriter, true, i);
|
||||
initableWriter.WriteLine();
|
||||
}
|
||||
|
||||
initableWriter.Flush();
|
||||
}
|
||||
|
||||
void WriteInitableExtension(StreamWriter initableWriter, bool isAsync, int i)
|
||||
{
|
||||
initableWriter.Write($"\tpublic static {(isAsync ? "Async" : "")}Resolver");
|
||||
if (i > 0)
|
||||
{
|
||||
initableWriter.Write("<");
|
||||
initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => "T" + c)));
|
||||
initableWriter.Write(">");
|
||||
}
|
||||
initableWriter.Write($" Get{(isAsync ? "Async" : "")}InitableResolver");
|
||||
if (i > 0)
|
||||
{
|
||||
initableWriter.Write("<");
|
||||
initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => "T" + c)));
|
||||
initableWriter.Write(">");
|
||||
}
|
||||
|
||||
initableWriter.Write("(");
|
||||
var thisParameter = "this IServiceProvider serviceProvider";
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => $"T{c} obj{c}").Prepend(thisParameter)));
|
||||
}
|
||||
else
|
||||
{
|
||||
initableWriter.Write(thisParameter);
|
||||
}
|
||||
|
||||
initableWriter.WriteLine(")");
|
||||
initableWriter.WriteLine("\t{");
|
||||
initableWriter.Write($"\t\treturn new {(isAsync ? "Async" : "")}Resolver");
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
initableWriter.Write("<");
|
||||
initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => "T" + c)));
|
||||
initableWriter.Write(">");
|
||||
}
|
||||
|
||||
initableWriter.Write("(");
|
||||
if (i > 0)
|
||||
{
|
||||
initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => $"obj{c}").Prepend("serviceProvider")));
|
||||
}
|
||||
else
|
||||
{
|
||||
initableWriter.Write("serviceProvider");
|
||||
}
|
||||
initableWriter.WriteLine(");");
|
||||
|
||||
initableWriter.WriteLine("\t}");
|
||||
}
|
||||
|
||||
void WriteInitableResolver(StreamWriter initableWriter, bool isAsync, int i)
|
||||
{
|
||||
initableWriter.Write($"public class {(isAsync ? "Async" : "")}Resolver");
|
||||
if (i > 0)
|
||||
{
|
||||
initableWriter.Write("<");
|
||||
initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => "T" + c)));
|
||||
initableWriter.Write(">");
|
||||
}
|
||||
initableWriter.WriteLine();
|
||||
initableWriter.WriteLine("{");
|
||||
initableWriter.WriteLine("\tIServiceProvider _serviceProvider;");
|
||||
if (i > 0)
|
||||
{
|
||||
for (var j = 1; j <= i; j++)
|
||||
{
|
||||
initableWriter.WriteLine($"\tprivate T{j} _obj{j};");
|
||||
}
|
||||
}
|
||||
|
||||
initableWriter.Write($"\tpublic {(isAsync ? "Async" : "")}Resolver(");
|
||||
if (i > 0)
|
||||
{
|
||||
initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => $"T{c} obj{c}").Prepend("IServiceProvider serviceProvider")));
|
||||
}
|
||||
else
|
||||
{
|
||||
initableWriter.Write("IServiceProvider serviceProvider");
|
||||
}
|
||||
|
||||
initableWriter.WriteLine(")");
|
||||
initableWriter.WriteLine("\t{");
|
||||
initableWriter.WriteLine("\t\t_serviceProvider = serviceProvider;");
|
||||
if (i > 0)
|
||||
{
|
||||
for (var j = 1; j <= i; j++)
|
||||
{
|
||||
initableWriter.WriteLine($"\t\t_obj{j} = obj{j};");
|
||||
}
|
||||
}
|
||||
initableWriter.WriteLine("\t}");
|
||||
initableWriter.WriteLine("");
|
||||
|
||||
var returnType = isAsync ? "async Task<TResult>" : "TResult";
|
||||
initableWriter.WriteLine($"\tpublic {returnType} GetRequiredService{(isAsync ? "Async" : "")}<TResult>()");
|
||||
|
||||
|
||||
|
||||
initableWriter.Write($"\t\twhere TResult : class, I{(isAsync ? "Async" : "")}Initable");
|
||||
if (i > 0)
|
||||
{
|
||||
initableWriter.Write("<");
|
||||
initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => "T" + c)));
|
||||
initableWriter.Write(">");
|
||||
}
|
||||
initableWriter.WriteLine("");
|
||||
initableWriter.WriteLine("\t{");
|
||||
initableWriter.WriteLine("\t\tvar initableService = _serviceProvider.GetRequiredService<TResult>()!;");
|
||||
if (isAsync)
|
||||
{
|
||||
initableWriter.Write("\t\tawait initableService.InitAsync");
|
||||
}
|
||||
else
|
||||
{
|
||||
initableWriter.Write("\t\tinitableService.Init");
|
||||
}
|
||||
|
||||
initableWriter.Write("(");
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => $"_obj{c}")));
|
||||
}
|
||||
initableWriter.WriteLine(");");
|
||||
initableWriter.WriteLine("\t\treturn initableService;");
|
||||
|
||||
initableWriter.WriteLine("\t}");
|
||||
initableWriter.WriteLine("}");
|
||||
}
|
||||
66
src/Library/InitableService/generateInterfaces.csx
Normal file
66
src/Library/InitableService/generateInterfaces.csx
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env dotnet-script
|
||||
|
||||
{
|
||||
const string header =
|
||||
@"// Autogenerated
|
||||
namespace InitableService;
|
||||
";
|
||||
|
||||
const string footer = "";
|
||||
using var initableWriter = File.CreateText("Initable.cs");
|
||||
initableWriter.WriteLine(header);
|
||||
const int generateForParams = 15;
|
||||
|
||||
for (var i = 0; i <= generateForParams; i++)
|
||||
{
|
||||
WriteInitable(initableWriter, false, i);
|
||||
}
|
||||
|
||||
for (var i = 0; i <= generateForParams; i++)
|
||||
{
|
||||
WriteInitable(initableWriter, true, i);
|
||||
initableWriter.WriteLine();
|
||||
}
|
||||
|
||||
initableWriter.WriteLine(footer);
|
||||
initableWriter.Flush();
|
||||
}
|
||||
|
||||
void WriteInitable(StreamWriter initableWriter, bool isAsync, int i)
|
||||
{
|
||||
initableWriter.Write($"public interface I{(isAsync ? "Async" : "")}Initable");
|
||||
if (i > 0)
|
||||
{
|
||||
initableWriter.Write("<");
|
||||
initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => "T" + c)));
|
||||
initableWriter.Write(">");
|
||||
}
|
||||
|
||||
initableWriter.WriteLine();
|
||||
initableWriter.WriteLine("{");
|
||||
|
||||
if (isAsync)
|
||||
{
|
||||
initableWriter.Write("\tTask InitAsync");
|
||||
}
|
||||
else
|
||||
{
|
||||
initableWriter.Write("\tvoid Init");
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
/*initableWriter.Write("<");
|
||||
initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => "T" + c)));
|
||||
initableWriter.Write(">");*/
|
||||
initableWriter.Write("(");
|
||||
initableWriter.Write(string.Join(", ", Enumerable.Range(1, i).Select(c => $"T{c} obj{c}")));
|
||||
initableWriter.Write(")");
|
||||
}
|
||||
else
|
||||
{
|
||||
initableWriter.Write("()");
|
||||
}
|
||||
initableWriter.WriteLine(";");
|
||||
initableWriter.WriteLine("}");
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\AppCommon\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,9 @@
|
||||
using FileTime.Core.Services;
|
||||
|
||||
namespace FileTime.Providers.Local
|
||||
{
|
||||
public interface ILocalContentProvider : IContentProvider
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.Models\FileTime.Core.Models.csproj" />
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.Services\FileTime.Core.Services.csproj" />
|
||||
<ProjectReference Include="..\FileTime.Providers.Local.Abstractions\FileTime.Providers.Local.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -5,7 +5,7 @@ using FileTime.Core.Services;
|
||||
|
||||
namespace FileTime.Providers.Local
|
||||
{
|
||||
public class LocalContentProvider : ContentProviderBase
|
||||
public class LocalContentProvider : ContentProviderBase, ILocalContentProvider
|
||||
{
|
||||
protected bool IsCaseInsensitive { get; init; }
|
||||
public LocalContentProvider() : base("local")
|
||||
@@ -43,7 +43,7 @@ namespace FileTime.Providers.Local
|
||||
return Task.FromResult((IItem)FileToElement(new FileInfo(path)));
|
||||
}
|
||||
|
||||
throw new FileNotFoundException();
|
||||
throw new FileNotFoundException("Directory or file not found", path);
|
||||
}
|
||||
|
||||
public override Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName) => Task.FromResult(GetItemsByContainer(fullName));
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace FileTime.Providers.Local
|
||||
public static IServiceCollection AddLocalServices(this IServiceCollection serviceCollection)
|
||||
{
|
||||
return serviceCollection
|
||||
.AddSingleton<LocalContentProvider>()
|
||||
.AddSingleton<IContentProvider, LocalContentProvider>(sp => sp.GetService<LocalContentProvider>() ?? throw new Exception($"No {nameof(LocalContentProvider)} instance found"));
|
||||
.AddSingleton<ILocalContentProvider, LocalContentProvider>()
|
||||
.AddSingleton<IContentProvider, ILocalContentProvider>(sp => sp.GetRequiredService<ILocalContentProvider>());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user