async archive loading

This commit is contained in:
Paddy Xu
2017-12-22 13:19:49 +02:00
parent dedac98702
commit 97b749e64f
5 changed files with 157 additions and 55 deletions

View File

@@ -46,7 +46,7 @@ namespace QuickLook.Plugin.ArchiveViewer
{ {
treeGrid.DataContext = context; treeGrid.DataContext = context;
treeView.Loaded += (sender, e) => treeView.LayoutUpdated += (sender, e) =>
{ {
// return when empty // return when empty
if (treeView.Items.Count == 0) if (treeView.Items.Count == 0)

View File

@@ -7,21 +7,42 @@
mc:Ignorable="d" mc:Ignorable="d"
x:Name="infoPanel" x:Name="infoPanel"
d:DesignHeight="600" d:DesignWidth="800"> d:DesignHeight="600" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- only for design -->
<ResourceDictionary Source="/QuickLook;component/Styles/MainWindowStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
<local:Percent100ToVisibilityVisibleConverter x:Key="Percent100ToVisibilityVisibleConverter" />
<local:Percent100ToVisibilityCollapsedConverter x:Key="Percent100ToVisibilityCollapsedConverter" />
</ResourceDictionary>
</UserControl.Resources>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid ZIndex="9999"
<RowDefinition /> Visibility="{Binding ElementName=infoPanel, Path=LoadPercent, Mode=OneWay, Converter={StaticResource Percent100ToVisibilityCollapsedConverter}}">
<RowDefinition Height="30" /> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
</Grid.RowDefinitions> <Label FontSize="14" HorizontalAlignment="Center">Loading archive ...</Label>
<local:ArchiveFileListView Grid.Row="0" x:Name="fileListView" Focusable="False" /> <ProgressBar Height="13" Width="150"
<Grid Grid.Row="1"> Value="{Binding ElementName=infoPanel, Path=LoadPercent, Mode=OneWay}" />
<Grid.ColumnDefinitions> </StackPanel>
<ColumnDefinition Width="40*" /> </Grid>
<ColumnDefinition Width="30*" /> <Grid
<ColumnDefinition Width="30*" /> Visibility="{Binding ElementName=infoPanel, Path=LoadPercent, Mode=OneWay, Converter={StaticResource Percent100ToVisibilityVisibleConverter}}">
</Grid.ColumnDefinitions> <Grid.RowDefinitions>
<Label x:Name="archiveCount" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center">9 folders and 10 files, solid, password-protected</Label> <RowDefinition />
<Label x:Name="archiveSizeC" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center">Compressed size 9999 bytes</Label> <RowDefinition Height="30" />
<Label x:Name="archiveSizeU" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center">Uncompressed size 99999 bytes</Label> </Grid.RowDefinitions>
<local:ArchiveFileListView Grid.Row="0" x:Name="fileListView" Focusable="False" />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40*" />
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="30*" />
</Grid.ColumnDefinitions>
<Label x:Name="archiveCount" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center">0 folders and 0 files, solid, password-protected</Label>
<Label x:Name="archiveSizeC" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center">Compressed size 0 bytes</Label>
<Label x:Name="archiveSizeU" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center">Uncompressed size 0 bytes</Label>
</Grid>
</Grid> </Grid>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -17,9 +17,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows.Controls; using System.Windows.Controls;
using QuickLook.Annotations;
using QuickLook.ExtensionMethods; using QuickLook.ExtensionMethods;
using SharpCompress.Archives; using SharpCompress.Archives;
using SharpCompress.Common; using SharpCompress.Common;
@@ -30,9 +34,11 @@ namespace QuickLook.Plugin.ArchiveViewer
/// <summary> /// <summary>
/// Interaction logic for ArchiveInfoPanel.xaml /// Interaction logic for ArchiveInfoPanel.xaml
/// </summary> /// </summary>
public partial class ArchiveInfoPanel : UserControl, IDisposable public partial class ArchiveInfoPanel : UserControl, IDisposable, INotifyPropertyChanged
{ {
private readonly Dictionary<string, ArchiveFileEntry> _fileEntries = new Dictionary<string, ArchiveFileEntry>(); private readonly Dictionary<string, ArchiveFileEntry> _fileEntries = new Dictionary<string, ArchiveFileEntry>();
private bool _disposed;
private double _loadPercent;
private ulong _totalZippedSize; private ulong _totalZippedSize;
private string _type; private string _type;
@@ -40,58 +46,88 @@ namespace QuickLook.Plugin.ArchiveViewer
{ {
InitializeComponent(); InitializeComponent();
LoadArchive(path); // design-time only
Resources.MergedDictionaries.Clear();
fileListView.SetDataContext(_fileEntries[""].Children.Keys); BeginLoadArchive(path);
}
public double LoadPercent
{
get => _loadPercent;
private set
{
if (value == _loadPercent) return;
_loadPercent = value;
OnPropertyChanged();
}
} }
public void Dispose() public void Dispose()
{ {
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
_fileEntries.Clear(); _disposed = true;
fileListView.Dispose(); fileListView.Dispose();
} }
public event PropertyChangedEventHandler PropertyChanged;
~ArchiveInfoPanel() ~ArchiveInfoPanel()
{ {
Dispose(); Dispose();
} }
private void LoadArchive(string path) private void BeginLoadArchive(string path)
{ {
_totalZippedSize = (ulong) new FileInfo(path).Length; new Task(() =>
LoadItemsFromArchive(path);
var folders = -1; // do not count root node
var files = 0;
ulong sizeU = 0L;
_fileEntries.ForEach(e =>
{ {
if (e.Value.IsFolder) _totalZippedSize = (ulong) new FileInfo(path).Length;
folders++;
var root = new ArchiveFileEntry(Path.GetFileName(path), true);
_fileEntries.Add("", root);
LoadItemsFromArchive(path);
var folders = -1; // do not count root node
var files = 0;
ulong sizeU = 0L;
_fileEntries.ForEach(e =>
{
if (e.Value.IsFolder)
folders++;
else
files++;
sizeU += e.Value.Size;
});
string t;
var d = folders != 0 ? $"{folders} folders" : string.Empty;
var f = files != 0 ? $"{files} files" : string.Empty;
if (!string.IsNullOrEmpty(d) && !string.IsNullOrEmpty(f))
t = $", {d} and {f}";
else if (string.IsNullOrEmpty(d) && string.IsNullOrEmpty(f))
t = string.Empty;
else else
files++; t = $", {d}{f}";
sizeU += e.Value.Size; Dispatcher.Invoke(() =>
}); {
if (_disposed)
return;
string t; fileListView.SetDataContext(_fileEntries[""].Children.Keys);
var d = folders != 0 ? $"{folders} folders" : string.Empty; archiveCount.Content =
var f = files != 0 ? $"{files} files" : string.Empty; $"{_type} archive{t}";
if (!string.IsNullOrEmpty(d) && !string.IsNullOrEmpty(f)) archiveSizeC.Content =
t = $", {d} and {f}"; $"Compressed size {((long) _totalZippedSize).ToPrettySize(2)}";
else if (string.IsNullOrEmpty(d) && string.IsNullOrEmpty(f)) archiveSizeU.Content = $"Uncompressed size {((long) sizeU).ToPrettySize(2)}";
t = string.Empty; });
else
t = $", {d}{f}";
archiveCount.Content = LoadPercent = 100d;
$"{_type} archive{t}"; }).Start();
archiveSizeC.Content =
$"Compressed size {((long) _totalZippedSize).ToPrettySize(2)}";
archiveSizeU.Content = $"Uncompressed size {((long) sizeU).ToPrettySize(2)}";
} }
private void LoadItemsFromArchive(string path) private void LoadItemsFromArchive(string path)
@@ -107,11 +143,13 @@ namespace QuickLook.Plugin.ArchiveViewer
_type = reader.ArchiveType.ToString(); _type = reader.ArchiveType.ToString();
var root = new ArchiveFileEntry(Path.GetFileName(path), true);
_fileEntries.Add("", root);
while (reader.MoveToNextEntry()) while (reader.MoveToNextEntry())
{
if (_disposed)
return;
LoadPercent = 100d * stream.Position / stream.Length;
ProcessByLevel(reader.Entry); ProcessByLevel(reader.Entry);
}
} }
else else
{ {
@@ -119,11 +157,13 @@ namespace QuickLook.Plugin.ArchiveViewer
_type = archive.Type.ToString(); _type = archive.Type.ToString();
var root = new ArchiveFileEntry(Path.GetFileName(path), true);
_fileEntries.Add("", root);
foreach (var entry in archive.Entries) foreach (var entry in archive.Entries)
{
if (_disposed)
return;
LoadPercent = 100d * stream.Position / stream.Length;
ProcessByLevel(entry); ProcessByLevel(entry);
}
} }
} }
} }
@@ -140,7 +180,7 @@ namespace QuickLook.Plugin.ArchiveViewer
if (_fileEntries.ContainsKey(f)) if (_fileEntries.ContainsKey(f))
return; return;
_fileEntries.TryGetValue(GetDirectoryName(f), out ArchiveFileEntry parent); _fileEntries.TryGetValue(GetDirectoryName(f), out var parent);
var afe = new ArchiveFileEntry(Path.GetFileName(f), true, parent); var afe = new ArchiveFileEntry(Path.GetFileName(f), true, parent);
@@ -152,7 +192,7 @@ namespace QuickLook.Plugin.ArchiveViewer
{ {
var file = pf.Last(); var file = pf.Last();
_fileEntries.TryGetValue(GetDirectoryName(file), out ArchiveFileEntry parent); _fileEntries.TryGetValue(GetDirectoryName(file), out var parent);
_fileEntries.Add(file, new ArchiveFileEntry(Path.GetFileName(entry.Key), false, parent) _fileEntries.Add(file, new ArchiveFileEntry(Path.GetFileName(entry.Key), false, parent)
{ {
@@ -179,5 +219,11 @@ namespace QuickLook.Plugin.ArchiveViewer
return frags.Select((s, i) => frags.Take(i + 1).Aggregate((a, b) => a + "\\" + b)).ToArray(); return frags.Select((s, i) => frags.Take(i + 1).Aggregate((a, b) => a + "\\" + b)).ToArray();
} }
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
} }
} }

View File

@@ -23,6 +23,40 @@ using QuickLook.ExtensionMethods;
namespace QuickLook.Plugin.ArchiveViewer namespace QuickLook.Plugin.ArchiveViewer
{ {
public class Percent100ToVisibilityVisibleConverter : DependencyObject, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
value = 0;
var percent = (double) value;
return Math.Abs(percent - 100) < 0.00001 ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class Percent100ToVisibilityCollapsedConverter : DependencyObject, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
value = 0;
var percent = (double) value;
return Math.Abs(percent - 100) < 0.00001 ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class LevelToIndentConverter : DependencyObject, IMultiValueConverter public class LevelToIndentConverter : DependencyObject, IMultiValueConverter
{ {
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)

View File

@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="SharpCompress" version="0.18.2" targetFramework="net462" /> <package id="SharpCompress" version="0.18.2" targetFramework="net462" />
<package id="UTF.Unknown" version="1.0.0-beta1" targetFramework="net462" /> <package id="UTF.Unknown" version="1.0.0-beta1" targetFramework="net462" />