TimeTravel

This commit is contained in:
2022-01-31 23:13:39 +01:00
parent 80570d8895
commit c2dcb49016
78 changed files with 2294 additions and 363 deletions

View File

@@ -15,12 +15,18 @@
<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="AccentForegroundColor">#268bd2</Color>
<Color x:Key="LightForegroundColor">#7793a1a1</Color>
<Color x:Key="AlternativeForegroundColor">#93a1a1</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>
@@ -48,6 +54,15 @@
<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"
@@ -59,11 +74,20 @@
x:Key="LightForegroundBrush"
Color="{DynamicResource LightForegroundColor}" />
<SolidColorBrush
x:Key="AlternativeForegroundBrush"
Color="{DynamicResource AlternativeForegroundColor}" />
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"
@@ -79,14 +103,23 @@
<converters:ItemViewModeToBrushConverter
x:Key="ItemViewModeToForegroundConverter"
DefaultBrush="{StaticResource ForegroundBrush}"
AlternativeBrush="{StaticResource AlternativeForegroundBrush}"
SelectedBrush="{StaticResource SelectedItemForegroundBrush}"/>
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}"/>
SelectedBrush="{StaticResource SelectedItemBackgroundBrush}"
MarkedBrush="{StaticResource MarkedItemBackgroundBrush}"
MarkedAlternativeBrush="{StaticResource MarkedAlternativeItemBackgroundBrush}"
MarkedSelectedBrush="{StaticResource MarkedSelectedItemBackgroundBrush}"/>
<converters:ContextMenuGenerator x:Key="ContextMenuGenerator"/>
<converters:ItemToImageConverter x:Key="ItemToImageConverter"/>
<converters:IsNullConverter x:Key="IsNullConverter"/>
<converters:IsNullConverter x:Key="IsNotNullConverter" Inverse="true"/>
</ResourceDictionary>
</Application.Resources>

View File

@@ -19,6 +19,7 @@ namespace FileTime.Avalonia
.RegisterDefaultServices()
.AddViewModels()
.AddServices()
.RegisterCommandHandlers()
.BuildServiceProvider();
}

View File

@@ -1,9 +1,11 @@
using FileTime.Core.Components;
using MvvmGen;
using System;
using MvvmGen;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Collections.Specialized;
using System.Linq;
using FileTime.App.Core.Tab;
using System.Threading.Tasks;
using FileTime.Core.Models;
namespace FileTime.Avalonia.Application
{
@@ -11,7 +13,7 @@ namespace FileTime.Avalonia.Application
public partial class AppState
{
[Property]
private ObservableCollection<TabContainer> _tabs = new ObservableCollection<TabContainer>();
private ObservableCollection<TabContainer> _tabs = new();
[Property]
private TabContainer _selectedTab;
@@ -24,7 +26,66 @@ namespace FileTime.Avalonia.Application
partial void OnInitialize()
{
_tabs.CollectionChanged += (o, e) => SelectedTab ??= Tabs.Count > 0 ? Tabs[0] : null;
_tabs.CollectionChanged += TabsChanged;
}
private void TabsChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
SelectedTab ??= Tabs.Count > 0 ? Tabs[0] : null;
List<TabContainer> itemsAdded = new();
List<TabContainer> itemsRemoved = new();
if (e.NewItems != null && e.OldItems != null)
{
itemsAdded.AddRange(e.NewItems.Cast<TabContainer>().Except(e.OldItems.Cast<TabContainer>()));
itemsRemoved.AddRange(e.OldItems.Cast<TabContainer>().Except(e.NewItems.Cast<TabContainer>()));
}
else if (e.NewItems != null)
{
itemsAdded.AddRange(e.NewItems.Cast<TabContainer>());
}
else if (e.OldItems != null)
{
itemsRemoved.AddRange(e.OldItems.Cast<TabContainer>());
}
foreach (var item in itemsAdded)
{
item.TabState.ItemMarked.Add(TabItemMarked);
item.TabState.ItemUnmarked.Add(TabItemUnmarked);
}
foreach (var item in itemsRemoved)
{
item.TabState.ItemMarked.Remove(TabItemMarked);
item.TabState.ItemUnmarked.Remove(TabItemUnmarked);
}
}
private async Task TabItemMarked(TabState tabState, AbsolutePath item)
{
var tabContainer = Tabs.FirstOrDefault(t => t.TabState == tabState);
if (tabContainer != null)
{
var item2 = (await tabContainer.CurrentLocation.GetItems()).FirstOrDefault(i => i.Item.FullName == item.Path);
if (item2 != null)
{
item2.IsMarked = true;
}
}
}
private async Task TabItemUnmarked(TabState tabState, AbsolutePath item)
{
var tabContainer = Tabs.FirstOrDefault(t => t.TabState == tabState);
if (tabContainer != null)
{
var item2 = (await tabContainer.CurrentLocation.GetItems()).FirstOrDefault(i => i.Item.FullName == item.Path);
if (item2 != null)
{
item2.IsMarked = false;
}
}
}
}
}

View File

@@ -0,0 +1,10 @@
using System.Threading.Tasks;
using FileTime.Avalonia.ViewModels;
namespace FileTime.Avalonia.Application
{
public interface INewItemProcessor
{
Task UpdateMarkedItems(ContainerViewModel containerViewModel);
}
}

View File

