Move common things to AppCore from GuiApp

This commit is contained in:
2023-08-07 12:06:34 +02:00
parent 3d0eda54fb
commit 936c3896b9
48 changed files with 427 additions and 187 deletions

View File

@@ -12,8 +12,4 @@
<ProjectReference Include="..\FileTime.App.FuzzyPanel.Abstraction\FileTime.App.FuzzyPanel.Abstraction.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.2" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,4 @@
using Avalonia.Input;
using FileTime.App.Core.Models;
using FileTime.App.Core.ViewModels;
using FileTime.App.FuzzyPanel;
@@ -8,5 +8,5 @@ public interface ICommandPaletteViewModel : IFuzzyPanelViewModel<ICommandPalette
{
IObservable<bool> ShowWindow { get; }
void Close();
Task<bool> HandleKeyUp(KeyEventArgs keyEventArgs);
Task<bool> HandleKeyUp(GeneralKeyEventArgs keyEventArgs);
}

View File

@@ -7,7 +7,6 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\GuiApp\Avalonia\FileTime.GuiApp.App.Abstractions\FileTime.GuiApp.App.Abstractions.csproj" />
<ProjectReference Include="..\FileTime.App.CommandPalette.Abstractions\FileTime.App.CommandPalette.Abstractions.csproj" />
<ProjectReference Include="..\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
<ProjectReference Include="..\FileTime.App.FuzzyPanel\FileTime.App.FuzzyPanel.csproj" />

View File

