MainPageViewModel refactor, keybindings from config
This commit is contained in:
@@ -2,34 +2,58 @@ namespace FileTime.App.Core.Command
|
||||
{
|
||||
public enum Commands
|
||||
{
|
||||
None,
|
||||
|
||||
AutoRefresh,
|
||||
ChangeTimelineMode,
|
||||
CloseTab,
|
||||
Copy,
|
||||
CopyPath,
|
||||
CreateContainer,
|
||||
CreateElement,
|
||||
Cut,
|
||||
EnterRapidTravel,
|
||||
GoToHome,
|
||||
GoToPath,
|
||||
GoToProvider,
|
||||
GoToRoot,
|
||||
GoUp,
|
||||
Delete,
|
||||
HardDelete,
|
||||
Mark,
|
||||
MoveCursorDown,
|
||||
MoveCursorDownPage,
|
||||
MoveCursorUp,
|
||||
MoveCursorUpPage,
|
||||
MoveToBottom,
|
||||
MoveToFirst,
|
||||
MoveToLast,
|
||||
MoveToTop,
|
||||
NextTimelineBlock,
|
||||
NextTimelineCommand,
|
||||
Open,
|
||||
OpenInFileBrowser,
|
||||
OpenOrRun,
|
||||
PasteMerge,
|
||||
PasteOverwrite,
|
||||
PasteSkip,
|
||||
Select,
|
||||
ToggleHidden,
|
||||
Rename,
|
||||
Dummy,
|
||||
PreviousTimelineBlock,
|
||||
PreviousTimelineCommand,
|
||||
Refresh,
|
||||
Rename,
|
||||
RunCommand,
|
||||
ShowAllShotcut,
|
||||
SoftDelete,
|
||||
SwitchToLastTab,
|
||||
SwitchToTab1,
|
||||
SwitchToTab2,
|
||||
SwitchToTab3,
|
||||
SwitchToTab4,
|
||||
SwitchToTab5,
|
||||
SwitchToTab6,
|
||||
SwitchToTab7,
|
||||
SwitchToTab8,
|
||||
TimelinePause,
|
||||
TimelineRefresh,
|
||||
TimelineStart,
|
||||
ToggleAdvancedIcons,
|
||||
ToggleHidden,
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ namespace FileTime.ConsoleUI.App
|
||||
new CommandBinding("open", Commands.Open, new[] { new ConsoleKeyInfo('→', ConsoleKey.RightArrow, false, false, false) }, Open),
|
||||
new CommandBinding(
|
||||
"go to top",
|
||||
Commands.MoveToTop,
|
||||
Commands.MoveToFirst,
|
||||
new[]
|
||||
{
|
||||
new ConsoleKeyInfo('g', ConsoleKey.G, false, false, false),
|
||||
@@ -99,7 +99,7 @@ namespace FileTime.ConsoleUI.App
|
||||
MoveCursorToTop),
|
||||
new CommandBinding(
|
||||
"go to bottom",
|
||||
Commands.MoveToBottom,
|
||||
Commands.MoveToLast,
|
||||
new[]
|
||||
{
|
||||
new ConsoleKeyInfo('G', ConsoleKey.G, true, false, false)
|
||||
@@ -114,7 +114,7 @@ namespace FileTime.ConsoleUI.App
|
||||
new ConsoleKeyInfo('h', ConsoleKey.H, false, false, false)
|
||||
},
|
||||
ToggleHidden),
|
||||
new CommandBinding("select", Commands.Select, new[] { new ConsoleKeyInfo(' ', ConsoleKey.Spacebar, false, false, false) }, Select),
|
||||
new CommandBinding("select", Commands.Mark, new[] { new ConsoleKeyInfo(' ', ConsoleKey.Spacebar, false, false, false) }, Select),
|
||||
new CommandBinding(
|
||||
"copy",
|
||||
Commands.Copy,
|
||||
|
||||
@@ -134,6 +134,7 @@
|
||||
<converters:ItemViewModelIsAttibuteTypeConverter x:Key="ItemViewModelIsAttibuteTypeConverter"/>
|
||||
<converters:ItemViewModelIsAttibuteTypeConverter x:Key="ItemViewModelIsNotAttibuteTypeConverter" Invert="true"/>
|
||||
<converters:GetFileExtensionConverter x:Key="GetFileExtensionConverter"/>
|
||||
<converters:CommandToCommandNameConverter x:Key="CommandToCommandNameConverter"/>
|
||||
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
@@ -186,15 +187,5 @@
|
||||
<Style Selector="Border.SelectedTimelineCommand">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ForegroundBrush}"/>
|
||||
</Style>
|
||||
|
||||
<!--Style Selector="MenuItem">
|
||||
<Setter Property="Template">
|
||||
<DataTemplate>
|
||||
<Grid ColumnDefinitions="20,*">
|
||||
<TextBlock Grid.Column="1" Text="{Binding Header}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</Setter>
|
||||
</Style-->
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
|
||||
@@ -7,6 +7,10 @@ using FileTime.App.Core.Tab;
|
||||
using System.Threading.Tasks;
|
||||
using FileTime.Core.Models;
|
||||
using System.Threading;
|
||||
using FileTime.Avalonia.Configuration;
|
||||
using FileTime.Avalonia.Misc;
|
||||
using FileTime.Core.Extensions;
|
||||
using FileTime.Avalonia.ViewModels;
|
||||
|
||||
namespace FileTime.Avalonia.Application
|
||||
{
|
||||
@@ -26,6 +30,28 @@ namespace FileTime.Avalonia.Application
|
||||
[Property]
|
||||
private string _rapidTravelText = "";
|
||||
|
||||
[Property]
|
||||
private List<CommandBindingConfiguration> _possibleCommands = new();
|
||||
|
||||
[Property]
|
||||
private List<InputElementWrapper> _inputs;
|
||||
|
||||
[Property]
|
||||
private string _messageBoxText;
|
||||
|
||||
[Property]
|
||||
private ObservableCollection<string> _popupTexts = new();
|
||||
|
||||
[Property]
|
||||
private bool _isAllShortcutVisible;
|
||||
|
||||
[Property]
|
||||
private bool _noCommandFound;
|
||||
|
||||
public List<KeyConfig> PreviousKeys { get; } = new();
|
||||
|
||||
public ObservableCollection<ParallelCommandsViewModel> TimelineCommands { get; } = new();
|
||||
|
||||
partial void OnInitialize()
|
||||
{
|
||||
_tabs.CollectionChanged += TabsChanged;
|
||||
@@ -99,5 +125,16 @@ namespace FileTime.Avalonia.Application
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ExitRapidTravelMode()
|
||||
{
|
||||
ViewMode = ViewMode.Default;
|
||||
|
||||
PreviousKeys.Clear();
|
||||
PossibleCommands = new();
|
||||
RapidTravelText = "";
|
||||
|
||||
await SelectedTab.OpenContainer(await SelectedTab.CurrentLocation.Container.WithoutVirtualContainer(MainPageViewModel.RAPIDTRAVEL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using FileTime.App.Core.Command;
|
||||
/* using FileTime.App.Core.Command;
|
||||
using FileTime.Avalonia.Misc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -14,11 +14,11 @@ namespace FileTime.Avalonia.Command
|
||||
|
||||
public string Name { get; }
|
||||
public Commands? Command { get; }
|
||||
public KeyWithModifiers[] Keys { get; }
|
||||
public KeyConfig[] Keys { get; }
|
||||
|
||||
public string KeysDisplayText => GetKeysDisplayText();
|
||||
|
||||
public CommandBinding(string name, Commands? command, KeyWithModifiers[] keys, Func<Task> commandHandler)
|
||||
public CommandBinding(string name, Commands? command, KeyConfig[] keys, Func<Task> commandHandler)
|
||||
{
|
||||
_commandHandler = commandHandler;
|
||||
Name = name;
|
||||
@@ -48,7 +48,7 @@ namespace FileTime.Avalonia.Command
|
||||
return s;
|
||||
}
|
||||
|
||||
private static string AddKeyWithCtrlOrAlt(KeyWithModifiers key, string currentText, Func<KeyWithModifiers, string, bool, string> keyProcessor)
|
||||
private static string AddKeyWithCtrlOrAlt(KeyConfig key, string currentText, Func<KeyConfig, string, bool, string> keyProcessor)
|
||||
{
|
||||
var s = "";
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace FileTime.Avalonia.Command
|
||||
return s;
|
||||
}
|
||||
|
||||
private static string AddSpecialKey(KeyWithModifiers key, string currentText, bool wasCtrlOrAlt)
|
||||
private static string AddSpecialKey(KeyConfig key, string currentText, bool wasCtrlOrAlt)
|
||||
{
|
||||
var s = "";
|
||||
|
||||
@@ -77,3 +77,4 @@ namespace FileTime.Avalonia.Command
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Avalonia.Input;
|
||||
using FileTime.App.Core.Command;
|
||||
|
||||
namespace FileTime.Avalonia.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.Avalonia.Configuration
|
||||
{
|
||||
public class KeyBindingConfiguration
|
||||
{
|
||||
public bool UseDefaultBindings { get; set; } = true;
|
||||
public List<CommandBindingConfiguration> DefaultKeyBindings { get; set; } = new();
|
||||
public List<CommandBindingConfiguration> KeyBindings { get; set; } = new();
|
||||
}
|
||||
}
|
||||
22
src/GuiApp/FileTime.Avalonia/Configuration/KeyConfig.cs
Normal file
22
src/GuiApp/FileTime.Avalonia/Configuration/KeyConfig.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Avalonia.Input;
|
||||
|
||||
namespace FileTime.Avalonia.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,101 @@
|
||||
using Avalonia.Input;
|
||||
using FileTime.App.Core.Command;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FileTime.Avalonia.Configuration
|
||||
{
|
||||
public static class MainConfiguration
|
||||
{
|
||||
public static readonly Dictionary<string, string> Configuration = new();
|
||||
private static readonly Lazy<List<CommandBindingConfiguration>> _defaultKeybindings = new(InitDefaultKeyBindings);
|
||||
internal const string KeybindingBaseConfigKey = "KeyBindings";
|
||||
|
||||
public static Dictionary<string, string> Configuration { get; }
|
||||
|
||||
static MainConfiguration()
|
||||
{
|
||||
Configuration = new();
|
||||
PopulateDefaultKeyBindings(Configuration, _defaultKeybindings.Value, KeybindingBaseConfigKey + ":" + nameof(KeyBindingConfiguration.DefaultKeyBindings));
|
||||
}
|
||||
|
||||
private static void PopulateDefaultKeyBindings(Dictionary<string, string> keybindings, List<CommandBindingConfiguration> commandBindingConfigs, string basePath)
|
||||
{
|
||||
for (var i = 0; i < commandBindingConfigs.Count; i++)
|
||||
{
|
||||
var baseKey = basePath + $":[{i}]:";
|
||||
var commandBindingConfig = commandBindingConfigs[i];
|
||||
keybindings.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}]:";
|
||||
keybindings.Add(keyBaseKey + nameof(KeyConfig.Key), key.Key.ToString());
|
||||
keybindings.Add(keyBaseKey + nameof(KeyConfig.Shift), key.Shift.ToString());
|
||||
keybindings.Add(keyBaseKey + nameof(KeyConfig.Alt), key.Alt.ToString());
|
||||
keybindings.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.Copy, new[] { Key.Y, Key.Y }),
|
||||
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.EnterRapidTravel, new KeyConfig(Key.OemComma, shift: true)),
|
||||
new CommandBindingConfiguration(Commands.GoToHome, new[] { Key.G, Key.H }),
|
||||
new CommandBindingConfiguration(Commands.GoToPath, new KeyConfig(Key.OemComma, 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.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.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),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using FileTime.App.Core.Command;
|
||||
|
||||
namespace FileTime.Avalonia.Converters
|
||||
{
|
||||
public class CommandToCommandNameConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if(value is not Commands command) return value;
|
||||
|
||||
//TODO: implement
|
||||
return command.ToString();
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using Avalonia.Input;
|
||||
|
||||
namespace FileTime.Avalonia.Misc
|
||||
{
|
||||
public class KeyWithModifiers
|
||||
{
|
||||
public Key Key { get; }
|
||||
|
||||
public bool? Alt { get; }
|
||||
public bool? Shift { get; }
|
||||
public bool? Ctrl { get; }
|
||||
|
||||
public KeyWithModifiers(Key key, bool alt = false, bool shift = false, bool ctrl = false)
|
||||
{
|
||||
Key = key;
|
||||
Alt = alt;
|
||||
Shift = shift;
|
||||
Ctrl = ctrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
783
src/GuiApp/FileTime.Avalonia/Services/CommandHandlerService.cs
Normal file
783
src/GuiApp/FileTime.Avalonia/Services/CommandHandlerService.cs
Normal file
@@ -0,0 +1,783 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Threading;
|
||||
using FileTime.App.Core.Clipboard;
|
||||
using FileTime.App.Core.Command;
|
||||
using FileTime.Avalonia.Application;
|
||||
using FileTime.Avalonia.IconProviders;
|
||||
using FileTime.Avalonia.Misc;
|
||||
using FileTime.Avalonia.ViewModels;
|
||||
using FileTime.Core.Command;
|
||||
using FileTime.Core.Components;
|
||||
using FileTime.Core.Interactions;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
using FileTime.Core.Timeline;
|
||||
using FileTime.Providers.Local;
|
||||
|
||||
namespace FileTime.Avalonia.Services
|
||||
{
|
||||
public class CommandHandlerService
|
||||
{
|
||||
private bool _addCommandToNextBatch;
|
||||
|
||||
private readonly AppState _appState;
|
||||
private readonly LocalContentProvider _localContentProvider;
|
||||
private readonly ItemNameConverterService _itemNameConverterService;
|
||||
private readonly DialogService _dialogService;
|
||||
private readonly IClipboard _clipboard;
|
||||
private readonly TimeRunner _timeRunner;
|
||||
private readonly IIconProvider _iconProvider;
|
||||
private readonly IEnumerable<IContentProvider> _contentProviders;
|
||||
private readonly Dictionary<Commands, Func<Task>> _commandHandlers;
|
||||
|
||||
public CommandHandlerService(
|
||||
AppState appState,
|
||||
LocalContentProvider localContentProvider,
|
||||
ItemNameConverterService itemNameConverterService,
|
||||
DialogService dialogService,
|
||||
IClipboard clipboard,
|
||||
TimeRunner timeRunner,
|
||||
IIconProvider iconProvider,
|
||||
IEnumerable<IContentProvider> contentProviders)
|
||||
{
|
||||
_appState = appState;
|
||||
_localContentProvider = localContentProvider;
|
||||
_itemNameConverterService = itemNameConverterService;
|
||||
_dialogService = dialogService;
|
||||
_clipboard = clipboard;
|
||||
_timeRunner = timeRunner;
|
||||
_iconProvider = iconProvider;
|
||||
_contentProviders = contentProviders;
|
||||
|
||||
_commandHandlers = new Dictionary<Commands, Func<Task>>
|
||||
{
|
||||
{Commands.AutoRefresh, ToggleAutoRefresh},
|
||||
{Commands.ChangeTimelineMode, ChangeTimelineMode},
|
||||
{Commands.CloseTab, CloseTab},
|
||||
{Commands.Copy, Copy},
|
||||
{Commands.CopyPath, CopyPath},
|
||||
{Commands.CreateContainer, CreateContainer},
|
||||
{Commands.CreateElement, CreateElement},
|
||||
{Commands.Cut, Cut},
|
||||
{Commands.EnterRapidTravel, EnterRapidTravelMode},
|
||||
{Commands.GoToHome, GotToHome},
|
||||
{Commands.GoToPath, GoToContainer},
|
||||
{Commands.GoToProvider, GotToProvider},
|
||||
{Commands.GoToRoot, GotToRoot},
|
||||
{Commands.GoUp, GoUp},
|
||||
{Commands.HardDelete, HardDelete},
|
||||
{Commands.Mark, MarkCurrentItem},
|
||||
{Commands.MoveCursorDown, MoveCursorDown},
|
||||
{Commands.MoveCursorDownPage, MoveCursorDownPage},
|
||||
{Commands.MoveCursorUp, MoveCursorUp},
|
||||
{Commands.MoveCursorUpPage, MoveCursorUpPage},
|
||||
{Commands.MoveToFirst, MoveToFirst},
|
||||
{Commands.MoveToLast, MoveToLast},
|
||||
{Commands.NextTimelineBlock, SelectNextTimelineBlock},
|
||||
{Commands.NextTimelineCommand, SelectNextTimelineCommand},
|
||||
{Commands.Open, OpenContainer},
|
||||
{Commands.OpenInFileBrowser, OpenInDefaultFileExplorer},
|
||||
{Commands.OpenOrRun, OpenOrRun},
|
||||
{Commands.PasteMerge, PasteMerge},
|
||||
{Commands.PasteOverwrite, PasteOverwrite},
|
||||
{Commands.PasteSkip, PasteSkip},
|
||||
{Commands.PreviousTimelineBlock, SelectPreviousTimelineBlock},
|
||||
{Commands.PreviousTimelineCommand, SelectPreviousTimelineCommand},
|
||||
{Commands.Refresh, RefreshCurrentLocation},
|
||||
{Commands.Rename, Rename},
|
||||
{Commands.RunCommand, RunCommandInContainer},
|
||||
{Commands.ShowAllShotcut, ShowAllShortcut},
|
||||
{Commands.SoftDelete, SoftDelete},
|
||||
{Commands.SwitchToLastTab, async() => await SwitchToTab(-1)},
|
||||
{Commands.SwitchToTab1, async() => await SwitchToTab(1)},
|
||||
{Commands.SwitchToTab2, async() => await SwitchToTab(2)},
|
||||
{Commands.SwitchToTab3, async() => await SwitchToTab(3)},
|
||||
{Commands.SwitchToTab4, async() => await SwitchToTab(4)},
|
||||
{Commands.SwitchToTab5, async() => await SwitchToTab(5)},
|
||||
{Commands.SwitchToTab6, async() => await SwitchToTab(6)},
|
||||
{Commands.SwitchToTab7, async() => await SwitchToTab(7)},
|
||||
{Commands.SwitchToTab8, async() => await SwitchToTab(8)},
|
||||
{Commands.TimelinePause, PauseTimeline},
|
||||
{Commands.TimelineRefresh, RefreshTimeline},
|
||||
{Commands.TimelineStart, ContinueTimeline},
|
||||
{Commands.ToggleAdvancedIcons, ToggleAdvancedIcons},
|
||||
{Commands.ToggleHidden, ToggleHidden},
|
||||
};
|
||||
}
|
||||
|
||||
public async Task HandleCommandAsync(Commands command) =>
|
||||
await _commandHandlers[command].Invoke();
|
||||
|
||||
private async Task OpenContainer()
|
||||
{
|
||||
_appState.RapidTravelText = "";
|
||||
await _appState.SelectedTab.Open();
|
||||
}
|
||||
|
||||
public async Task OpenContainer(IContainer container)
|
||||
{
|
||||
_appState.RapidTravelText = "";
|
||||
await _appState.SelectedTab.OpenContainer(container);
|
||||
}
|
||||
|
||||
private async Task OpenOrRun()
|
||||
{
|
||||
if (_appState.SelectedTab.SelectedItem is ContainerViewModel)
|
||||
{
|
||||
await OpenContainer();
|
||||
}
|
||||
else if (_appState.SelectedTab.SelectedItem is ElementViewModel elementViewModel && elementViewModel.Element is LocalFile localFile)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(localFile.File.FullName) { UseShellExecute = true });
|
||||
|
||||
if (_appState.ViewMode == ViewMode.RapidTravel)
|
||||
{
|
||||
await _appState.ExitRapidTravelMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GoUp()
|
||||
{
|
||||
await _appState.SelectedTab.GoUp();
|
||||
}
|
||||
|
||||
private async Task MoveCursorUp()
|
||||
{
|
||||
await _appState.SelectedTab.MoveCursorUp();
|
||||
}
|
||||
|
||||
private async Task MoveCursorDown()
|
||||
{
|
||||
await _appState.SelectedTab.MoveCursorDown();
|
||||
}
|
||||
|
||||
private async Task MoveCursorUpPage()
|
||||
{
|
||||
await _appState.SelectedTab.MoveCursorUpPage();
|
||||
}
|
||||
|
||||
private async Task MoveCursorDownPage()
|
||||
{
|
||||
await _appState.SelectedTab.MoveCursorDownPage();
|
||||
}
|
||||
|
||||
private async Task MoveToFirst()
|
||||
{
|
||||
await _appState.SelectedTab.MoveCursorToFirst();
|
||||
}
|
||||
|
||||
private async Task MoveToLast()
|
||||
{
|
||||
await _appState.SelectedTab.MoveCursorToLast();
|
||||
}
|
||||
|
||||
private async Task GotToProvider()
|
||||
{
|
||||
await _appState.SelectedTab.GotToProvider();
|
||||
}
|
||||
|
||||
private async Task GotToRoot()
|
||||
{
|
||||
await _appState.SelectedTab.GotToRoot();
|
||||
}
|
||||
|
||||
private async Task GotToHome()
|
||||
{
|
||||
await _appState.SelectedTab.GotToHome();
|
||||
}
|
||||
|
||||
private Task EnterRapidTravelMode()
|
||||
{
|
||||
_appState.ViewMode = ViewMode.RapidTravel;
|
||||
|
||||
_appState.PreviousKeys.Clear();
|
||||
_appState.PossibleCommands = new();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task SwitchToTab(int number)
|
||||
{
|
||||
var tabContainer = _appState.Tabs.FirstOrDefault(t => t.TabNumber == number);
|
||||
|
||||
if (number == -1)
|
||||
{
|
||||
var greatestNumber = _appState.Tabs.Max(t => t.TabNumber);
|
||||
tabContainer = _appState.Tabs.FirstOrDefault(t => t.TabNumber == greatestNumber);
|
||||
}
|
||||
else if (tabContainer == null)
|
||||
{
|
||||
var newContainer = await _appState.SelectedTab.CurrentLocation.Container.Clone();
|
||||
|
||||
var newTab = new Tab();
|
||||
await newTab.Init(newContainer);
|
||||
|
||||
tabContainer = new TabContainer(newTab, _localContentProvider, _itemNameConverterService);
|
||||
await tabContainer.Init(number);
|
||||
|
||||
var i = 0;
|
||||
for (i = 0; i < _appState.Tabs.Count; i++)
|
||||
{
|
||||
if (_appState.Tabs[i].TabNumber > number) break;
|
||||
}
|
||||
_appState.Tabs.Insert(i, tabContainer);
|
||||
}
|
||||
|
||||
if (_appState.ViewMode == ViewMode.RapidTravel)
|
||||
{
|
||||
await _appState.ExitRapidTravelMode();
|
||||
}
|
||||
|
||||
_appState.SelectedTab = tabContainer;
|
||||
}
|
||||
|
||||
private async Task CloseTab()
|
||||
{
|
||||
var tabs = _appState.Tabs;
|
||||
if (tabs.Count > 1)
|
||||
{
|
||||
var currentTab = tabs.FirstOrDefault(t => t == _appState.SelectedTab);
|
||||
|
||||
if (currentTab != null)
|
||||
{
|
||||
tabs.Remove(currentTab);
|
||||
var tabNumber = tabs[0].TabNumber;
|
||||
for (var i = 0; i < tabs.Count; i++)
|
||||
{
|
||||
tabNumber = tabs[i].TabNumber;
|
||||
if (tabs[i].TabNumber > currentTab.TabNumber) break;
|
||||
}
|
||||
await SwitchToTab(tabNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task CreateContainer()
|
||||
{
|
||||
var handler = async (List<InputElementWrapper> inputs) =>
|
||||
{
|
||||
var container = _appState.SelectedTab.CurrentLocation.Container;
|
||||
var createContainerCommand = new CreateContainerCommand(new AbsolutePath(container), inputs[0].Value);
|
||||
await AddCommand(createContainerCommand);
|
||||
};
|
||||
|
||||
_dialogService.ReadInputs(new List<InputElement>() { new InputElement("Container name", InputType.Text) }, handler);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task CreateElement()
|
||||
{
|
||||
var handler = async (List<InputElementWrapper> inputs) =>
|
||||
{
|
||||
var container = _appState.SelectedTab.CurrentLocation.Container;
|
||||
var createElementCommand = new CreateElementCommand(new AbsolutePath(container), inputs[0].Value);
|
||||
await AddCommand(createElementCommand);
|
||||
};
|
||||
|
||||
_dialogService.ReadInputs(new List<InputElement>() { new InputElement("Element name", InputType.Text) }, handler);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task MarkCurrentItem()
|
||||
{
|
||||
await _appState.SelectedTab.MarkCurrentItem();
|
||||
}
|
||||
|
||||
private async Task Copy()
|
||||
{
|
||||
_clipboard.Clear();
|
||||
_clipboard.SetCommand<CopyCommand>();
|
||||
|
||||
var currentSelectedItems = await _appState.SelectedTab.TabState.GetCurrentMarkedItems();
|
||||
if (currentSelectedItems.Count > 0)
|
||||
{
|
||||
foreach (var selectedItem in currentSelectedItems)
|
||||
{
|
||||
_clipboard.AddContent(selectedItem);
|
||||
}
|
||||
await _appState.SelectedTab.TabState.ClearCurrentMarkedItems();
|
||||
}
|
||||
else
|
||||
{
|
||||
var currentSelectedItem = _appState.SelectedTab.SelectedItem?.Item;
|
||||
if (currentSelectedItem != null)
|
||||
{
|
||||
_clipboard.AddContent(new AbsolutePath(currentSelectedItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task Cut()
|
||||
{
|
||||
_clipboard.Clear();
|
||||
_clipboard.SetCommand<MoveCommand>();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task SoftDelete() => await Delete(false);
|
||||
|
||||
private async Task HardDelete() => await Delete(true);
|
||||
|
||||
public async Task Delete(bool hardDelete = false)
|
||||
{
|
||||
IList<AbsolutePath>? itemsToDelete = null;
|
||||
var askForDelete = false;
|
||||
var questionText = "";
|
||||
var shouldDelete = false;
|
||||
var shouldClearMarkedItems = false;
|
||||
|
||||
var currentSelectedItems = await _appState.SelectedTab.TabState.GetCurrentMarkedItems();
|
||||
var currentSelectedItem = _appState.SelectedTab.SelectedItem?.Item;
|
||||
if (currentSelectedItems.Count > 0)
|
||||
{
|
||||
itemsToDelete = new List<AbsolutePath>(currentSelectedItems);
|
||||
shouldClearMarkedItems = true;
|
||||
|
||||
//FIXME: check 'is Container'
|
||||
if (currentSelectedItems.Count == 1)
|
||||
{
|
||||
if ((await currentSelectedItems[0].Resolve()) is IContainer container
|
||||
&& (await container.GetItems())?.Count > 0)
|
||||
{
|
||||
askForDelete = true;
|
||||
questionText = $"The container '{container.Name}' is not empty. Proceed with delete?";
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldDelete = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
askForDelete = true;
|
||||
questionText = $"Are you sure you want to delete {itemsToDelete.Count} item?";
|
||||
}
|
||||
}
|
||||
else if (currentSelectedItem != null)
|
||||
{
|
||||
itemsToDelete = new List<AbsolutePath>()
|
||||
{
|
||||
new AbsolutePath(currentSelectedItem)
|
||||
};
|
||||
|
||||
if (currentSelectedItem is IContainer container && (await container.GetItems())?.Count > 0)
|
||||
{
|
||||
askForDelete = true;
|
||||
questionText = $"The container '{container.Name}' is not empty. Proceed with delete?";
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldDelete = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemsToDelete?.Count > 0)
|
||||
{
|
||||
if (askForDelete)
|
||||
{
|
||||
_dialogService.ShowMessageBox(questionText, HandleDelete);
|
||||
}
|
||||
else if (shouldDelete)
|
||||
{
|
||||
await HandleDelete();
|
||||
}
|
||||
}
|
||||
|
||||
async Task HandleDelete()
|
||||
{
|
||||
var deleteCommand = new DeleteCommand
|
||||
{
|
||||
HardDelete = hardDelete
|
||||
};
|
||||
|
||||
foreach (var itemToDelete in itemsToDelete!)
|
||||
{
|
||||
deleteCommand.ItemsToDelete.Add(itemToDelete);
|
||||
}
|
||||
|
||||
await AddCommand(deleteCommand);
|
||||
_clipboard.Clear();
|
||||
if (shouldClearMarkedItems)
|
||||
{
|
||||
await _appState.SelectedTab.TabState.ClearCurrentMarkedItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PasteMerge()
|
||||
{
|
||||
await Paste(TransportMode.Merge);
|
||||
}
|
||||
private async Task PasteOverwrite()
|
||||
{
|
||||
await Paste(TransportMode.Overwrite);
|
||||
}
|
||||
|
||||
private async Task PasteSkip()
|
||||
{
|
||||
await Paste(TransportMode.Skip);
|
||||
}
|
||||
|
||||
private async Task Paste(TransportMode transportMode)
|
||||
{
|
||||
if (_clipboard.CommandType != null)
|
||||
{
|
||||
var command = (ITransportationCommand)Activator.CreateInstance(_clipboard.CommandType!)!;
|
||||
command.TransportMode = transportMode;
|
||||
|
||||
command.Sources.Clear();
|
||||
|
||||
foreach (var item in _clipboard.Content)
|
||||
{
|
||||
command.Sources.Add(item);
|
||||
}
|
||||
|
||||
var currentLocation = _appState.SelectedTab.CurrentLocation.Container;
|
||||
command.Target = currentLocation is VirtualContainer virtualContainer
|
||||
? virtualContainer.BaseContainer
|
||||
: currentLocation;
|
||||
|
||||
await AddCommand(command);
|
||||
|
||||
_clipboard.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private Task Rename()
|
||||
{
|
||||
var selectedItem = _appState.SelectedTab.SelectedItem?.Item;
|
||||
if (selectedItem != null)
|
||||
{
|
||||
var handler = async (List<InputElementWrapper> inputs) =>
|
||||
{
|
||||
var renameCommand = new RenameCommand(new AbsolutePath(selectedItem), inputs[0].Value);
|
||||
await AddCommand(renameCommand);
|
||||
};
|
||||
|
||||
_dialogService.ReadInputs(new List<InputElement>() { new InputElement("New name", InputType.Text, selectedItem.Name) }, handler);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task RefreshCurrentLocation()
|
||||
{
|
||||
await _appState.SelectedTab.CurrentLocation.Container.RefreshAsync();
|
||||
await _appState.SelectedTab.UpdateCurrentSelectedItem();
|
||||
}
|
||||
|
||||
private Task PauseTimeline()
|
||||
{
|
||||
_timeRunner.EnableRunning = false;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task ContinueTimeline()
|
||||
{
|
||||
_timeRunner.EnableRunning = true;
|
||||
await _timeRunner.TryStartCommandRunner();
|
||||
}
|
||||
|
||||
private async Task RefreshTimeline()
|
||||
{
|
||||
await _timeRunner.Refresh();
|
||||
}
|
||||
|
||||
private Task ChangeTimelineMode()
|
||||
{
|
||||
_addCommandToNextBatch = !_addCommandToNextBatch;
|
||||
_dialogService.ShowToastMessage("Timeline mode: " + (_addCommandToNextBatch ? "Continuous" : "Parallel"));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task GoToContainer()
|
||||
{
|
||||
var handler = async (List<InputElementWrapper> inputs) =>
|
||||
{
|
||||
var path = inputs[0].Value;
|
||||
foreach (var contentProvider in _contentProviders)
|
||||
{
|
||||
if (contentProvider.CanHandlePath(path))
|
||||
{
|
||||
var possibleContainer = await contentProvider.GetByPath(path);
|
||||
if (possibleContainer is IContainer container)
|
||||
{
|
||||
await _appState.SelectedTab.OpenContainer(container);
|
||||
}
|
||||
//TODO: multiple possible content provider handler
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_dialogService.ReadInputs(new List<InputElement>() { new InputElement("Path", InputType.Text) }, handler);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task ToggleAdvancedIcons()
|
||||
{
|
||||
_iconProvider.EnableAdvancedIcons = !_iconProvider.EnableAdvancedIcons;
|
||||
_dialogService.ShowToastMessage("Advanced icons are: " + (_iconProvider.EnableAdvancedIcons ? "ON" : "OFF"));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task ToggleHidden()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Task OpenInDefaultFileExplorer()
|
||||
{
|
||||
if (_appState.SelectedTab.CurrentLocation.Container is LocalFolder localFolder)
|
||||
{
|
||||
var path = localFolder.Directory.FullName;
|
||||
if (path != null)
|
||||
{
|
||||
Process.Start("explorer.exe", "\"" + path + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task CopyPath()
|
||||
{
|
||||
string? textToCopy = null;
|
||||
if (_appState.SelectedTab.CurrentLocation.Container is LocalFolder localFolder)
|
||||
{
|
||||
textToCopy = localFolder.Directory.FullName;
|
||||
}
|
||||
if (_appState.SelectedTab.CurrentLocation.Container is LocalFile localFile)
|
||||
{
|
||||
textToCopy = localFile.File.FullName;
|
||||
}
|
||||
else if (_appState.SelectedTab.CurrentLocation.Container.FullName is string fullName)
|
||||
{
|
||||
textToCopy = fullName;
|
||||
}
|
||||
|
||||
if (textToCopy != null && global::Avalonia.Application.Current?.Clipboard is not null)
|
||||
{
|
||||
await global::Avalonia.Application.Current.Clipboard.SetTextAsync(textToCopy);
|
||||
}
|
||||
}
|
||||
|
||||
private Task ShowAllShortcut()
|
||||
{
|
||||
_appState.IsAllShortcutVisible = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task RunCommandInContainer()
|
||||
{
|
||||
var handler = (List<InputElementWrapper> inputs) =>
|
||||
{
|
||||
var input = inputs[0].Value;
|
||||
string? path = null;
|
||||
string? arguments = null;
|
||||
|
||||
if (input.StartsWith("\""))
|
||||
{
|
||||
var pathEnd = input.IndexOf('\"', 1);
|
||||
|
||||
path = input.Substring(1, pathEnd);
|
||||
arguments = input.Substring(pathEnd + 1).Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
var inputParts = input.Split(' ');
|
||||
path = inputParts[0];
|
||||
arguments = inputParts.Length > 1 ? string.Join(' ', inputParts[1..]).Trim() : null;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
using var process = new Process();
|
||||
process.StartInfo.FileName = path;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(arguments))
|
||||
{
|
||||
process.StartInfo.Arguments = arguments;
|
||||
}
|
||||
if (_appState.SelectedTab.CurrentLocation.Container is LocalFolder localFolder)
|
||||
{
|
||||
process.StartInfo.WorkingDirectory = localFolder.Directory.FullName;
|
||||
}
|
||||
process.Start();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_dialogService.ReadInputs(new List<InputElement>() { new InputElement("Command", InputType.Text) }, handler);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task SelectPreviousTimelineBlock()
|
||||
{
|
||||
var currentSelected = GetSelectedTimelineCommandOrSelectFirst();
|
||||
if (currentSelected == null) return Task.CompletedTask;
|
||||
|
||||
ParallelCommandsViewModel? newBlockVM = null;
|
||||
ParallelCommandsViewModel? previousBlockVM = null;
|
||||
|
||||
foreach (var timelineBlock in _appState.TimelineCommands)
|
||||
{
|
||||
|
||||
foreach (var command in timelineBlock.ParallelCommands)
|
||||
{
|
||||
if (command.IsSelected)
|
||||
{
|
||||
newBlockVM = previousBlockVM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
previousBlockVM = timelineBlock;
|
||||
}
|
||||
|
||||
if (newBlockVM == null) return Task.CompletedTask;
|
||||
|
||||
foreach (var val in _appState.TimelineCommands.Select(t => t.ParallelCommands.Select((c, i) => (ParalellCommandVM: t, CommandVM: c, Index: i))).SelectMany(t => t))
|
||||
{
|
||||
val.CommandVM.IsSelected = val.ParalellCommandVM == newBlockVM && val.Index == 0;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task SelectNextTimelineCommand()
|
||||
{
|
||||
var currentSelected = GetSelectedTimelineCommandOrSelectFirst();
|
||||
if (currentSelected == null) return Task.CompletedTask;
|
||||
|
||||
ParallelCommandViewModel? lastCommand = null;
|
||||
var any = false;
|
||||
foreach (var command in _appState.TimelineCommands.SelectMany(t => t.ParallelCommands))
|
||||
{
|
||||
var isSelected = lastCommand == currentSelected;
|
||||
command.IsSelected = isSelected;
|
||||
any = any || isSelected;
|
||||
lastCommand = command;
|
||||
}
|
||||
if (!any && lastCommand != null) lastCommand.IsSelected = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task SelectPreviousTimelineCommand()
|
||||
{
|
||||
var currentSelected = GetSelectedTimelineCommandOrSelectFirst();
|
||||
if (currentSelected == null) return Task.CompletedTask;
|
||||
|
||||
ParallelCommandViewModel? lastCommand = null;
|
||||
foreach (var command in _appState.TimelineCommands.SelectMany(t => t.ParallelCommands))
|
||||
{
|
||||
if (lastCommand != null)
|
||||
{
|
||||
lastCommand.IsSelected = command == currentSelected;
|
||||
}
|
||||
lastCommand = command;
|
||||
}
|
||||
if (lastCommand != null) lastCommand.IsSelected = false;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task SelectNextTimelineBlock()
|
||||
{
|
||||
var currentSelected = GetSelectedTimelineCommandOrSelectFirst();
|
||||
if (currentSelected == null) return Task.CompletedTask;
|
||||
|
||||
ParallelCommandsViewModel? newBlockVM = null;
|
||||
var select = false;
|
||||
foreach (var timelineBlock in _appState.TimelineCommands)
|
||||
{
|
||||
if (select)
|
||||
{
|
||||
newBlockVM = timelineBlock;
|
||||
break;
|
||||
}
|
||||
foreach (var command in timelineBlock.ParallelCommands)
|
||||
{
|
||||
if (command.IsSelected)
|
||||
{
|
||||
select = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newBlockVM == null) return Task.CompletedTask;
|
||||
|
||||
foreach (var val in _appState.TimelineCommands.Select(t => t.ParallelCommands.Select((c, i) => (ParalellCommandVM: t, CommandVM: c, Index: i))).SelectMany(t => t))
|
||||
{
|
||||
val.CommandVM.IsSelected = val.ParalellCommandVM == newBlockVM && val.Index == 0;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private ParallelCommandViewModel? GetSelectedTimelineCommandOrSelectFirst()
|
||||
{
|
||||
var currentSelected = _appState.TimelineCommands.SelectMany(t => t.ParallelCommands).FirstOrDefault(c => c.IsSelected);
|
||||
if (currentSelected != null) return currentSelected;
|
||||
|
||||
var firstCommand = _appState.TimelineCommands.SelectMany(t => t.ParallelCommands).FirstOrDefault();
|
||||
if (firstCommand != null)
|
||||
{
|
||||
firstCommand.IsSelected = true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task AddCommand(ICommand command)
|
||||
{
|
||||
if (_addCommandToNextBatch)
|
||||
{
|
||||
await _timeRunner.AddCommand(command, toNewBatch: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ParallelCommandsViewModel? batchToAdd = null;
|
||||
foreach (var val in _appState.TimelineCommands.Select(t => t.ParallelCommands.Select(c => (ParalellCommandVM: t, CommandVM: c))).SelectMany(t => t))
|
||||
{
|
||||
if (val.CommandVM.IsSelected)
|
||||
{
|
||||
batchToAdd = val.ParalellCommandVM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (batchToAdd != null)
|
||||
{
|
||||
await _timeRunner.AddCommand(command, batchToAdd.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _timeRunner.AddCommand(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task ToggleAutoRefresh()
|
||||
{
|
||||
var tab = _appState.SelectedTab.TabState.Tab;
|
||||
tab.AutoRefresh = !tab.AutoRefresh;
|
||||
|
||||
_dialogService.ShowToastMessage("Auto refresh is: " + (tab.AutoRefresh ? "ON" : "OFF"));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
105
src/GuiApp/FileTime.Avalonia/Services/DialogService.cs
Normal file
105
src/GuiApp/FileTime.Avalonia/Services/DialogService.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Threading;
|
||||
using FileTime.Avalonia.Application;
|
||||
using FileTime.Avalonia.Misc;
|
||||
using FileTime.Core.Interactions;
|
||||
|
||||
namespace FileTime.Avalonia.Services
|
||||
{
|
||||
public class DialogService
|
||||
{
|
||||
private readonly AppState _appState;
|
||||
|
||||
private Func<List<InputElementWrapper>, Task>? _inputHandler;
|
||||
|
||||
public DialogService(AppState appState)
|
||||
{
|
||||
_appState = appState;
|
||||
}
|
||||
public void ReadInputs(List<InputElement> inputs, Action<List<InputElementWrapper>> inputHandler) =>
|
||||
ReadInputs(inputs, (inputs) => { inputHandler(inputs); return Task.CompletedTask; });
|
||||
|
||||
public void ReadInputs(List<InputElement> inputs, Func<List<InputElementWrapper>, Task> inputHandler)
|
||||
{
|
||||
_appState.Inputs = inputs.ConvertAll(i => new InputElementWrapper(i, i.DefaultValue));
|
||||
_inputHandler = inputHandler;
|
||||
}
|
||||
|
||||
public async Task<string?[]> ReadInputs(IEnumerable<InputElement> fields)
|
||||
{
|
||||
var waiting = true;
|
||||
var result = Array.Empty<string>();
|
||||
ReadInputs(fields.ToList(), (inputs) =>
|
||||
{
|
||||
if (inputs != null)
|
||||
{
|
||||
result = inputs.Select(i => i.Value).ToArray();
|
||||
}
|
||||
waiting = false;
|
||||
});
|
||||
|
||||
while (waiting) await Task.Delay(100);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void ClearInputs()
|
||||
{
|
||||
_appState.Inputs = null;
|
||||
_inputHandler = null;
|
||||
}
|
||||
|
||||
public async Task ProcessInputs()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_inputHandler != null)
|
||||
{
|
||||
await _inputHandler.Invoke(_appState.Inputs);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
ClearInputs();
|
||||
}
|
||||
|
||||
public void CancelInputs()
|
||||
{
|
||||
ClearInputs();
|
||||
}
|
||||
|
||||
public void ShowMessageBox(string text, Func<Task> inputHandler)
|
||||
{
|
||||
_appState.MessageBoxText = text;
|
||||
_inputHandler = async (_) => await inputHandler();
|
||||
}
|
||||
|
||||
public void ProcessMessageBox()
|
||||
{
|
||||
_inputHandler?.Invoke(null!);
|
||||
|
||||
_appState.MessageBoxText = null;
|
||||
_inputHandler = null;
|
||||
}
|
||||
|
||||
public void CancelMessageBox()
|
||||
{
|
||||
_appState.MessageBoxText = null;
|
||||
_inputHandler = null;
|
||||
}
|
||||
|
||||
public void ShowToastMessage(string text)
|
||||
{
|
||||
_appState.PopupTexts.Add(text);
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(5000);
|
||||
await Dispatcher.UIThread.InvokeAsync(() => _appState.PopupTexts.Remove(text));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
215
src/GuiApp/FileTime.Avalonia/Services/KeyInputHandlerService.cs
Normal file
215
src/GuiApp/FileTime.Avalonia/Services/KeyInputHandlerService.cs
Normal file
@@ -0,0 +1,215 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Input;
|
||||
using FileTime.Avalonia.Application;
|
||||
using FileTime.Avalonia.Configuration;
|
||||
using FileTime.Avalonia.ViewModels;
|
||||
using FileTime.Core.Extensions;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.Avalonia.Services
|
||||
{
|
||||
public class KeyInputHandlerService
|
||||
{
|
||||
private readonly List<KeyConfig[]> _keysToSkip = new();
|
||||
private readonly AppState _appState;
|
||||
private readonly KeyboardConfigurationService _keyboardConfigurationService;
|
||||
private readonly CommandHandlerService _commandHandlerService;
|
||||
private readonly DialogService _dialogService;
|
||||
|
||||
public KeyInputHandlerService(
|
||||
AppState appState,
|
||||
KeyboardConfigurationService keyboardConfigurationService,
|
||||
CommandHandlerService commandHandlerService,
|
||||
DialogService dialogService)
|
||||
{
|
||||
_appState = appState;
|
||||
_keyboardConfigurationService = keyboardConfigurationService;
|
||||
_commandHandlerService = commandHandlerService;
|
||||
_dialogService = dialogService;
|
||||
|
||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.Up) });
|
||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.Down) });
|
||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.Tab) });
|
||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.PageDown) });
|
||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.PageUp) });
|
||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.F4, alt: true) });
|
||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.LWin) });
|
||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.RWin) });
|
||||
}
|
||||
public async Task ProcessKeyDown(Key key, KeyModifiers keyModifiers, Action<bool> setHandled)
|
||||
{
|
||||
if (key == Key.LeftAlt
|
||||
|| key == Key.RightAlt
|
||||
|| key == Key.LeftShift
|
||||
|| key == Key.RightShift
|
||||
|| key == Key.LeftCtrl
|
||||
|| key == Key.RightCtrl) return;
|
||||
|
||||
_appState.NoCommandFound = false;
|
||||
|
||||
var isAltPressed = (keyModifiers & KeyModifiers.Alt) == KeyModifiers.Alt;
|
||||
var isShiftPressed = (keyModifiers & KeyModifiers.Shift) == KeyModifiers.Shift;
|
||||
var isCtrlPressed = (keyModifiers & KeyModifiers.Control) == KeyModifiers.Control;
|
||||
|
||||
if (_appState.ViewMode == ViewMode.Default)
|
||||
{
|
||||
var keyWithModifiers = new KeyConfig(key, shift: isShiftPressed, alt: isAltPressed, ctrl: isCtrlPressed);
|
||||
_appState.PreviousKeys.Add(keyWithModifiers);
|
||||
|
||||
var selectedCommandBinding = _keyboardConfigurationService.UniversalCommandBindings.FirstOrDefault(c => AreKeysEqual(c.Keys, _appState.PreviousKeys));
|
||||
selectedCommandBinding ??= _keyboardConfigurationService.CommandBindings.FirstOrDefault(c => AreKeysEqual(c.Keys, _appState.PreviousKeys));
|
||||
|
||||
if (key == Key.Escape)
|
||||
{
|
||||
_appState.IsAllShortcutVisible = false;
|
||||
_appState.MessageBoxText = null;
|
||||
_appState.PreviousKeys.Clear();
|
||||
_appState.PossibleCommands = new();
|
||||
setHandled(true);
|
||||
}
|
||||
else if (key == Key.Enter
|
||||
&& _appState.MessageBoxText != null)
|
||||
{
|
||||
_appState.PreviousKeys.Clear();
|
||||
_dialogService.ProcessMessageBox();
|
||||
setHandled(true);
|
||||
}
|
||||
else if (selectedCommandBinding != null)
|
||||
{
|
||||
setHandled(true);
|
||||
await _commandHandlerService.HandleCommandAsync(selectedCommandBinding.Command);
|
||||
_appState.PreviousKeys.Clear();
|
||||
_appState.PossibleCommands = new();
|
||||
}
|
||||
else if (_keysToSkip.Any(k => AreKeysEqual(k, _appState.PreviousKeys)))
|
||||
{
|
||||
_appState.PreviousKeys.Clear();
|
||||
_appState.PossibleCommands = new();
|
||||
return;
|
||||
}
|
||||
else if (_appState.PreviousKeys.Count == 2)
|
||||
{
|
||||
setHandled(true);
|
||||
_appState.NoCommandFound = true;
|
||||
_appState.PreviousKeys.Clear();
|
||||
_appState.PossibleCommands = new();
|
||||
}
|
||||
else
|
||||
{
|
||||
var possibleCommands = _keyboardConfigurationService.AllShortcut.Where(c => AreKeysEqual(c.Keys[0], keyWithModifiers)).ToList();
|
||||
|
||||
if (possibleCommands.Count == 0)
|
||||
{
|
||||
_appState.NoCommandFound = true;
|
||||
_appState.PreviousKeys.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
_appState.PossibleCommands = possibleCommands;
|
||||
}
|
||||
setHandled(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var keyString = key.ToString();
|
||||
var updateRapidTravelFilter = false;
|
||||
|
||||
if (key == Key.Escape)
|
||||
{
|
||||
setHandled(true);
|
||||
if (_appState.IsAllShortcutVisible)
|
||||
{
|
||||
_appState.IsAllShortcutVisible = false;
|
||||
}
|
||||
else if (_appState.MessageBoxText != null)
|
||||
{
|
||||
_appState.MessageBoxText = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
await _appState.ExitRapidTravelMode();
|
||||
}
|
||||
}
|
||||
else if (key == Key.Back)
|
||||
{
|
||||
if (_appState.RapidTravelText.Length > 0)
|
||||
{
|
||||
setHandled(true);
|
||||
_appState.RapidTravelText = _appState.RapidTravelText.Substring(0, _appState.RapidTravelText.Length - 1);
|
||||
updateRapidTravelFilter = true;
|
||||
}
|
||||
}
|
||||
else if (keyString.Length == 1)
|
||||
{
|
||||
setHandled(true);
|
||||
_appState.RapidTravelText += keyString.ToLower();
|
||||
updateRapidTravelFilter = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var currentKeyAsList = new List<KeyConfig>() { new KeyConfig(key) };
|
||||
var selectedCommandBinding = _keyboardConfigurationService.UniversalCommandBindings.FirstOrDefault(c => AreKeysEqual(c.Keys, currentKeyAsList));
|
||||
if (selectedCommandBinding != null)
|
||||
{
|
||||
setHandled(true);
|
||||
await _commandHandlerService.HandleCommandAsync(selectedCommandBinding.Command);
|
||||
}
|
||||
}
|
||||
|
||||
if (updateRapidTravelFilter)
|
||||
{
|
||||
var currentLocation = await _appState.SelectedTab.CurrentLocation.Container.WithoutVirtualContainer(MainPageViewModel.RAPIDTRAVEL);
|
||||
var newLocation = new VirtualContainer(
|
||||
currentLocation,
|
||||
new List<Func<IEnumerable<IContainer>, IEnumerable<IContainer>>>()
|
||||
{
|
||||
container => container.Where(c => c.Name.ToLower().Contains(_appState.RapidTravelText))
|
||||
},
|
||||
new List<Func<IEnumerable<IElement>, IEnumerable<IElement>>>()
|
||||
{
|
||||
element => element.Where(e => e.Name.ToLower().Contains(_appState.RapidTravelText))
|
||||
},
|
||||
virtualContainerName: MainPageViewModel.RAPIDTRAVEL
|
||||
);
|
||||
|
||||
await newLocation.Init();
|
||||
|
||||
await _appState.SelectedTab.OpenContainer(newLocation);
|
||||
|
||||
var selectedItemName = _appState.SelectedTab.SelectedItem?.Item.Name;
|
||||
var currentLocationItems = await _appState.SelectedTab.CurrentLocation.GetItems();
|
||||
if (currentLocationItems.FirstOrDefault(i => string.Equals(i.Item.Name, _appState.RapidTravelText, StringComparison.OrdinalIgnoreCase)) is IItemViewModel matchItem)
|
||||
{
|
||||
await _appState.SelectedTab.SetCurrentSelectedItem(matchItem.Item);
|
||||
}
|
||||
else if (!currentLocationItems.Select(i => i.Item.Name).Any(n => n == selectedItemName))
|
||||
{
|
||||
await _appState.SelectedTab.MoveCursorToFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool AreKeysEqual(IReadOnlyList<KeyConfig> collection1, IReadOnlyList<KeyConfig> collection2)
|
||||
{
|
||||
if (collection1.Count != collection2.Count) return false;
|
||||
|
||||
for (var i = 0; i < collection1.Count; i++)
|
||||
{
|
||||
if (!AreKeysEqual(collection1[i], collection2[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool AreKeysEqual(KeyConfig key1, KeyConfig key2) =>
|
||||
key1.Key == key2.Key
|
||||
&& key1.Alt == key2.Alt
|
||||
&& key1.Shift == key2.Shift
|
||||
&& key1.Ctrl == key2.Ctrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using FileTime.Avalonia.Configuration;
|
||||
using FileTime.App.Core.Command;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace FileTime.Avalonia.Services
|
||||
{
|
||||
public class KeyboardConfigurationService
|
||||
{
|
||||
public IReadOnlyList<CommandBindingConfiguration> CommandBindings { get; }
|
||||
public IReadOnlyList<CommandBindingConfiguration> UniversalCommandBindings { get; }
|
||||
public IReadOnlyList<CommandBindingConfiguration> AllShortcut { get; }
|
||||
|
||||
public KeyboardConfigurationService(IOptions<KeyBindingConfiguration> keyBindingConfiguration)
|
||||
{
|
||||
List<CommandBindingConfiguration> commandBindings = new();
|
||||
List<CommandBindingConfiguration> universalCommandBindings = new();
|
||||
IEnumerable<CommandBindingConfiguration> keyBindings = keyBindingConfiguration.Value.KeyBindings;
|
||||
|
||||
if (keyBindingConfiguration.Value.UseDefaultBindings)
|
||||
{
|
||||
keyBindings = keyBindings.Concat(keyBindingConfiguration.Value.DefaultKeyBindings);
|
||||
}
|
||||
|
||||
foreach (var keyBinding in keyBindings)
|
||||
{
|
||||
if (keyBinding.Command == Commands.None)
|
||||
{
|
||||
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>(CommandBindings.Concat(UniversalCommandBindings)).AsReadOnly();
|
||||
}
|
||||
|
||||
private static bool IsUniversal(CommandBindingConfiguration keyMapping)
|
||||
{
|
||||
return keyMapping.Command == Commands.GoUp
|
||||
|| keyMapping.Command == Commands.Open
|
||||
|| keyMapping.Command == Commands.OpenOrRun
|
||||
|| keyMapping.Command == Commands.MoveCursorUp
|
||||
|| keyMapping.Command == Commands.MoveCursorUpPage
|
||||
|| keyMapping.Command == Commands.MoveCursorDownPage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,10 @@ namespace FileTime.Avalonia
|
||||
serviceCollection = serviceCollection
|
||||
.AddSingleton<ItemNameConverterService>()
|
||||
.AddSingleton<StatePersistenceService>()
|
||||
.AddSingleton<CommandHandlerService>()
|
||||
.AddSingleton<KeyboardConfigurationService>()
|
||||
.AddSingleton<KeyInputHandlerService>()
|
||||
.AddSingleton<DialogService>()
|
||||
.AddSingleton<IIconProvider, MaterialIconProvider>();
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
@@ -66,14 +70,15 @@ namespace FileTime.Avalonia
|
||||
.Build();
|
||||
|
||||
return serviceCollection
|
||||
//.Configure<SzopiAPIConfig>(configuration.GetSection("server"))
|
||||
.Configure<KeyBindingConfiguration>(configuration.GetSection(MainConfiguration.KeybindingBaseConfigKey))
|
||||
.AddSingleton<IConfiguration>(configuration);
|
||||
}
|
||||
|
||||
internal static IServiceCollection InitSerilog(this IServiceCollection serviceCollection)
|
||||
{
|
||||
using var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.ReadFrom.Configuration(serviceCollection.BuildServiceProvider().GetService<IConfiguration>())
|
||||
.ReadFrom.Configuration(serviceProvider.GetService<IConfiguration>())
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.File(
|
||||
Path.Combine(Program.AppDataRoot, "logs", "appLog.log"),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,8 @@
|
||||
ExtendClientAreaToDecorationsHint="True"
|
||||
Opened="OnWindowOpened"
|
||||
Closed="OnWindowClosed"
|
||||
x:DataType="vm:MainPageViewModel"
|
||||
x:CompileBindings="True"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{DynamicResource AppBackgroundBrush}">
|
||||
@@ -149,7 +151,7 @@
|
||||
|
||||
<Grid Grid.Column="1" Grid.Row="1" RowDefinitions="Auto,40,*,Auto">
|
||||
<Grid>
|
||||
<ItemsControl Items="{Binding TimelineCommands}">
|
||||
<ItemsControl Items="{Binding AppState.TimelineCommands}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" />
|
||||
@@ -335,7 +337,7 @@
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<ItemsRepeater Items="{Binding PopupTexts}" Margin="0,0,0,20" HorizontalAlignment="Center" VerticalAlignment="Bottom" IsVisible="{Binding PopupTexts.Count,Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
|
||||
<ItemsRepeater Items="{Binding AppState.PopupTexts}" Margin="0,0,0,20" HorizontalAlignment="Center" VerticalAlignment="Bottom" IsVisible="{Binding AppState.PopupTexts.Count,Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
|
||||
<ItemsRepeater.Styles>
|
||||
<Style Selector="TextBlock">
|
||||
<Style.Animations>
|
||||
@@ -387,7 +389,7 @@
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Grid IsVisible="{Binding NoCommandFound}">
|
||||
<Grid IsVisible="{Binding AppState.NoCommandFound}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="1" />
|
||||
<RowDefinition Height="Auto" />
|
||||
@@ -408,7 +410,7 @@
|
||||
Text="No command found" />
|
||||
</Grid>
|
||||
|
||||
<Grid IsVisible="{Binding PossibleCommands.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
|
||||
<Grid IsVisible="{Binding AppState.PossibleCommands.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="1" />
|
||||
<RowDefinition Height="Auto" />
|
||||
@@ -423,7 +425,7 @@
|
||||
|
||||
<ItemsRepeater
|
||||
Grid.Row="1"
|
||||
Items="{Binding PossibleCommands}">
|
||||
Items="{Binding AppState.PossibleCommands}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -433,7 +435,7 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Text="{Binding KeysDisplayText}" />
|
||||
<TextBlock Grid.Column="1" Text="{Binding Name}" />
|
||||
<TextBlock Grid.Column="1" Text="{Binding Command, Converter={StaticResource CommandToCommandNameConverter}}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
@@ -447,7 +449,7 @@
|
||||
Background="{DynamicResource BarelyTransparentBackgroundColor}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
IsVisible="{Binding Inputs, Converter={StaticResource IsNotNullConverter}}">
|
||||
IsVisible="{Binding AppState.Inputs, Converter={StaticResource IsNotNullConverter}}">
|
||||
<Border
|
||||
Background="{DynamicResource ContainerBackgroundBrush}"
|
||||
Padding="20"
|
||||
@@ -457,7 +459,7 @@
|
||||
|
||||
<ItemsControl
|
||||
x:Name="InputList"
|
||||
Items="{Binding Inputs}">
|
||||
Items="{Binding AppState.Inputs}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid MinWidth="500" ColumnDefinitions="250,*" Margin="10,5">
|
||||
@@ -502,7 +504,7 @@
|
||||
Background="{DynamicResource BarelyTransparentBackgroundColor}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
IsVisible="{Binding MessageBoxText, Converter={StaticResource IsNotNullConverter}}">
|
||||
IsVisible="{Binding AppState.MessageBoxText, Converter={StaticResource IsNotNullConverter}}">
|
||||
<Border
|
||||
Background="{DynamicResource ContainerBackgroundBrush}"
|
||||
HorizontalAlignment="Center"
|
||||
@@ -510,7 +512,7 @@
|
||||
Padding="20">
|
||||
<Grid RowDefinitions="Auto,Auto">
|
||||
|
||||
<TextBlock Text="{Binding MessageBoxText}"/>
|
||||
<TextBlock Text="{Binding AppState.MessageBoxText}"/>
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Orientation="Horizontal"
|
||||
@@ -535,14 +537,14 @@
|
||||
Background="{DynamicResource TransparentContainerBackgroundBrush}"
|
||||
Margin="20"
|
||||
HorizontalAlignment="Center"
|
||||
IsVisible="{Binding IsAllShortcutVisible}">
|
||||
IsVisible="{Binding AppState.IsAllShortcutVisible}">
|
||||
<Grid RowDefinitions="Auto, *" Margin="30,10">
|
||||
<TextBlock Text="Shortcuts" Margin="0,0,0,20"/>
|
||||
<ScrollViewer
|
||||
Grid.Row="1"
|
||||
HorizontalScrollBarVisibility="Disabled">
|
||||
<ItemsControl
|
||||
Items="{Binding AllShortcut}">
|
||||
Items="{Binding KeyboardConfigurationService.AllShortcut}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="0,5,10,5">
|
||||
@@ -552,7 +554,7 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Text="{Binding KeysDisplayText}" />
|
||||
<TextBlock Grid.Column="1" Text="{Binding Name}" />
|
||||
<TextBlock Grid.Column="1" Text="{Binding Command, Converter={StaticResource CommandToCommandNameConverter}}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
|
||||
@@ -55,13 +55,13 @@ namespace FileTime.Avalonia.Views
|
||||
|
||||
private void InputText_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Enter && _inputElementWrapper == ViewModel!.Inputs.Last())
|
||||
if (e.Key == Key.Enter && _inputElementWrapper == ViewModel!.AppState.Inputs.Last())
|
||||
{
|
||||
ViewModel.ProcessInputs();
|
||||
_inputElementWrapper = null;
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.Escape && _inputElementWrapper == ViewModel!.Inputs.Last())
|
||||
else if (e.Key == Key.Escape && _inputElementWrapper == ViewModel!.AppState.Inputs.Last())
|
||||
{
|
||||
ViewModel.CancelInputs();
|
||||
_inputElementWrapper = null;
|
||||
@@ -87,7 +87,7 @@ namespace FileTime.Avalonia.Views
|
||||
|
||||
private void InputText_AttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (sender is TextBox inputText && inputText.DataContext is InputElementWrapper inputElementWrapper && inputElementWrapper == ViewModel!.Inputs.First())
|
||||
if (sender is TextBox inputText && inputText.DataContext is InputElementWrapper inputElementWrapper && inputElementWrapper == ViewModel!.AppState.Inputs.First())
|
||||
{
|
||||
inputText.Focus();
|
||||
}
|
||||
@@ -101,7 +101,7 @@ namespace FileTime.Avalonia.Views
|
||||
&& sender is StyledElement control
|
||||
&& control.DataContext is PlaceInfo placeInfo)
|
||||
{
|
||||
ViewModel.OpenContainer(placeInfo.Container);
|
||||
ViewModel.CommandHandlerService.OpenContainer(placeInfo.Container);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,7 @@ namespace FileTime.Avalonia.Views
|
||||
&& sender is StyledElement control
|
||||
&& control.DataContext is RootDriveInfo rootDriveInfo)
|
||||
{
|
||||
ViewModel.OpenContainer(rootDriveInfo.Container);
|
||||
ViewModel.CommandHandlerService.OpenContainer(rootDriveInfo.Container);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user