@@ -6,11 +6,11 @@ using FileTime.Avalonia.Services;
using FileTime.Avalonia.ViewModels;
using MvvmGen;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FileTime.App.Core.Tab;
using System.Collections.Generic;
namespace FileTime.Avalonia.Application
{
@@ -18,8 +18,11 @@ namespace FileTime.Avalonia.Application
[Inject(typeof(ItemNameConverterService))]
[Inject(typeof(LocalContentProvider))]
[Inject(typeof(Tab))]
public partial class TabContainer
public partial class TabContainer : INewItemProcessor
{
[Property]
private TabState _tabState;
[Property]
private ContainerViewModel _parent;
@@ -45,25 +48,33 @@ namespace FileTime.Avalonia.Application
if (_selectedItem != value)// && value != null
{
_selectedItem = value;
OnPropertyChanged("SelectedItem");
SelectedItemChanged();
}
}
}
partial void OnInitialize()
{
_tabState = new TabState(Tab);
}
public async Task Init(int tabNumber)
{
TabNumber = tabNumber;
Tab.CurrentLocationChanged.Add(Tab_CurrentLocationChanged);
Tab.CurrentSelectedItemChanged.Add(Tab_CurrentSelectedItemChanged);
CurrentLocation = new ContainerViewModel(await Tab.GetCurrentLocation(), ItemNameConverterService);
var currentLocation = await Tab.GetCurrentLocation();
var parent = GenerateParent(currentLocation);
CurrentLocation = new ContainerViewModel(this, parent, currentLocation, ItemNameConverterService);
await CurrentLocation.Init();
var parent = (await Tab.GetCurrentLocation()).GetParent();
if (parent != null)
{
Parent = new ContainerViewModel(parent, ItemNameConverterService);
parent.ChildrenToAdopt.Add(CurrentLocation);
Parent = parent;
await Parent.Init();
}
else
@@ -74,16 +85,28 @@ namespace FileTime.Avalonia.Application
await UpdateCurrentSelectedItem();
}
private ContainerViewModel? GenerateParent(IContainer? container, bool recursive = false)
{
var parentContainer = container?.GetParent();
if (parentContainer == null) return null;
var parentParent = recursive ? GenerateParent(parentContainer.GetParent(), recursive) : null;
var parent = new ContainerViewModel(this, parentParent, parentContainer, ItemNameConverterService);
parentParent?.ChildrenToAdopt.Add(parent);
return parent;
}
private async Task Tab_CurrentLocationChanged(object? sender, AsyncEventArgs e)
{
var currentLocation = await Tab.GetCurrentLocation();
CurrentLocation = new ContainerViewModel(currentLocation, ItemNameConverterService);
var parent = GenerateParent(currentLocation);
CurrentLocation = new ContainerViewModel(this, parent, currentLocation, ItemNameConverterService);
await CurrentLocation.Init();
var parent = currentLocation.GetParent();
if (parent != null)
{
Parent = new ContainerViewModel(parent, ItemNameConverterService);
parent.ChildrenToAdopt.Add(CurrentLocation);
Parent = parent;
await Parent.Init();
}
else
@@ -97,39 +120,89 @@ namespace FileTime.Avalonia.Application
await UpdateCurrentSelectedItem();
}
private async Task UpdateCurrentSelectedItem()
public async Task UpdateCurrentSelectedItem()
{
var tabCurrentSelectenItem = await Tab.GetCurrentSelectedItem();
IItemViewModel? currentSelectenItem = null;
if (tabCurrentSelectenItem == null)
try
{
SelectedItem = null;
ChildContainer = null;
}
else
{
currentSelectenItem = (await _currentLocation.GetItems()).FirstOrDefault(i => i.Item.Name == tabCurrentSelectenItem.Name);
if (currentSelectenItem is ContainerViewModel currentSelectedContainer)
{
SelectedItem = currentSelectedContainer;
ChildContainer = currentSelectedContainer;
}
else if (currentSelectenItem is ElementViewModel element)
{
SelectedItem = element;
ChildContainer = null;
}
else
var tabCurrentSelectenItem = await Tab.GetCurrentSelectedItem();
IItemViewModel? currentSelectenItem = null;
if (tabCurrentSelectenItem == null)
{
SelectedItem = null;
ChildContainer = null;
}
}
else
{
currentSelectenItem = (await _currentLocation.GetItems()).FirstOrDefault(i => i.Item.Name == tabCurrentSelectenItem.Name);
if (currentSelectenItem is ContainerViewModel currentSelectedContainer)
{
SelectedItem = currentSelectedContainer;
ChildContainer = currentSelectedContainer;
}
else if (currentSelectenItem is ElementViewModel element)
{
SelectedItem = element;
ChildContainer = null;
}
else
{
SelectedItem = null;
ChildContainer = null;
}
}
var items = await _currentLocation.GetItems();
foreach (var item in items)
var items = await _currentLocation.GetItems();
if (items != null && items.Count > 0)
{
foreach (var item in items)
{
var isSelected = item == currentSelectenItem;
item.IsSelected = isSelected;
if (isSelected)
{
var parent = item.Parent;
while (parent != null)
{
parent.IsSelected = true;
parent = parent.Parent;
}
try
{
var child = item;
while (child is ContainerViewModel containerViewModel && containerViewModel.Container.IsLoaded)
{
var activeChildItem = await Tab.GetItemByLastPath(containerViewModel.Container);
child = (await containerViewModel.GetItems()).FirstOrDefault(i => i.Item == activeChildItem);
if (child != null)
{
child.IsSelected = true;
}
}
}
catch
{
//INFO collection modified exception on: child = (await containerViewModel.GetItems()).FirstOrDefault(i => i.Item == activeChildItem);
//TODO: handle or error message
}
}
}
}
else
{
var parent = _currentLocation;
while (parent != null)
{
parent.IsSelected = true;
parent = parent.Parent;
}
}
}
catch
{
item.IsSelected = item == currentSelectenItem;
//INFO collection modified exception on: currentSelectenItem = (await _currentLocation.GetItems()).FirstOrDefault(i => i.Item.Name == tabCurrentSelectenItem.Name);
//TODO: handle or error message
}
}
@@ -212,9 +285,32 @@ namespace FileTime.Avalonia.Application
(await Tab.GetCurrentLocation())?.CreateContainer(name);
}
public async Task CreateElement(string name)
{
(await Tab.GetCurrentLocation())?.CreateElement(name);
}
public async Task OpenContainer(IContainer container)
{
await Tab.OpenContainer(container);
}
public async Task MarkCurrentItem()
{
await _tabState.MakrCurrentItem();
}
public async Task UpdateMarkedItems(ContainerViewModel containerViewModel)
{
if (containerViewModel == CurrentLocation && containerViewModel.Container.IsLoaded)
{
var selectedItems = TabState.GetCurrentMarkedItems(containerViewModel.Container);
foreach (var item in await containerViewModel.GetItems())
{
item.IsMarked = selectedItems.Any(c => c.Path == item.Item.FullName);
}
}
}
}
}

