Item name "..."

This commit is contained in:
2022-02-14 12:07:17 +01:00
parent 9661155cdd
commit b11d009195
11 changed files with 188 additions and 24 deletions

View File

@@ -135,6 +135,7 @@
<converters:ItemViewModelIsAttibuteTypeConverter x:Key="ItemViewModelIsNotAttibuteTypeConverter" Invert="true"/> <converters:ItemViewModelIsAttibuteTypeConverter x:Key="ItemViewModelIsNotAttibuteTypeConverter" Invert="true"/>
<converters:GetFileExtensionConverter x:Key="GetFileExtensionConverter"/> <converters:GetFileExtensionConverter x:Key="GetFileExtensionConverter"/>
<converters:CommandToCommandNameConverter x:Key="CommandToCommandNameConverter"/> <converters:CommandToCommandNameConverter x:Key="CommandToCommandNameConverter"/>
<converters:NamePartShrinkerConverter x:Key="NamePartShrinkerConverter"/>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>
@@ -158,11 +159,14 @@
</Style> </Style>
<Style Selector="ListBox.ContentListView"> <Style Selector="ListBox.ContentListView">
<Setter Property="Background" Value="Transparent"/> <Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
</Style> </Style>
<Style Selector="ListBox.ContentListView > ListBoxItem"> <Style Selector="ListBox.ContentListView > ListBoxItem">
<Setter Property="Margin" Value="0"/> <Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/> <Setter Property="Padding" Value="0"/>
<Setter Property="Background" Value="Transparent"/> <Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="ContextMenu"> <Setter Property="ContextMenu">
<ContextMenu Items="{Binding Converter={StaticResource ContextMenuGenerator}}"/> <ContextMenu Items="{Binding Converter={StaticResource ContextMenuGenerator}}"/>
</Setter> </Setter>

View File

@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Avalonia.Data.Converters;
using Avalonia.Media;
using FileTime.Avalonia.Models;
using FileTime.Avalonia.ViewModels;
using FileTime.Avalonia.Views;
namespace FileTime.Avalonia.Converters
{
public class NamePartShrinkerConverter : IMultiValueConverter
{
private const int PixelPerChar = 8;
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
{
if (values.Count > 0 && values[0] is IList<ItemNamePart> nameParts)
{
var newNameParts = nameParts;
//if (values.Count > 1 && values[1] is ItemView itemView && itemView.ShowAttributes && itemView.Bounds.Width > 0)
if (values.Count > 1 && values[1] is double width && width > 0)
{
newNameParts = GetNamePartsForWidth(nameParts, width - 340);
}
return newNameParts.Select(p => new ItemNamePartViewModel(p.Text, p.IsSpecial ? TextDecorations.Underline : null)).ToList();
}
return null;
}
private static List<ItemNamePart> GetNamePartsForWidth(IList<ItemNamePart> nameParts, double maxWidth)
{
//Best case, we are in the range
var textLength = nameParts.Select(p => p.Text.Length).Sum();
if (textLength * PixelPerChar <= maxWidth)
{
return nameParts.ToList();
}
//Trying at least with the special parts
var newNameParts = new ItemNamePart?[nameParts.Count];
for (var i = 0; i < nameParts.Count; i++)
{
if (nameParts[i].IsSpecial)
{
newNameParts[i] = nameParts[i];
}
}
return GetNamePartsForWidthOptimistic(nameParts, newNameParts, maxWidth);
}
private static List<ItemNamePart> GetNamePartsForWidthOptimistic(IList<ItemNamePart> nameParts, ItemNamePart?[] newNameParts, double maxWidth)
{
var trimmedIndexes = new List<int>();
for (var i = 0; i < newNameParts.Length; i++)
{
if (newNameParts[i] == null)
{
trimmedIndexes.Add(i);
newNameParts[i] = new ItemNamePart("...");
}
}
var textLength = newNameParts.Select(p => p?.Text.Length ?? 0).Sum();
if (textLength * PixelPerChar > maxWidth)
{
return GetNamePartsForWidthPessimistic(nameParts, maxWidth);
}
foreach (var trimmedIndex in trimmedIndexes)
{
var baseTextLength = newNameParts.Select((p, i) => i == trimmedIndex ? 0 : (p?.Text.Length ?? 0)).Sum();
var proposedText = nameParts[trimmedIndex].Text;
var trimmed = false;
while ((baseTextLength + proposedText.Length + (trimmed ? 3 : 0)) * PixelPerChar > maxWidth)
{
proposedText = proposedText[0..^1];
trimmed = true;
}
newNameParts[trimmedIndex] = new ItemNamePart(proposedText + (trimmed ? "..." : ""));
if (trimmed) break;
}
return newNameParts.OfType<ItemNamePart>().ToList();
}
private static List<ItemNamePart> GetNamePartsForWidthPessimistic(IList<ItemNamePart> nameParts, double maxWidth)
{
var newNameParts = new List<ItemNamePart>(nameParts);
foreach (var namePart in nameParts)
{
var baseTextLength = newNameParts.Select(p => p.Text.Length).Sum();
var proposedText = namePart.Text;
var trimmed = false;
while ((baseTextLength + 3) * PixelPerChar > maxWidth && proposedText != "")
{
proposedText = proposedText[0..^1];
trimmed = true;
}
if (!string.IsNullOrWhiteSpace(proposedText)) newNameParts.Add(new ItemNamePart(proposedText, namePart.IsSpecial));
if (trimmed) break;
}
if (newNameParts.Last().IsSpecial)
{
newNameParts.Add(new ItemNamePart("..."));
}
else
{
var last = newNameParts.Last();
last.Text += "...";
}
return newNameParts;
}
}
}