@@ -1,11 +1,10 @@
using System.Text;
using Avalonia.Input;
using FileTime.App.CommandPalette.Services;
using FileTime.App.Core.Configuration;
using FileTime.App.Core.Models;
using FileTime.App.Core.Services;
using FileTime.App.Core.ViewModels;
using FileTime.App.FuzzyPanel;
using FileTime.GuiApp.App.Configuration;
using FileTime.GuiApp.App.Services;
using Microsoft.Extensions.Logging;
namespace FileTime.App.CommandPalette.ViewModels;
@@ -89,7 +88,7 @@ public class CommandPaletteViewModel : FuzzyPanelViewModel<ICommandPaletteEntryV
.Select(k => k.Keys)
.ToList();
public override async Task<bool> HandleKeyDown(KeyEventArgs keyEventArgs)
public override async Task<bool> HandleKeyDown(GeneralKeyEventArgs keyEventArgs)
{
if (keyEventArgs.Handled) return false;
@@ -99,7 +98,7 @@ public class CommandPaletteViewModel : FuzzyPanelViewModel<ICommandPaletteEntryV
return true;
}
if (keyEventArgs.Key == Key.Escape)
if (keyEventArgs.Key == Keys.Escape)
{
keyEventArgs.Handled = true;
Close();
@@ -109,11 +108,11 @@ public class CommandPaletteViewModel : FuzzyPanelViewModel<ICommandPaletteEntryV
return false;
}
public async Task<bool> HandleKeyUp(KeyEventArgs keyEventArgs)
public async Task<bool> HandleKeyUp(GeneralKeyEventArgs keyEventArgs)
{
if (keyEventArgs.Handled) return false;
if (keyEventArgs.Key == Key.Enter)
if (keyEventArgs.Key == Keys.Enter)
{
if (SelectedItem is null) return false;

View File

@@ -0,0 +1,94 @@
using FileTime.App.Core.Models;
namespace FileTime.App.Core.Configuration;
public class CommandBindingConfiguration
{
public List<KeyConfig> Keys { get; set; }
public string Command { get; set; }
public string KeysDisplayText => GetKeysDisplayText();
public CommandBindingConfiguration()
{
Command = null!;
Keys = null!;
}
public CommandBindingConfiguration(string command, IEnumerable<KeyConfig> keys)
{
Keys = new List<KeyConfig>(keys);
Command = command;
}
public CommandBindingConfiguration(string command, KeyConfig key)
{
Keys = new List<KeyConfig> { key };
Command = command;
}
public CommandBindingConfiguration(string command, IEnumerable<Keys> keys)
{
Keys = keys.Select(k => new KeyConfig(k)).ToList();
Command = command;
}
public CommandBindingConfiguration(string command, Keys key)
{
Keys = new List<KeyConfig>() { new(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 = "";
var 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;
}
}

View File

@@ -0,0 +1,8 @@
namespace FileTime.App.Core.Configuration;
public class KeyBindingConfiguration
{
public bool UseDefaultBindings { get; set; } = true;
public List<CommandBindingConfiguration> DefaultKeyBindings { get; set; } = new();
public List<CommandBindingConfiguration> KeyBindings { get; set; } = new();
}

View File

@@ -0,0 +1,31 @@
using FileTime.App.Core.Models;
namespace FileTime.App.Core.Configuration;
public class KeyConfig
{
public Keys Key { get; set; }
public bool Shift { get; set; }
public bool Alt { get; set; }
public bool Ctrl { get; set; }
public KeyConfig() { }
public KeyConfig(
Keys key,
bool shift = false,
bool alt = false,
bool ctrl = false)
{
Key = key;
Shift = shift;
Alt = alt;
Ctrl = ctrl;
}
public bool AreEquals(KeyConfig otherKeyConfig) =>
Key.Equals(otherKeyConfig.Key)
&& Alt == otherKeyConfig.Alt
&& Shift == otherKeyConfig.Shift
&& Ctrl == otherKeyConfig.Ctrl;
}

View File

@@ -0,0 +1,15 @@
namespace FileTime.App.Core.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;
}
}

View File

@@ -0,0 +1,7 @@
namespace FileTime.App.Core.Configuration;
public class ProgramsConfiguration
{
public List<ProgramConfiguration> DefaultEditorPrograms { get; set; } = new();
public List<ProgramConfiguration> EditorPrograms { get; set; } = new();
}

View File

@@ -0,0 +1,7 @@
namespace FileTime.App.Core.Configuration;
public static class SectionNames
{
public const string KeybindingSectionName = "KeyBindings";
public const string ProgramsSectionName = "Programs";
}

View File

@@ -0,0 +1,9 @@
using FileTime.App.Core.Configuration;
namespace FileTime.App.Core.Extensions;
public static class KeyConfigExtensions
{
public static bool AreKeysEqual(this IReadOnlyList<KeyConfig> collection1, IReadOnlyList<KeyConfig> collection2)
=> collection1.Count == collection2.Count && collection1.Zip(collection2).All(t => t.First.AreEquals(t.Second));
}

View File

@@ -0,0 +1,26 @@
namespace FileTime.App.Core.Models;
public class GeneralKeyEventArgs
{
private readonly Action<bool> _handledChanged;
private bool _handled;
public required Keys Key { get; init; }
public bool Handled
{
get => _handled;
set
{
if (_handled != value)
{
_handled = value;
_handledChanged(value);
}
}
}
public GeneralKeyEventArgs(Action<bool> handledChanged)
{
_handledChanged = handledChanged;
}
}

View File

@@ -0,0 +1,58 @@
namespace FileTime.App.Core.Models;
public enum Keys
{
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
Up,
Down,
Left,
Right,
Enter,
Escape,
Back,
Space,
PageUp,
PageDown,
Comma,
Question,
Tab,
LWin,
RWin,
}

View File

@@ -0,0 +1,8 @@
using FileTime.App.Core.Models;
namespace FileTime.App.Core.Services;
public interface IAppKeyService<TKey>
{
Keys? MapKey(TKey key);
}

View File

@@ -0,0 +1,10 @@
using FileTime.App.Core.Configuration;
namespace FileTime.App.Core.Services;
public interface IKeyboardConfigurationService
{
IReadOnlyList<CommandBindingConfiguration> CommandBindings { get; }
IReadOnlyList<CommandBindingConfiguration> UniversalCommandBindings { get; }
IReadOnlyList<CommandBindingConfiguration> AllShortcut { get; }
}

View File

@@ -0,0 +1,7 @@
namespace FileTime.App.Core.Services;
public interface ILifecycleService
{
Task InitStartupHandlersAsync();
Task ExitAsync();
}

View File

@@ -0,0 +1,144 @@
using FileTime.App.Core.Models;
using FileTime.App.Core.UserCommand;
using FileTime.Providers.LocalAdmin;
namespace FileTime.App.Core.Configuration;
public class MainConfiguration
{
private static readonly Lazy<List<CommandBindingConfiguration>> _defaultKeybindings = new(InitDefaultKeyBindings);
public static Dictionary<string, string?> Configuration { get; }
static MainConfiguration()
{
Configuration = new()
{
{AdminElevationConfiguration.SectionName + ":" + nameof(AdminElevationConfiguration.ServerExecutablePath), "FileTime.Server.exe"},
};
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);
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() =>
new List<CommandBindingConfiguration>
{
//new CommandBindingConfiguration(ConfigCommand.AutoRefresh, new KeyConfig(Keys.R, shift: true)),
//new CommandBindingConfiguration(ConfigCommand.ChangeTimelineMode, new[] { Keys.T, Keys.M }),
new(CloseTabCommand.CommandName, Keys.Q),
//new CommandBindingConfiguration(ConfigCommand.Compress, new[] { Keys.Y, Keys.C }),
new(CopyBase64Command.CommandName, new[] {Keys.C, Keys.B}),
new(CopyCommand.CommandName, new[] {Keys.Y, Keys.Y}),
//new CommandBindingConfiguration(ConfigCommand.CopyHash, new[] { Keys.C, Keys.H }),
new(CopyNativePathCommand.CommandName, new[] {Keys.C, Keys.P}),
new(CopyFilesToClipboardCommand.CommandName, new[] {Keys.Y, Keys.C}),
new(CreateContainer.CommandName, Keys.F7),
new(CreateContainer.CommandName, new[] {Keys.C, Keys.C}),
new(CreateElementCommand.CommandName, new[] {Keys.C, Keys.E}),
//new CommandBindingConfiguration(ConfigCommand.Cut, new[] { Keys.D, Keys.D }),
//new CommandBindingConfiguration(ConfigCommand.Edit, new KeyConfig(Keys.F4)),
new(EnterRapidTravelCommand.CommandName, new KeyConfig(Keys.Comma, shift: true)),
new(EnterRapidTravelCommand.CommandName, new KeyConfig(Keys.Question, shift: true)),
new(GoBackCommand.CommandName, new KeyConfig(Keys.Left, alt: true)),
new(GoByFrequencyCommand.CommandName, Keys.Z),
new(GoForwardCommand.CommandName, new KeyConfig(Keys.Right, alt: true)),
new(GoToHomeCommand.CommandName, new[] {Keys.G, Keys.H}),
new(GoToPathCommand.CommandName, new KeyConfig(Keys.L, ctrl: true)),
new(GoToPathCommand.CommandName, new[] {Keys.G, Keys.P}),
new(GoToProviderCommand.CommandName, new[] {Keys.G, Keys.T}),
new(GoToRootCommand.CommandName, new[] {Keys.G, Keys.R}),
new(GoUpCommand.CommandName, Keys.Left),
new(DeleteCommand.HardDeleteCommandName, new[] {new KeyConfig(Keys.D, shift: true), new KeyConfig(Keys.D, shift: true)}),
new(MarkCommand.CommandName, Keys.Space),
new(MoveCursorToLastCommand.CommandName, new KeyConfig(Keys.G, shift: true)),
new(MoveCursorToFirstCommand.CommandName, new[] {Keys.G, Keys.G}),
new(MoveCursorUpCommand.CommandName, Keys.Up),
new(MoveCursorDownCommand.CommandName, Keys.Down),
new(MoveCursorUpPageCommand.CommandName, Keys.PageUp),
new(MoveCursorDownPageCommand.CommandName, Keys.PageDown),
//new CommandBindingConfiguration(ConfigCommand.NextTimelineBlock, Keys.L ),
//new CommandBindingConfiguration(ConfigCommand.NextTimelineCommand, Keys.J ),
new(OpenSelectedCommand.CommandName, Keys.Right),
new(OpenCommandPaletteCommand.CommandName, new[] {Keys.F1}),
new(OpenCommandPaletteCommand.CommandName, new[] {new KeyConfig(Keys.P, ctrl: true, shift: true)}),
new(OpenInDefaultFileExplorerCommand.CommandName, new[] {Keys.O, Keys.E}),
new(PasteCommand.PasteMergeCommandName, new[] {Keys.P, Keys.P}),
new(PasteCommand.PasteOverwriteCommandName, new[] {Keys.P, Keys.O}),
new(PasteCommand.PasteSkipCommandName, new[] {Keys.P, Keys.S}),
new(PasteFilesFromClipboardCommand.PasteMergeCommandName, new[] {new KeyConfig(Keys.V, ctrl: true)}),
new(PasteFilesFromClipboardCommand.PasteOverwriteCommandName, new[] {new KeyConfig(Keys.V, ctrl: true, shift: true)}),
//new CommandBindingConfiguration(ConfigCommand.PinFavorite, new[] { Keys.F, Keys.P }),
//new CommandBindingConfiguration(ConfigCommand.PreviousTimelineBlock, Keys.H ),
//new CommandBindingConfiguration(ConfigCommand.PreviousTimelineCommand, Keys.K ),
new(RefreshCommand.CommandName, Keys.R),
new(RenameCommand.CommandName, Keys.F2),
new(RenameCommand.CommandName, new[] {Keys.C, Keys.W}),
new(IdentifiableRunOrOpenCommand.CommandName, Keys.Enter),
//new CommandBindingConfiguration(ConfigCommand.RunCommand, new KeyConfig(Keys.D4, shift: true)),
//new CommandBindingConfiguration(ConfigCommand.ScanContainerSize, new[] { Keys.C, Keys.S }),
//new CommandBindingConfiguration(ConfigCommand.ShowAllShortcut, Keys.F1),
new(DeleteCommand.SoftDeleteCommandName, new[] {new KeyConfig(Keys.D), new KeyConfig(Keys.D, shift: true)}),
new(IdentifiableSearchCommand.SearchByNameContainsCommandName, new[] {Keys.S, Keys.N}),
new(SwitchToTabCommand.SwitchToLastTabCommandName, new[] {new KeyConfig(Keys.F9, alt: true)}),
new(SwitchToTabCommand.SwitchToTab1CommandName, new[] {new KeyConfig(Keys.F1, alt: true)}),
new(SwitchToTabCommand.SwitchToTab2CommandName, new[] {new KeyConfig(Keys.F2, alt: true)}),
new(SwitchToTabCommand.SwitchToTab3CommandName, new[] {new KeyConfig(Keys.F3, alt: true)}),
new(SwitchToTabCommand.SwitchToTab4CommandName, new[] {new KeyConfig(Keys.F4, alt: true)}),
new(SwitchToTabCommand.SwitchToTab5CommandName, new[] {new KeyConfig(Keys.F5, alt: true)}),
new(SwitchToTabCommand.SwitchToTab6CommandName, new[] {new KeyConfig(Keys.F6, alt: true)}),
new(SwitchToTabCommand.SwitchToTab7CommandName, new[] {new KeyConfig(Keys.F7, alt: true)}),
new(SwitchToTabCommand.SwitchToTab8CommandName, new[] {new KeyConfig(Keys.F8, alt: true)}),
new(PauseCommandSchedulerCommand.CommandName, new[] {Keys.T, Keys.P}),
//new CommandBindingConfiguration(ConfigCommand.TimelineRefresh, new[] { Keys.T, Keys.R }),
new(StartCommandSchedulerCommand.CommandName, new[] {Keys.T, Keys.S}),
//new CommandBindingConfiguration(ConfigCommand.ToggleAdvancedIcons, new[] { Keys.Z, Keys.I }),
};
private static void PopulateDefaultEditorPrograms(Dictionary<string, string?> configuration)
{
var editorPrograms = new List<ProgramConfiguration>()
{
new(@"c:\Program Files\Notepad++\notepad++.exe"),
new("notepad.exe"),
};
for (var i = 0; i < editorPrograms.Count; i++)
{
if (editorPrograms[i].Path is not { } path) continue;
configuration.Add(
$"{SectionNames.ProgramsSectionName}:{nameof(ProgramsConfiguration.DefaultEditorPrograms)}:[{i}]:{nameof(ProgramConfiguration.Path)}",
path);
if (editorPrograms[i].Arguments is { } arguments)
{
configuration.Add(
$"{SectionNames.ProgramsSectionName}:{nameof(ProgramsConfiguration.DefaultEditorPrograms)}:[{i}]:{nameof(ProgramConfiguration.Arguments)}",
arguments);
}
}
}
}

View File

@@ -12,6 +12,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
<PackageReference Include="morelinq" Version="3.4.2" />
<PackageReference Include="MvvmGen" Version="1.2.1" />
<PackageReference Include="ObservableComputations" Version="2.3.0" />
@@ -24,6 +25,7 @@
<ProjectReference Include="..\..\Core\FileTime.Core.Models\FileTime.Core.Models.csproj" />
<ProjectReference Include="..\..\Library\Defer\Defer.csproj" />
<ProjectReference Include="..\..\Providers\FileTime.Providers.Local.Abstractions\FileTime.Providers.Local.Abstractions.csproj" />
<ProjectReference Include="..\..\Providers\FileTime.Providers.LocalAdmin.Abstractions\FileTime.Providers.LocalAdmin.Abstractions.csproj" />
<ProjectReference Include="..\..\Tools\FileTime.Tools\FileTime.Tools.csproj" />
<ProjectReference Include="..\FileTime.App.CommandPalette.Abstractions\FileTime.App.CommandPalette.Abstractions.csproj" />
<ProjectReference Include="..\FileTime.App.CommandPalette\FileTime.App.CommandPalette.csproj" />

View File

@@ -0,0 +1,59 @@
using FileTime.App.Core.Configuration;
using FileTime.App.Core.UserCommand;
using Microsoft.Extensions.Options;
namespace FileTime.App.Core.Services;
public class KeyboardConfigurationService : IKeyboardConfigurationService
{
public IReadOnlyList<CommandBindingConfiguration> CommandBindings { get; }
public IReadOnlyList<CommandBindingConfiguration> UniversalCommandBindings { get; }
public IReadOnlyList<CommandBindingConfiguration> AllShortcut { get; }
public KeyboardConfigurationService(IOptions<KeyBindingConfiguration> keyBindingConfiguration)
{
IEnumerable<CommandBindingConfiguration> keyBindings = keyBindingConfiguration.Value.KeyBindings;
if (keyBindingConfiguration.Value.UseDefaultBindings)
{
keyBindings = keyBindings.Concat(keyBindingConfiguration.Value.DefaultKeyBindings);
}
var commandBindings = new List<CommandBindingConfiguration>();
var universalCommandBindings = new List<CommandBindingConfiguration>();
foreach (var keyBinding in keyBindings)
{
if (string.IsNullOrWhiteSpace(keyBinding.Command))
{
throw new FormatException($"No command is set in keybinding for keys '{keyBinding.KeysDisplayText}'");
}
else if (keyBinding.Keys.Count == 0)
{
throw new FormatException($"No keys set in keybinding for command '{keyBinding.Command}'.");
}
if (IsUniversal(keyBinding))
{
universalCommandBindings.Add(keyBinding);
}
else
{
commandBindings.Add(keyBinding);
}
}
CommandBindings = commandBindings.AsReadOnly();
UniversalCommandBindings = universalCommandBindings.AsReadOnly();
AllShortcut = new List<CommandBindingConfiguration>(UniversalCommandBindings.Concat(CommandBindings)).AsReadOnly();
}
private static bool IsUniversal(CommandBindingConfiguration keyMapping)
=> keyMapping.Command is
GoUpCommand.CommandName
or OpenSelectedCommand.CommandName
or MoveCursorDownCommand.CommandName
or MoveCursorDownPageCommand.CommandName
or MoveCursorUpCommand.CommandName
or MoveCursorUpPageCommand.CommandName
or IdentifiableRunOrOpenCommand.CommandName;
}

View File

@@ -0,0 +1,65 @@
using FileTime.Core.Extensions;
using Microsoft.Extensions.Logging;
namespace FileTime.App.Core.Services;
public class LifecycleService : ILifecycleService
{
private readonly IEnumerable<IExitHandler> _exitHandlers;
private readonly IEnumerable<IStartupHandler> _startupHandlers;
private readonly ILogger<LifecycleService> _logger;
public LifecycleService(
IEnumerable<IStartupHandler> startupHandlers,
IEnumerable<IExitHandler> exitHandlers,
ILogger<LifecycleService> logger)
{
_exitHandlers = exitHandlers;
_startupHandlers = startupHandlers;
_logger = logger;
}
public async Task InitStartupHandlersAsync()
{
foreach (var startupHandler in _startupHandlers)
{
try
{
await startupHandler.InitAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while running startup handler {Handler}", startupHandler?.GetType().FullName);
}
}
}
public async Task ExitAsync()
{
var exitCancellation = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var exitHandlerTasks = _exitHandlers.Select(e =>
{
try
{
return e.ExitAsync(exitCancellation.Token);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while running exit handler {Handler}", e.GetType().FullName);
}
return Task.CompletedTask;
});
try
{
await Task.WhenAll(exitHandlerTasks).TimeoutAfter(10000);
}
catch
{
}
exitCancellation.Cancel();
}
}

View File

@@ -0,0 +1,38 @@
using DynamicData;
using FileTime.App.Core.ViewModels;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.App.Core.Services;
public class ModalService : IModalService
{
private readonly IServiceProvider _serviceProvider;
private readonly SourceList<IModalViewModel> _openModals = new();
public IObservable<IChangeSet<IModalViewModel>> OpenModals { get; }
public event EventHandler? AllModalClosed;
public ModalService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
OpenModals = _openModals.Connect().StartWithEmpty();
}
public void OpenModal(IModalViewModel modalToOpen) => _openModals.Add(modalToOpen);
public void CloseModal(IModalViewModel modalToClose)
{
_openModals.Remove(modalToClose);
if (_openModals.Count == 0)
{
AllModalClosed?.Invoke(this, EventArgs.Empty);
}
}
public T OpenModal<T>() where T : IModalViewModel
{
var modal = _serviceProvider.GetRequiredService<T>();
OpenModal(modal);
return modal;
}
}

View File

@@ -26,6 +26,8 @@ public static class Startup
serviceCollection.TryAddSingleton<ITimelineViewModel, TimelineViewModel>();
serviceCollection.TryAddSingleton<IRefreshSmoothnessCalculator, RefreshSmoothnessCalculator>();
serviceCollection.TryAddSingleton<IItemPreviewProvider, ElementPreviewProvider>();
serviceCollection.TryAddSingleton<ILifecycleService, LifecycleService>();
serviceCollection.TryAddSingleton<IModalService, ModalService>();
return serviceCollection
.AddCommandHandlers()

View File

@@ -12,8 +12,4 @@
<ProjectReference Include="..\FileTime.App.FuzzyPanel.Abstraction\FileTime.App.FuzzyPanel.Abstraction.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.2" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,4 @@
using Avalonia.Input;
using FileTime.App.Core.Models;
using FileTime.App.Core.ViewModels;
using FileTime.App.FuzzyPanel;
@@ -8,5 +8,5 @@ public interface IFrequencyNavigationViewModel : IFuzzyPanelViewModel<string>, I
{
IObservable<bool> ShowWindow { get; }
void Close();
Task<bool> HandleKeyUp(KeyEventArgs keyEventArgs);
Task<bool> HandleKeyUp(GeneralKeyEventArgs keyEventArgs);
}

View File

@@ -1,4 +1,4 @@
using Avalonia.Input;
using FileTime.App.Core.Models;
using FileTime.App.Core.Services;
using FileTime.App.Core.UserCommand;
using FileTime.App.Core.ViewModels;
@@ -30,11 +30,11 @@ public class FrequencyNavigationViewModel : FuzzyPanelViewModel<string>, IFreque
public void Close()
=> _frequencyNavigationService.CloseNavigationWindow();
public async Task<bool> HandleKeyUp(KeyEventArgs keyEventArgs)
public async Task<bool> HandleKeyUp(GeneralKeyEventArgs keyEventArgs)
{
if (keyEventArgs.Handled) return false;
if (keyEventArgs.Key == Key.Enter)
if (keyEventArgs.Key == Keys.Enter)
{
keyEventArgs.Handled = true;
var targetContainer = await _timelessContentProvider.GetItemByFullNameAsync(new FullName(SelectedItem), PointInTime.Present);
@@ -47,14 +47,14 @@ public class FrequencyNavigationViewModel : FuzzyPanelViewModel<string>, IFreque
return false;
}
public override async Task<bool> HandleKeyDown(KeyEventArgs keyEventArgs)
public override async Task<bool> HandleKeyDown(GeneralKeyEventArgs keyEventArgs)
{
if (keyEventArgs.Handled) return false;
var handled = await base.HandleKeyDown(keyEventArgs);
if (handled) return true;
if (keyEventArgs.Key == Key.Escape)
if (keyEventArgs.Key == Keys.Escape)
{
keyEventArgs.Handled = true;
Close();

View File

@@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.2" />
<ProjectReference Include="..\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,4 @@
using Avalonia.Input;
using FileTime.App.Core.Models;
namespace FileTime.App.FuzzyPanel;
@@ -8,5 +8,5 @@ public interface IFuzzyPanelViewModel<TItem> where TItem : class
TItem? SelectedItem { get; }
string SearchText { get; set; }
void UpdateFilteredMatches();
Task<bool> HandleKeyDown(KeyEventArgs keyEventArgs);
Task<bool> HandleKeyDown(GeneralKeyEventArgs keyEventArgs);
}

View File

@@ -7,7 +7,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.2" />
<PackageReference Include="PropertyChanged.SourceGenerator" Version="1.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -1,5 +1,5 @@
using System.ComponentModel;
using Avalonia.Input;
using FileTime.App.Core.Models;
using PropertyChanged.SourceGenerator;
namespace FileTime.App.FuzzyPanel;
@@ -44,9 +44,9 @@ public abstract partial class FuzzyPanelViewModel<TItem> : IFuzzyPanelViewModel<
public abstract void UpdateFilteredMatches();
public virtual Task<bool> HandleKeyDown(KeyEventArgs keyEventArgs)
public virtual Task<bool> HandleKeyDown(GeneralKeyEventArgs keyEventArgs)
{
if (keyEventArgs.Key == Key.Down)
if (keyEventArgs.Key == Keys.Down)
{
var nextItem = SelectedItem is null
? FilteredMatches.FirstOrDefault()
@@ -60,7 +60,7 @@ public abstract partial class FuzzyPanelViewModel<TItem> : IFuzzyPanelViewModel<
return Task.FromResult(true);
}
else if (keyEventArgs.Key == Key.Up)
else if (keyEventArgs.Key == Keys.Up)
{
var previousItem = SelectedItem is null
? FilteredMatches.LastOrDefault()