View File

@@ -0,0 +1,23 @@
using Avalonia.Data.Converters;
using System;
using System.Globalization;
namespace FileTime.Avalonia.Converters
{
public class IsNullConverter : IValueConverter
{
public bool Inverse { get; set; }
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var result = value == null;
if (Inverse) result = !result;
return result;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Globalization;
using Avalonia.Data.Converters;
using Avalonia.Svg.Skia;
using FileTime.Avalonia.IconProviders;
using FileTime.Avalonia.ViewModels;
using FileTime.Core.Models;
namespace FileTime.Avalonia.Converters
{
public class ItemToImageConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value == null) return null;
IIconProvider converter = new MaterialIconProvider();
IItem item = value switch
{
ContainerViewModel container => container.Container,
ElementViewModel element => element.Element,
_ => throw new NotImplementedException()
};
var path = converter.GetImage(item)!;
var source = SvgSource.Load<SvgSource>("avares://FileTime.Avalonia" + path, null);
return new SvgImage { Source = source };
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -11,6 +11,9 @@ namespace FileTime.Avalonia.Converters
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)
{
@@ -21,6 +24,9 @@ namespace FileTime.Avalonia.Converters
ItemViewMode.Default => DefaultBrush,
ItemViewMode.Alternative => AlternativeBrush,
ItemViewMode.Selected => SelectedBrush,
ItemViewMode.Marked => MarkedBrush,
ItemViewMode.MarkedSelected => MarkedSelectedBrush,
ItemViewMode.MarkedAlternative => MarkedAlternativeBrush,
_ => throw new NotImplementedException()
};
}

View File

@@ -0,0 +1,9 @@
using FileTime.Core.Models;
namespace FileTime.Avalonia.IconProviders
{
public interface IIconProvider
{
string GetImage(IItem item);
}
}

View File

@@ -0,0 +1,33 @@
using FileTime.Core.Models;
using FileTime.Providers.Local;
using System.Linq;
namespace FileTime.Avalonia.IconProviders
{
public class MaterialIconProvider : IIconProvider
{
public string GetImage(IItem item)
{
var icon = "file.svg";
if (item is IContainer)
{
icon = "folder.svg";
}
else if (item is IElement element)
{
if(element is LocalFile localFile && element.FullName.EndsWith(".svg"))
{
return localFile.File.FullName;
}
icon = !element.Name.Contains('.')
? icon
: element.Name.Split('.').Last() switch
{
"cs" => "csharp.svg",
_ => icon
};
}
return "/Assets/material/" + icon;
}
}
}

View File

@@ -12,9 +12,10 @@ namespace FileTime.Avalonia.Misc
public string Value { get; set; }
public InputElementWrapper(InputElement inputElement)
public InputElementWrapper(InputElement inputElement, string? defaultValue = null)
{
InputElement = inputElement;
Value = defaultValue ?? "";
}
}
}

View File

@@ -3,6 +3,7 @@ using FileTime.Avalonia.Application;
using FileTime.Avalonia.Models;
using FileTime.Avalonia.ViewModels;
using MvvmGen;
using System;
using System.Collections.Generic;
namespace FileTime.Avalonia.Services
@@ -20,9 +21,8 @@ namespace FileTime.Avalonia.Services
{
var nameLeft = itemViewModel.Item.Name;
while (nameLeft.ToLower().Contains(rapidTravelText))
while (nameLeft.ToLower().IndexOf(rapidTravelText, StringComparison.Ordinal) is int rapidTextStart && rapidTextStart != -1)
{
var rapidTextStart = nameLeft.ToLower().IndexOf(rapidTravelText);
var before = rapidTextStart > 0 ? nameLeft.Substring(0, rapidTextStart) : null;
var rapidTravel = nameLeft.Substring(rapidTextStart, rapidTravelText.Length);

View File

@@ -2,6 +2,7 @@
using FileTime.Avalonia.Application;
using FileTime.Avalonia.Services;
using FileTime.Avalonia.ViewModels;
using FileTime.Core.Command;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Avalonia
@@ -29,6 +30,15 @@ namespace FileTime.Avalonia
throw new System.Exception("TODO: implement linux contextmenu provider");
}
return serviceCollection;
}
internal static IServiceCollection RegisterCommandHandlers(this IServiceCollection serviceCollection)
{
foreach (var commandHandler in FileTime.Providers.Local.Startup.GetCommandHandlers())
{
serviceCollection.AddTransient(typeof(ICommandHandler), commandHandler);
}
return serviceCollection;
}
}