View File

@@ -4,6 +4,7 @@
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ApplicationIcon>Assets\filetime.ico</ApplicationIcon> <ApplicationIcon>Assets\filetime.ico</ApplicationIcon>
<Version>0.0.1</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'"> <PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>

View File

@@ -1,18 +1,14 @@
using Avalonia.Media; namespace FileTime.Avalonia.Models
using System;
using System.Collections.Generic;
using System.Text;
namespace FileTime.Avalonia.Models
{ {
public class ItemNamePart public class ItemNamePart
{ {
public string Text { get; set; } public string Text { get; set; }
public TextDecorationCollection? TextDecorations { get; set; } public bool IsSpecial { get; set; }
public ItemNamePart(string text) public ItemNamePart(string text, bool isSpecial = false)
{ {
Text = text; Text = text;
IsSpecial = isSpecial;
} }
} }
} }

View File

@@ -64,6 +64,7 @@ namespace FileTime.Avalonia.Services
{Commands.CreateElement, CreateElement}, {Commands.CreateElement, CreateElement},
{Commands.Cut, Cut}, {Commands.Cut, Cut},
{Commands.EnterRapidTravel, EnterRapidTravelMode}, {Commands.EnterRapidTravel, EnterRapidTravelMode},
{Commands.Edit, Edit},
{Commands.GoToHome, GotToHome}, {Commands.GoToHome, GotToHome},
{Commands.GoToPath, GoToContainer}, {Commands.GoToPath, GoToContainer},
{Commands.GoToProvider, GotToProvider}, {Commands.GoToProvider, GotToProvider},
@@ -779,5 +780,10 @@ namespace FileTime.Avalonia.Services
return Task.CompletedTask; return Task.CompletedTask;
} }
private Task Edit()
{
return Task.CompletedTask;
}
} }
} }

View File

