Type based attributes

This commit is contained in:
2022-02-07 17:56:00 +01:00
parent b6d57869b1
commit 2b5ab06b57
10 changed files with 162 additions and 58 deletions

4
.vscode/tasks.json vendored
View File

@@ -72,7 +72,7 @@
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{ {
"label": "publish windows gui", "label": "publish gui",
"command": "dotnet", "command": "dotnet",
"type": "process", "type": "process",
"args": [ "args": [
@@ -80,8 +80,6 @@
"${workspaceFolder}/src/GuiApp/FileTime.Avalonia/FileTime.Avalonia.csproj", "${workspaceFolder}/src/GuiApp/FileTime.Avalonia/FileTime.Avalonia.csproj",
"-c", "-c",
"Release", "Release",
"-r",
"win-x64"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },

View File

@@ -129,7 +129,9 @@
<converters:IsEmptyConverter x:Key="IsNotEmptyConverter" Inverse="true"/> <converters:IsEmptyConverter x:Key="IsNotEmptyConverter" Inverse="true"/>
<converters:ExceptionToStringConverter x:Key="ExceptionToStringConverter"/> <converters:ExceptionToStringConverter x:Key="ExceptionToStringConverter"/>
<converters:BoolInverter x:Key="BoolInverter"/> <converters:BoolInverter x:Key="BoolInverter"/>
<converters:IsElementConverter x:Key="IsElementConverter"/> <converters:DateTimeConverter x:Key="DateTimeConverter"/>
<converters:IsTypeConverter x:Key="IsTypeConverter"/>
<converters:ItemViewModelIsAttibuteTypeConverter x:Key="ItemViewModelIsAttibuteTypeConverter"/>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>

View File

@@ -0,0 +1,19 @@
using System;
using System.Globalization;
using Avalonia.Data.Converters;
namespace FileTime.Avalonia.Converters
{
public class DateTimeConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
value is DateTime dateTime && parameter is string parameterS
? dateTime.ToString(parameterS)
: value;
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,13 +1,13 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using FileTime.Core.Models;
namespace FileTime.Avalonia.Converters namespace FileTime.Avalonia.Converters
{ {
public class IsElementConverter : IValueConverter public class IsTypeConverter : IValueConverter
{ {
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) => value is IElement; public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
parameter is Type type && type.IsInstanceOfType(value);
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {

View File

@@ -0,0 +1,40 @@
using System;
using System.Globalization;
using Avalonia.Data.Converters;
using FileTime.Avalonia.Models;
using FileTime.Avalonia.ViewModels;
using FileTime.Providers.Local;
namespace FileTime.Avalonia.Converters
{
public class ItemViewModelIsAttibuteTypeConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return parameter is AttibuteType targetAttribute && GetAttibuteType(value) == targetAttribute;
}
private static AttibuteType? GetAttibuteType(object? value)
{
if (value is ElementViewModel elementVM)
{
if (elementVM.Element is LocalFile)
{
return AttibuteType.LocalFile;
}
return AttibuteType.Element;
}
else if (value is ContainerViewModel)
{
return AttibuteType.Container;
}
return null;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -5,6 +5,17 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ApplicationIcon>Assets\filetime.ico</ApplicationIcon> <ApplicationIcon>Assets\filetime.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<EnvironmentName>Development</EnvironmentName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' != 'Debug'">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<EnvironmentName>Production</EnvironmentName>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Models\" /> <Folder Include="Models\" />
<AvaloniaResource Include="Assets\**" /> <AvaloniaResource Include="Assets\**" />
@@ -46,9 +57,7 @@
<DependentUpon>MainWindow.axaml</DependentUpon> <DependentUpon>MainWindow.axaml</DependentUpon>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup Condition="'$(Configuration)' == 'Debug'">
<None Update="appsettings.Development.json"> <Content Include="appsettings.Development.json" CopyToOutputDirectory="PreserveNewest" />
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,9 @@
namespace FileTime.Avalonia.Models
{
public enum AttibuteType
{
LocalFile,
Element,
Container
}
}

View File

@@ -158,8 +158,31 @@ namespace FileTime.Avalonia.ViewModels
{ {
_isRefreshing = true; _isRefreshing = true;
var containers = (await _container.GetContainers())!.Select(c => AdoptOrReuseOrCreateItem(c, alloweReuse , (c2) => new ContainerViewModel(_newItemProcessor, this, c2, ItemNameConverterService))).ToList(); List<ContainerViewModel> newContainers = new List<ContainerViewModel>();
var elements = (await _container.GetElements())!.Select(e => AdoptOrReuseOrCreateItem(e, alloweReuse , (e2) => new ElementViewModel(e2, this, ItemNameConverterService))).ToList(); List<ElementViewModel> newElements = new List<ElementViewModel>();
if (await _container.GetContainers() is IReadOnlyList<IContainer> containers)
{
foreach (var container in containers)
{
newContainers.Add(await AdoptOrReuseOrCreateItem(container, alloweReuse, (c2) => new ContainerViewModel(_newItemProcessor, this, c2, ItemNameConverterService)));
}
}
if(await _container.GetElements() is IReadOnlyList<IElement> elements)
{
foreach(var element in elements)
{
var generator = async (IElement e) =>
{
var element = new ElementViewModel(e, this, ItemNameConverterService);
await element.Init();
return element;
};
newElements.Add(await AdoptOrReuseOrCreateItem(element, alloweReuse, generator));
}
}
if (token.IsCancellationRequested) return; if (token.IsCancellationRequested) return;
@@ -167,55 +190,22 @@ namespace FileTime.Avalonia.ViewModels
if (initializeChildren) if (initializeChildren)
{ {
foreach (var container in containers) foreach (var container in newContainers)
{ {
if (token.IsCancellationRequested) return; if (token.IsCancellationRequested) return;
await container.Init(false, token); await container.Init(false, token);
} }
} }
/*var containersToAdd = containers.Except(_containers).ToList(); var containersToRemove = _containers.Except(newContainers).ToList();
var containersToRemove = _containers.Except(containers).ToList();
var elementsToAdd = elements.Except(_elements).ToList();
var elementsToRemove = _elements.Except(elements).ToList();
foreach (var containerToRemove in containersToRemove)
{
Containers.Remove(containerToRemove);
Items.Remove(containerToRemove);
containerToRemove?.Dispose();
}
foreach (var elementToRemove in elementsToRemove)
{
Elements.Remove(elementToRemove);
Items.Remove(elementToRemove);
}
foreach (var containerToAdd in containersToAdd)
{
Containers.Insert(GetNewItemPosition(containerToAdd, Containers), containerToAdd);
Items.Insert(GetNewItemPosition(containerToAdd, Items), containerToAdd);
}
foreach (var elementToAdd in elementsToAdd)
{
Elements.Insert(GetNewItemPosition(elementToAdd, Elements), elementToAdd);
Items.Insert(GetNewItemPosition(elementToAdd, Items), elementToAdd);
}*/
var containersToRemove = _containers.Except(containers).ToList();
foreach (var containerToRemove in containersToRemove) foreach (var containerToRemove in containersToRemove)
{ {
containerToRemove?.Dispose(); containerToRemove?.Dispose();
} }
Containers = new ObservableCollection<ContainerViewModel>(containers); Containers = new ObservableCollection<ContainerViewModel>(newContainers);
Elements = new ObservableCollection<ElementViewModel>(elements); Elements = new ObservableCollection<ElementViewModel>(newElements);
Items = new ObservableCollection<IItemViewModel>(containers.Cast<IItemViewModel>().Concat(elements)); Items = new ObservableCollection<IItemViewModel>(newContainers.Cast<IItemViewModel>().Concat(newElements));
for (var i = 0; i < Items.Count; i++) for (var i = 0; i < Items.Count; i++)
{ {
@@ -247,7 +237,16 @@ namespace FileTime.Avalonia.ViewModels
return i; return i;
} }
private TResult AdoptOrReuseOrCreateItem<T, TResult>(T item, bool allowResuse, Func<T, TResult> generator) where T : class, IItem private async Task<TResult> AdoptOrReuseOrCreateItem<T, TResult>(T item, bool allowResuse, Func<T, TResult> generator) where T : class, IItem
{
return await AdoptOrReuseOrCreateItem(item, allowResuse, Helper);
Task<TResult> Helper(T item)
{
return Task.FromResult(generator(item));
}
}
private async Task<TResult> AdoptOrReuseOrCreateItem<T, TResult>(T item, bool allowResuse, Func<T, Task<TResult>> generator) where T : class, IItem
{ {
var itemToAdopt = ChildrenToAdopt.Find(i => i.Item == item); var itemToAdopt = ChildrenToAdopt.Find(i => i.Item == item);
if (itemToAdopt is TResult itemViewModel) return itemViewModel; if (itemToAdopt is TResult itemViewModel) return itemViewModel;
@@ -258,7 +257,7 @@ namespace FileTime.Avalonia.ViewModels
if (existingViewModel is TResult itemViewModelToReuse) return itemViewModelToReuse; if (existingViewModel is TResult itemViewModelToReuse) return itemViewModelToReuse;
} }
return generator(item); return await generator(item);
} }
public void Unload(bool recursive = true) public void Unload(bool recursive = true)

View File

@@ -3,6 +3,7 @@ using FileTime.Avalonia.Models;
using FileTime.Avalonia.Services; using FileTime.Avalonia.Services;
using MvvmGen; using MvvmGen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
namespace FileTime.Avalonia.ViewModels namespace FileTime.Avalonia.ViewModels
{ {
@@ -27,6 +28,9 @@ namespace FileTime.Avalonia.ViewModels
[Property] [Property]
private ContainerViewModel? _parent; private ContainerViewModel? _parent;
[Property]
private long _size;
[PropertyInvalidate(nameof(IsSelected))] [PropertyInvalidate(nameof(IsSelected))]
[PropertyInvalidate(nameof(IsAlternative))] [PropertyInvalidate(nameof(IsAlternative))]
[PropertyInvalidate(nameof(IsMarked))] [PropertyInvalidate(nameof(IsMarked))]
@@ -50,5 +54,10 @@ namespace FileTime.Avalonia.ViewModels
} }
public void InvalidateDisplayName() => OnPropertyChanged(nameof(DisplayName)); public void InvalidateDisplayName() => OnPropertyChanged(nameof(DisplayName));
public async Task Init()
{
Size = await _element.GetElementSize();
}
} }
} }

View File

@@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="FileTime.Avalonia.Views.ItemView" x:Class="FileTime.Avalonia.Views.ItemView"
xmlns:models="using:FileTime.Avalonia.Models"
x:Name="ItemRoot" Background="{Binding ViewMode,Converter={StaticResource ItemViewModeToBackgroundConverter}}"> x:Name="ItemRoot" Background="{Binding ViewMode,Converter={StaticResource ItemViewModeToBackgroundConverter}}">
<Grid ColumnDefinitions="Auto,*,Auto" Margin="3"> <Grid ColumnDefinitions="Auto,*,Auto" Margin="3">
<Grid.Styles> <Grid.Styles>
@@ -40,9 +41,27 @@
</ItemsControl> </ItemsControl>
<Grid Grid.Column="2" IsVisible="{Binding ShowAttributes,ElementName=ItemRoot}"> <Grid Grid.Column="2" IsVisible="{Binding ShowAttributes,ElementName=ItemRoot}">
<Grid ColumnDefinitions="180,45"> <Grid ColumnDefinitions="90,90,40,45"
<TextBlock Text="{Binding Item.CreatedAt}"/> IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttibuteTypeConverter},ConverterParameter={x:Static models:AttibuteType.LocalFile}}">
<TextBlock Grid.Column="1" Text="{Binding Item.Attributes}"/>
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}"/>
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="1" Text="{Binding Item.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=yyyy-MM-dd}"/>
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="2" Text="{Binding Item.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=hh:mm}"/>
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="3" Text="{Binding Item.Attributes}"/>
</Grid>
<Grid ColumnDefinitions="90,40,45"
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttibuteTypeConverter},ConverterParameter={x:Static models:AttibuteType.Container}}">
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Text="{Binding Item.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=yyyy-MM-dd}"/>
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="1" Text="{Binding Item.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=hh:mm}"/>
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="2" Text="{Binding Item.Attributes}"/>
</Grid>
<Grid ColumnDefinitions="90,40,45"
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttibuteTypeConverter},ConverterParameter={x:Static models:AttibuteType.Element}}">
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Text="{Binding Item.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=yyyy-MM-dd}"/>
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="1" Text="{Binding Item.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=hh:mm}"/>
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="2" Text="{Binding Item.Attributes}"/>
</Grid> </Grid>
</Grid> </Grid>
</Grid> </Grid>