View File

@@ -9,6 +9,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FileTime.Avalonia.Application;
namespace FileTime.Avalonia.ViewModels
{
@@ -16,8 +17,9 @@ namespace FileTime.Avalonia.ViewModels
[Inject(typeof(ItemNameConverterService))]
public partial class ContainerViewModel : IItemViewModel
{
private bool isRefreshing;
private bool isInitialized;
private bool _isRefreshing;
private bool _isInitialized;
private INewItemProcessor _newItemProcessor;
[Property]
private IContainer _container;
@@ -25,29 +27,39 @@ namespace FileTime.Avalonia.ViewModels
[Property]
private bool _isSelected;
public IItem Item => _container;
//[Property]
private readonly ObservableCollection<ContainerViewModel> _containers = new ObservableCollection<ContainerViewModel>();
//[Property]
private readonly ObservableCollection<ElementViewModel> _elements = new ObservableCollection<ElementViewModel>();
//[Property]
private readonly ObservableCollection<IItemViewModel> _items = new ObservableCollection<IItemViewModel>();
[Property]
private bool _isAlternative;
[Property]
private bool _isMarked;
[Property]
private ContainerViewModel? _parent;
public IItem Item => _container;
private readonly ObservableCollection<ContainerViewModel> _containers = new ObservableCollection<ContainerViewModel>();
private readonly ObservableCollection<ElementViewModel> _elements = new ObservableCollection<ElementViewModel>();
private readonly ObservableCollection<IItemViewModel> _items = new ObservableCollection<IItemViewModel>();
public List<IItemViewModel> ChildrenToAdopt { get; } = new List<IItemViewModel>();
[PropertyInvalidate(nameof(IsSelected))]
[PropertyInvalidate(nameof(IsAlternative))]
[PropertyInvalidate(nameof(IsMarked))]
public ItemViewMode ViewMode =>
IsSelected
? ItemViewMode.Selected
: IsAlternative
? ItemViewMode.Alternative
: ItemViewMode.Default;
(IsMarked, IsSelected, IsAlternative) switch
{
(true, true, _) => ItemViewMode.MarkedSelected,
(true, false, true) => ItemViewMode.MarkedAlternative,
(false, true, _) => ItemViewMode.Selected,
(false, false, true) => ItemViewMode.Alternative,
(true, false, false) => ItemViewMode.Marked,
_ => ItemViewMode.Default
};
public List<ItemNamePart> DisplayName => ItemNameConverterService.GetDisplayName(this);
@@ -56,7 +68,7 @@ namespace FileTime.Avalonia.ViewModels
{
get
{
if (!isInitialized) Task.Run(Refresh);
if (!_isInitialized) Task.Run(Refresh);
return _containers;
}
}
@@ -66,7 +78,7 @@ namespace FileTime.Avalonia.ViewModels
{
get
{
if (!isInitialized) Task.Run(Refresh);
if (!_isInitialized) Task.Run(Refresh);
return _elements;
}
}
@@ -76,13 +88,16 @@ namespace FileTime.Avalonia.ViewModels
{
get
{
if (!isInitialized) Task.Run(Refresh);
if (!_isInitialized) Task.Run(Refresh);
return _items;
}
}
public ContainerViewModel(IContainer container, ItemNameConverterService itemNameConverterService) : this(itemNameConverterService)
public ContainerViewModel(INewItemProcessor newItemProcessor, ContainerViewModel? parent, IContainer container, ItemNameConverterService itemNameConverterService) : this(itemNameConverterService)
{
_newItemProcessor = newItemProcessor;
Parent = parent;
Container = container;
Container.Refreshed.Add(Container_Refreshed);
}
@@ -94,7 +109,7 @@ namespace FileTime.Avalonia.ViewModels
await Refresh(initializeChildren);
}
private async Task Container_Refreshed(object sender, AsyncEventArgs e)
private async Task Container_Refreshed(object? sender, AsyncEventArgs e)
{
await Refresh(false);
}
@@ -105,16 +120,16 @@ namespace FileTime.Avalonia.ViewModels
}
private async Task Refresh(bool initializeChildren)
{
if (isRefreshing) return;
if (_isRefreshing) return;
isInitialized = true;
_isInitialized = true;
try
{
isRefreshing = true;
_isRefreshing = true;
var containers = (await _container.GetContainers()).Select(c => new ContainerViewModel(c, ItemNameConverterService)).ToList();
var elements = (await _container.GetElements()).Select(e => new ElementViewModel(e, ItemNameConverterService)).ToList();
var containers = (await _container.GetContainers()).Select(c => AdoptOrCreateItem(c, (c2) => new ContainerViewModel(_newItemProcessor, this, c2, ItemNameConverterService))).ToList();
var elements = (await _container.GetElements()).Select(e => AdoptOrCreateItem(e, (e2) => new ElementViewModel(e2, this, ItemNameConverterService))).ToList();
_containers.Clear();
_elements.Clear();
@@ -141,24 +156,51 @@ namespace FileTime.Avalonia.ViewModels
}
catch { }
isRefreshing = false;
await _newItemProcessor.UpdateMarkedItems(this);
_isRefreshing = false;
}
private TResult AdoptOrCreateItem<T, TResult>(T item, Func<T, TResult> generator) where T : IItem
{
var itemToAdopt = ChildrenToAdopt.Find(i => i.Item.Name == item.Name);
if (itemToAdopt is TResult itemViewModel) return itemViewModel;
return generator(item);
}
public void Unload(bool recursive = true)
{
_isInitialized = false;
if (recursive)
{
foreach (var container in _containers)
{
container.Unload(true);
container.ChildrenToAdopt.Clear();
}
}
_containers.Clear();
_elements.Clear();
_items.Clear();
}
public async Task<ObservableCollection<ContainerViewModel>> GetContainers()
{
if (!isInitialized) await Task.Run(Refresh);
if (!_isInitialized) await Task.Run(Refresh);
return _containers;
}
public async Task<ObservableCollection<ElementViewModel>> GetElements()
{
if (!isInitialized) await Task.Run(Refresh);
if (!_isInitialized) await Task.Run(Refresh);
return _elements;
}
public async Task<ObservableCollection<IItemViewModel>> GetItems()
{
if (!isInitialized) await Task.Run(Refresh);
if (!_isInitialized) await Task.Run(Refresh);
return _items;
}
}