@@ -35,7 +35,7 @@ namespace FileTime.Avalonia.Services
nameParts.Add(new ItemNamePart(before)); nameParts.Add(new ItemNamePart(before));
} }
nameParts.Add(new ItemNamePart(rapidTravel) { TextDecorations = TextDecorations.Underline }); nameParts.Add(new ItemNamePart(rapidTravel, true));
} }
if (nameLeft.Length > 0) if (nameLeft.Length > 0)

View File

@@ -0,0 +1,16 @@
using Avalonia.Media;
namespace FileTime.Avalonia.ViewModels
{
public class ItemNamePartViewModel
{
public string Text { get; set; }
public TextDecorationCollection? TextDecorations { get; set; }
public ItemNamePartViewModel(string text, TextDecorationCollection? textDecorations)
{
Text = text;
TextDecorations = textDecorations;
}
}
}

View File

@@ -21,6 +21,7 @@ using FileTime.Avalonia.IconProviders;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Threading; using System.Threading;
using Avalonia.Input; using Avalonia.Input;
using System.Reflection;
namespace FileTime.Avalonia.ViewModels namespace FileTime.Avalonia.ViewModels
{ {
@@ -52,9 +53,24 @@ namespace FileTime.Avalonia.ViewModels
[Property] [Property]
private bool _loading = true; private bool _loading = true;
public string Title { get; private set; }
async partial void OnInitialize() async partial void OnInitialize()
{ {
_logger?.LogInformation($"Starting {nameof(MainPageViewModel)} initialization..."); _logger?.LogInformation($"Starting {nameof(MainPageViewModel)} initialization...");
var version = Assembly.GetExecutingAssembly().GetName().Version;
var versionString = "Unknwon version";
if (version != null)
{
versionString = $"{version.Major}.{version.Minor}.{version.Build}";
if (version.Revision != 0)
{
versionString += $" ({version.Revision})";
}
}
Title = "FileTime " + versionString;
_timeRunner = App.ServiceProvider.GetService<TimeRunner>()!; _timeRunner = App.ServiceProvider.GetService<TimeRunner>()!;
var inputInterface = (BasicInputHandler)App.ServiceProvider.GetService<IInputInterface>()!; var inputInterface = (BasicInputHandler)App.ServiceProvider.GetService<IInputInterface>()!;
inputInterface.InputHandler = DialogService.ReadInputs; inputInterface.InputHandler = DialogService.ReadInputs;

View File

@@ -5,15 +5,17 @@
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" xmlns:models="using:FileTime.Avalonia.Models"
x:Name="ItemRoot" Background="{Binding ViewMode,Converter={StaticResource ItemViewModeToBackgroundConverter}}"> x:Name="ItemRoot"
<Grid ColumnDefinitions="Auto,*,Auto" Margin="3"> HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="{Binding ViewMode,Converter={StaticResource ItemViewModeToBackgroundConverter}}">
<Grid ColumnDefinitions="20,*,300" Margin="3" x:Name="RootGrid">
<Grid.Styles> <Grid.Styles>
<Style Selector="TextBlock"> <Style Selector="TextBlock">
<Setter Property="Foreground" Value="{Binding DataContext.ViewMode,Converter={StaticResource ItemViewModeToForegroundConverter},ElementName=ItemRoot}"/> <Setter Property="Foreground" Value="{Binding DataContext.ViewMode,Converter={StaticResource ItemViewModeToForegroundConverter},ElementName=ItemRoot}"/>
</Style> </Style>
</Grid.Styles> </Grid.Styles>
<Image <Image
Grid.RowSpan="2"
Width="18" Width="18"
Height="18" Height="18"
HorizontalAlignment="Left" HorizontalAlignment="Left"
@@ -24,7 +26,15 @@
Margin="5,0,0,0" Margin="5,0,0,0"
Grid.Column="1" Grid.Column="1"
VerticalAlignment="Center" VerticalAlignment="Center"
Items="{Binding DisplayName}"> HorizontalAlignment="Stretch">
<ItemsControl.Items>
<MultiBinding Converter="{StaticResource NamePartShrinkerConverter}">
<MultiBinding.Bindings>
<Binding Path="DisplayName"/>
<Binding ElementName="RootGrid" Path="Bounds.Width"/>
</MultiBinding.Bindings>
</MultiBinding>
</ItemsControl.Items>
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" /> <StackPanel Orientation="Horizontal" />
@@ -42,7 +52,8 @@
</ItemsControl> </ItemsControl>
<Grid Grid.Column="2" IsVisible="{Binding ShowAttributes,ElementName=ItemRoot}"> <Grid Grid.Column="2" IsVisible="{Binding ShowAttributes,ElementName=ItemRoot}">
<Grid ColumnDefinitions="30,50,90,40,45" <Grid ColumnDefinitions="50,50,90,40,45"
HorizontalAlignment="Right"
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttibuteTypeConverter},ConverterParameter={x:Static models:AttibuteType.LocalFile}}"> IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttibuteTypeConverter},ConverterParameter={x:Static models:AttibuteType.LocalFile}}">
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Text="{Binding Item.Name, Converter={StaticResource GetFileExtensionConverter}}"/> <TextBlock HorizontalAlignment="Right" Classes="SmallText" Text="{Binding Item.Name, Converter={StaticResource GetFileExtensionConverter}}"/>
@@ -52,6 +63,7 @@
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="4" Text="{Binding Item.Attributes}"/> <TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="4" Text="{Binding Item.Attributes}"/>
</Grid> </Grid>
<Grid ColumnDefinitions="90,40,45" <Grid ColumnDefinitions="90,40,45"
HorizontalAlignment="Right"
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttibuteTypeConverter},ConverterParameter={x:Static models:AttibuteType.Container}}"> 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" Text="{Binding Item.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=yyyy-MM-dd}"/>
@@ -59,6 +71,7 @@
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="2" Text="{Binding Item.Attributes}"/> <TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="2" Text="{Binding Item.Attributes}"/>
</Grid> </Grid>
<Grid ColumnDefinitions="90,40,45" <Grid ColumnDefinitions="90,40,45"
HorizontalAlignment="Right"
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttibuteTypeConverter},ConverterParameter={x:Static models:AttibuteType.Element}}"> 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" Text="{Binding Item.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=yyyy-MM-dd}"/>

