Type based attributes
This commit is contained in:
4
.vscode/tasks.json
vendored
4
.vscode/tasks.json
vendored
@@ -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"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
19
src/GuiApp/FileTime.Avalonia/Converters/DateTimeConverter.cs
Normal file
19
src/GuiApp/FileTime.Avalonia/Converters/DateTimeConverter.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
{
|
{
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
9
src/GuiApp/FileTime.Avalonia/Models/AttibuteType.cs
Normal file
9
src/GuiApp/FileTime.Avalonia/Models/AttibuteType.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace FileTime.Avalonia.Models
|
||||||
|
{
|
||||||
|
public enum AttibuteType
|
||||||
|
{
|
||||||
|
LocalFile,
|
||||||
|
Element,
|
||||||
|
Container
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,18 +237,27 @@ 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;
|
||||||
|
|
||||||
if (allowResuse)
|
if (allowResuse)
|
||||||
{
|
{
|
||||||
var existingViewModel = _items?.FirstOrDefault(i => i.Item == item);
|
var existingViewModel = _items?.FirstOrDefault(i => i.Item == item);
|
||||||
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)
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user