View File

@@ -21,20 +21,32 @@ namespace FileTime.Avalonia.ViewModels
[Property]
private bool _isAlternative;
[Property]
private bool _isMarked;
[Property]
private ContainerViewModel? _parent;
[PropertyInvalidate(nameof(IsSelected))]
[PropertyInvalidate(nameof(IsAlternative))]
[PropertyInvalidate(nameof(IsMarked))]
public ItemViewMode ViewMode =>
IsSelected
? ItemViewMode.Selected
: IsAlternative
? ItemViewMode.Alternative
: ItemViewMode.Default;
(IsMarked, IsSelected, IsAlternative) switch
{
(true, true, _) => ItemViewMode.MarkedSelected,
(true, false, true) => ItemViewMode.MarkedAlternative,
(false, true, _) => ItemViewMode.Selected,
(false, false, true) => ItemViewMode.Alternative,
(true, false, false) => ItemViewMode.Marked,
_ => ItemViewMode.Default
};
public List<ItemNamePart> DisplayName => ItemNameConverterService.GetDisplayName(this);
public ElementViewModel(IElement element, ItemNameConverterService itemNameConverterService) : this(itemNameConverterService)
public ElementViewModel(IElement element, ContainerViewModel parent, ItemNameConverterService itemNameConverterService) : this(itemNameConverterService)
{
Element = element;
Parent = parent;
}
public void InvalidateDisplayName() => OnPropertyChanged(nameof(DisplayName));

View File