View File

@@ -27,7 +27,7 @@
<Grid PointerPressed="HeaderPointerPressed"> <Grid PointerPressed="HeaderPointerPressed">
<Rectangle Fill="#01000000"/> <Rectangle Fill="#01000000"/>
<TextBlock Margin="15,10" Text="FileTime"/> <TextBlock Margin="15,10" Text="{Binding Title}"/>
</Grid> </Grid>
<Grid Grid.Column="1" PointerPressed="HeaderPointerPressed"> <Grid Grid.Column="1" PointerPressed="HeaderPointerPressed">
@@ -249,13 +249,12 @@
IsTabStop="True" IsTabStop="True"
Items="{Binding AppState.SelectedTab.CurrentLocation.Items^}" Items="{Binding AppState.SelectedTab.CurrentLocation.Items^}"
ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Visible"
SelectedItem="{Binding AppState.SelectedTab.SelectedItem, Mode=TwoWay}" SelectedItem="{Binding AppState.SelectedTab.SelectedItem, Mode=TwoWay}"
SelectionChanged="OnCurrentItemsSelectionChanged"
Classes="ContentListView"> Classes="ContentListView">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>
<local:ItemView/> <local:ItemView HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch"/>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
</ListBox> </ListBox>

View File

@@ -154,11 +154,5 @@ namespace FileTime.Avalonia.Views
BeginMoveDrag(e); BeginMoveDrag(e);
} }
} }
private void OnCurrentItemsSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listBox = this.FindControl<ListBox>("CurrentItems");
listBox.ScrollIntoView(listBox.SelectedIndex);
}
} }
} }