@@ -1,8 +1,6 @@
using FileTime.Core.Models;
using FileTime.Avalonia.Models;
using System;
using System.Collections.Generic;
using System.Text;
namespace FileTime.Avalonia.ViewModels
{
@@ -12,6 +10,8 @@ namespace FileTime.Avalonia.ViewModels
bool IsSelected { get; set; }
bool IsAlternative { get; set; }
bool IsMarked { get; set; }
ContainerViewModel? Parent{ get; set; }
ItemViewMode ViewMode { get; }

View File

@@ -6,8 +6,11 @@ namespace FileTime.Avalonia.ViewModels
{
public enum ItemViewMode
{
Selected,
Default,
Alternative,
Default
Selected,
Marked,
MarkedSelected,
MarkedAlternative
}
}

View File

@@ -18,6 +18,10 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Avalonia.Input;
using FileTime.App.Core.Clipboard;
using Microsoft.Extensions.DependencyInjection;
using FileTime.Core.Command;
using FileTime.Core.Timeline;
namespace FileTime.Avalonia.ViewModels
{
@@ -34,6 +38,9 @@ namespace FileTime.Avalonia.ViewModels
private List<CommandBinding> _commandBindings = new();
private List<CommandBinding> _universalCommandBindings = new();
private IClipboard _clipboard;
private TimeRunner _timeRunner;
private Action? _inputHandler;
[Property]
@@ -51,10 +58,16 @@ namespace FileTime.Avalonia.ViewModels
[Property]
private List<RootDriveInfo> _rootDriveInfos;
public Action? FocusDefaultElement { get; set; }
[Property]
private string _messageBoxText;
public IReadOnlyList<ReadOnlyParallelCommands> TimelineCommands => _timeRunner.ParallelCommands;
async partial void OnInitialize()
{
_clipboard = App.ServiceProvider.GetService<IClipboard>()!;
_timeRunner = App.ServiceProvider.GetService<TimeRunner>()!;
_timeRunner.CommandsChanged += (o, e) => OnPropertyChanged(nameof(TimelineCommands));
InitCommandBindings();
_keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.Up) });
@@ -62,6 +75,7 @@ namespace FileTime.Avalonia.ViewModels
_keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.Tab) });
_keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.PageDown) });
_keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.PageUp) });
_keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.F4, alt: true) });
var tab = new Tab();
await tab.Init(LocalContentProvider);
@@ -176,7 +190,6 @@ namespace FileTime.Avalonia.ViewModels
_previousKeys.Clear();
PossibleCommands = new();
FocusDefaultElement?.Invoke();
return Task.CompletedTask;
}
@@ -190,7 +203,6 @@ namespace FileTime.Avalonia.ViewModels
AppState.RapidTravelText = "";
await AppState.SelectedTab.OpenContainer(await AppState.SelectedTab.CurrentLocation.Container.WithoutVirtualContainer(RAPIDTRAVEL));
FocusDefaultElement?.Invoke();
}
public async Task SwitchToTab(int number)
@@ -260,7 +272,9 @@ namespace FileTime.Avalonia.ViewModels
{
if (Inputs != null)
{
AppState.SelectedTab.CreateContainer(Inputs[0].Value).Wait();
var container = AppState.SelectedTab.CurrentLocation.Container;
var createContainerCommand = new CreateContainerCommand(new Core.Models.AbsolutePath(container), Inputs[0].Value);
_timeRunner.AddCommand(createContainerCommand).Wait();
Inputs = null;
}
};
@@ -270,10 +284,222 @@ namespace FileTime.Avalonia.ViewModels
return Task.CompletedTask;
}
public Task CreateElement()
{
var handler = () =>
{
if (Inputs != null)
{
var container = AppState.SelectedTab.CurrentLocation.Container;
var createElementCommand = new CreateElementCommand(new Core.Models.AbsolutePath(container), Inputs[0].Value);
_timeRunner.AddCommand(createElementCommand).Wait();
Inputs = null;
}
};
ReadInputs(new List<Core.Interactions.InputElement>() { new Core.Interactions.InputElement("Element name", InputType.Text) }, handler);
return Task.CompletedTask;
}
public async Task MarkCurrentItem()
{
await AppState.SelectedTab.MarkCurrentItem();
}
public 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);
}
}
else
{
var currentSelectedItem = AppState.SelectedTab.SelectedItem?.Item;
if (currentSelectedItem != null)
{
_clipboard.AddContent(new AbsolutePath(currentSelectedItem));
}
}
}
public Task Cut()
{
_clipboard.Clear();
_clipboard.SetCommand<MoveCommand>();
return Task.CompletedTask;
}
public async Task Delete()
{
IList<Core.Models.AbsolutePath>? itemsToDelete = null;
var askForDelete = false;
var questionText = "";
var shouldDelete = false;
var currentSelectedItems = await AppState.SelectedTab.TabState.GetCurrentMarkedItems();
var currentSelectedItem = AppState.SelectedTab.SelectedItem?.Item;
if (currentSelectedItems.Count > 0)
{
itemsToDelete = currentSelectedItems.Cast<Core.Models.AbsolutePath>().ToList();
//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<Core.Models.AbsolutePath>()
{
new Core.Models.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)
{
ShowMessageBox(questionText, HandleDelete);
}
else if (shouldDelete)
{
HandleDelete();
}
}
void HandleDelete()
{
var deleteCommand = new DeleteCommand();
foreach (var itemToDelete in itemsToDelete!)
{
deleteCommand.ItemsToDelete.Add(itemToDelete);
}
_timeRunner.AddCommand(deleteCommand).Wait();
_clipboard.Clear();
}
}
public async Task PasteMerge()
{
await Paste(TransportMode.Merge);
}
public async Task PasteOverwrite()
{
await Paste(TransportMode.Overwrite);
}
public 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 _timeRunner.AddCommand(command);
_clipboard.Clear();
}
}
private Task Rename()
{
var selectedItem = AppState.SelectedTab.SelectedItem?.Item;
if (selectedItem != null)
{
var handler = () =>
{
if (Inputs != null)
{
var renameCommand = new RenameCommand(new Core.Models.AbsolutePath(selectedItem), Inputs[0].Value);
_timeRunner.AddCommand(renameCommand).Wait();
}
};
ReadInputs(new List<Core.Interactions.InputElement>() { new Core.Interactions.InputElement("New name", InputType.Text, selectedItem.Name) }, handler);
}
return Task.CompletedTask;
}
private async Task RefreshCurrentLocation()
{
await AppState.SelectedTab.CurrentLocation.Container.Refresh();
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();
}
[Command]
public void ProcessInputs()
{
_inputHandler();
_inputHandler?.Invoke();
Inputs = null;
_inputHandler = null;
@@ -286,8 +512,31 @@ namespace FileTime.Avalonia.ViewModels
_inputHandler = null;
}
[Command]
public void ProcessMessageBoxCommand()
{
_inputHandler?.Invoke();
MessageBoxText = null;
_inputHandler = null;
}
[Command]
public void CancelMessageBoxCommand()
{
MessageBoxText = null;
_inputHandler = null;
}
public async Task<bool> ProcessKeyDown(Key key, KeyModifiers keyModifiers)
{
if (key == Key.LeftAlt
|| key == Key.RightAlt
|| key == Key.LeftShift
|| key == Key.RightShift
|| key == Key.LeftCtrl
|| key == Key.RightCtrl) return false;
NoCommandFound = false;
var isAltPressed = (keyModifiers & KeyModifiers.Alt) == KeyModifiers.Alt;
@@ -312,13 +561,12 @@ namespace FileTime.Avalonia.ViewModels
await selectedCommandBinding.InvokeAsync();
_previousKeys.Clear();
PossibleCommands = new();
FocusDefaultElement?.Invoke();
}
else if (_keysToSkip.Any(k => AreKeysEqual(k, _previousKeys)))
{
_previousKeys.Clear();
PossibleCommands = new();
return false;
}
else if (_previousKeys.Count == 2)
{
@@ -370,7 +618,11 @@ namespace FileTime.Avalonia.ViewModels
if (selectedCommandBinding != null)
{
await selectedCommandBinding.InvokeAsync();
FocusDefaultElement?.Invoke();
return true;
}
else
{
return false;
}
}
@@ -399,8 +651,6 @@ namespace FileTime.Avalonia.ViewModels
{
await AppState.SelectedTab.MoveCursorToFirst();
}
FocusDefaultElement?.Invoke();
}
}
@@ -415,7 +665,13 @@ namespace FileTime.Avalonia.ViewModels
private void ReadInputs(List<Core.Interactions.InputElement> inputs, Action inputHandler)
{
Inputs = inputs.Select(i => new InputElementWrapper(i)).ToList();
Inputs = inputs.Select(i => new InputElementWrapper(i, i.DefaultValue)).ToList();
_inputHandler = inputHandler;
}
private void ShowMessageBox(string text, Action inputHandler)
{
MessageBoxText = text;
_inputHandler = inputHandler;
}
@@ -451,6 +707,11 @@ namespace FileTime.Avalonia.ViewModels
FileTime.App.Core.Command.Commands.CreateContainer,
new KeyWithModifiers[]{new KeyWithModifiers(Key.C),new KeyWithModifiers(Key.C)},
CreateContainer),
new CommandBinding(
"create element",
FileTime.App.Core.Command.Commands.CreateElement,
new KeyWithModifiers[]{new KeyWithModifiers(Key.C),new KeyWithModifiers(Key.E)},
CreateElement),
new CommandBinding(
"move to first",
FileTime.App.Core.Command.Commands.MoveToTop,
@@ -526,6 +787,66 @@ namespace FileTime.Avalonia.ViewModels
FileTime.App.Core.Command.Commands.GoToHome,
new KeyWithModifiers[]{new KeyWithModifiers(Key.Q)},
CloseTab),
new CommandBinding(
"select",
FileTime.App.Core.Command.Commands.Select,
new KeyWithModifiers[]{new KeyWithModifiers(Key.Space)},
MarkCurrentItem),
new CommandBinding(
"copy",
FileTime.App.Core.Command.Commands.Copy,
new KeyWithModifiers[]{new KeyWithModifiers(Key.Y),new KeyWithModifiers(Key.Y)},
Copy),
new CommandBinding(
"cut",
FileTime.App.Core.Command.Commands.Cut,
new KeyWithModifiers[]{new KeyWithModifiers(Key.D),new KeyWithModifiers(Key.D)},
Cut),
new CommandBinding(
"delete",
FileTime.App.Core.Command.Commands.Delete,
new KeyWithModifiers[]{new KeyWithModifiers(Key.D),new KeyWithModifiers(Key.D, shift: true)},
Delete),
new CommandBinding(
"paste merge",
FileTime.App.Core.Command.Commands.PasteMerge,
new KeyWithModifiers[]{new KeyWithModifiers(Key.P),new KeyWithModifiers(Key.P)},
PasteMerge),
new CommandBinding(
"paste (overwrite)",
FileTime.App.Core.Command.Commands.PasteOverwrite,
new KeyWithModifiers[]{new KeyWithModifiers(Key.P),new KeyWithModifiers(Key.O)},
PasteOverwrite),
new CommandBinding(
"paste (skip)",
FileTime.App.Core.Command.Commands.PasteSkip,
new KeyWithModifiers[]{new KeyWithModifiers(Key.P),new KeyWithModifiers(Key.S)},
PasteSkip),
new CommandBinding(
"rename",
FileTime.App.Core.Command.Commands.Rename,
new KeyWithModifiers[]{new KeyWithModifiers(Key.C),new KeyWithModifiers(Key.W)},
Rename),
new CommandBinding(
"timeline pause",
FileTime.App.Core.Command.Commands.Dummy,
new KeyWithModifiers[]{new KeyWithModifiers(Key.T),new KeyWithModifiers(Key.P)},
PauseTimeline),
new CommandBinding(
"timeline start",
FileTime.App.Core.Command.Commands.Dummy,
new KeyWithModifiers[]{new KeyWithModifiers(Key.T),new KeyWithModifiers(Key.S)},
ContinueTimeline),
new CommandBinding(
"refresh timeline",
FileTime.App.Core.Command.Commands.Dummy,
new KeyWithModifiers[]{new KeyWithModifiers(Key.T),new KeyWithModifiers(Key.R)},
RefreshTimeline),
new CommandBinding(
"refresh",
FileTime.App.Core.Command.Commands.Refresh,
new KeyWithModifiers[]{new KeyWithModifiers(Key.R)},
RefreshCurrentLocation),
};
var universalCommandBindings = new List<CommandBinding>()
{

View File

@@ -17,7 +17,7 @@
Height="18"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Source="{SvgImage /Assets/material/folder.svg}" />
Source="{Binding Converter={StaticResource ItemToImageConverter}}" />
<ItemsControl
Grid.Column="1"

View File

@@ -89,8 +89,29 @@
</Border>
</Grid>
<Grid Grid.Column="1" RowDefinitions="40,*,Auto">
<Grid ColumnDefinitions="*,Auto">
<Grid Grid.Column="2" RowDefinitions="Auto,40,*,Auto">
<Grid>
<ItemsControl Items="{Binding TimelineCommands}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl Items="{Binding Commands}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
<Grid Grid.Row="1" ColumnDefinitions="*,Auto">
<StackPanel Orientation="Horizontal">
<TextBlock
@@ -98,7 +119,7 @@
Text="{Binding AppState.SelectedTab.TabNumber,StringFormat=({0})}" />
<local:PathPresenter Margin="10,5,0,5" DataContext="{Binding AppState.SelectedTab.CurrentLocation.Container.FullName}"/>
<TextBlock
Margin="0,5,10,5"
Text="{Binding AppState.SelectedTab.SelectedItem.Item.Name}" Foreground="{StaticResource AccentForegroundBrush}" />
@@ -123,7 +144,7 @@
<local:PathPresenter Margin="5,0,0,0" DataContext="{Binding CurrentLocation.Container.FullName}"/>
</StackPanel>
<Rectangle Fill="{DynamicResource ForegroundBrush}" Grid.Row="1" IsVisible="{Binding IsSelected}"/>
</Grid>
</DataTemplate>
@@ -132,7 +153,7 @@
</Grid>
<Grid
Grid.Row="1"
Grid.Row="2"
Margin="20,0,0,0">
<Grid>
<Grid.ColumnDefinitions>
@@ -146,8 +167,8 @@
<Grid>
<ListBox
Classes="ContentListView"
Items="{Binding AppState.SelectedTab.Parent.Items}"
SelectedItem="{Binding AppState.SelectedTab.CurrentLocation}">
IsEnabled="False"
Items="{Binding AppState.SelectedTab.Parent.Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:ItemView ShowAttributes="False"/>
@@ -202,6 +223,7 @@
<Grid Grid.Column="4">
<ListBox
Classes="ContentListView"
IsEnabled="False"
x:Name="ChildItems"
Items="{Binding AppState.SelectedTab.ChildContainer.Items}"
IsVisible="{Binding AppState.SelectedTab.ChildContainer.Items.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
@@ -223,9 +245,78 @@
</TextBlock>
</Grid>
</Grid>
<Grid
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsVisible="{Binding Inputs, Converter={StaticResource IsNotNullConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ItemsControl
x:Name="InputList"
Items="{Binding Inputs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid MinWidth="400">
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding InputElement.Text}" />
<TextBox
AttachedToVisualTree="InputText_AttachedToVisualTree"
Grid.Column="1"
GotFocus="InputText_GotFocus"
LostFocus="InputText_LostFocus"
KeyDown="InputText_KeyDown"
Text="{Binding Value, Mode=TwoWay}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel
Grid.Row="1"
Orientation="Horizontal">
<Button
Command="{Binding ProcessInputsCommand}"
Content="Ok" />
<Button
Command="{Binding CancelInputsCommand}"
Content="Cancel" />
</StackPanel>
</Grid>
<Grid
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsVisible="{Binding MessageBoxText, Converter={StaticResource IsNotNullConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding MessageBoxText}"/>
<StackPanel
Grid.Row="1"
Orientation="Horizontal">
<Button
Command="{Binding ProcessMessageBoxCommand}"
Content="Yes" />
<Button
Command="{Binding CancelMessageBoxCommand}"
Content="No" />
</StackPanel>
</Grid>
</Grid>
<Grid Grid.Row="2">
<Grid Grid.Row="3">
<Grid IsVisible="{Binding AppState.ViewMode, Converter={StaticResource EqualityConverter}, ConverterParameter=RapidTravel}">
<Grid.RowDefinitions>
<RowDefinition Height="1" />

View File

@@ -1,8 +1,11 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using FileTime.Avalonia.Misc;
using FileTime.Avalonia.ViewModels;
using System.Linq;
namespace FileTime.Avalonia.Views
{
@@ -15,21 +18,13 @@ namespace FileTime.Avalonia.Views
{
if (value != DataContext)
{
if (DataContext is MainPageViewModel currentViewModel)
{
currentViewModel.FocusDefaultElement = null;
}
DataContext = value;
if (value != null)
{
//value.FocusDefaultElement = () => this.FindControl<ListBox>("CurrentItems")?.Focus();
}
}
}
}
private InputElementWrapper? _inputElementWrapper;
public MainWindow()
{
InitializeComponent();
@@ -45,12 +40,58 @@ namespace FileTime.Avalonia.Views
public async void OnKeyDown(object sender, KeyEventArgs e)
{
await ViewModel?.ProcessKeyDown(e.Key, e.KeyModifiers);
if (_inputElementWrapper == null)
{
e.Handled = e.Handled || await ViewModel?.ProcessKeyDown(e.Key, e.KeyModifiers);
}
}
public async void OnKeyUp(object sender, KeyEventArgs e)
{
await ViewModel?.ProcessKeyUp(e.Key, e.KeyModifiers);
if (_inputElementWrapper == null)
{
e.Handled = e.Handled || await ViewModel?.ProcessKeyUp(e.Key, e.KeyModifiers);
}
}
private void InputText_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter && _inputElementWrapper == ViewModel!.Inputs.Last())
{
ViewModel.ProcessInputs();
_inputElementWrapper = null;
e.Handled = true;
}
else if (e.Key == Key.Escape && _inputElementWrapper == ViewModel!.Inputs.Last())
{
ViewModel.CancelInputs();
_inputElementWrapper = null;
e.Handled = true;
}
}
private void InputText_GotFocus(object sender, GotFocusEventArgs e)
{
if (sender is TextBox inputText && inputText.DataContext is InputElementWrapper inputElementWrapper)
{
_inputElementWrapper = inputElementWrapper;
}
}
private void InputText_LostFocus(object sender, RoutedEventArgs e)
{
if (sender is TextBox inputText && inputText.DataContext is InputElementWrapper inputElementWrapper)
{
_inputElementWrapper = null;
}
}
private void InputText_AttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs args)
{
if (sender is TextBox inputText && inputText.DataContext is InputElementWrapper inputElementWrapper && inputElementWrapper == ViewModel!.Inputs.First())
{
inputText.Focus();
}
}
}
}