mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-09-12 10:19:07 +00:00
async archive loading
This commit is contained in:
@@ -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)
|
||||||
|
@@ -7,7 +7,27 @@
|
|||||||
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 ZIndex="9999"
|
||||||
|
Visibility="{Binding ElementName=infoPanel, Path=LoadPercent, Mode=OneWay, Converter={StaticResource Percent100ToVisibilityCollapsedConverter}}">
|
||||||
|
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
|
<Label FontSize="14" HorizontalAlignment="Center">Loading archive ...</Label>
|
||||||
|
<ProgressBar Height="13" Width="150"
|
||||||
|
Value="{Binding ElementName=infoPanel, Path=LoadPercent, Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
Visibility="{Binding ElementName=infoPanel, Path=LoadPercent, Mode=OneWay, Converter={StaticResource Percent100ToVisibilityVisibleConverter}}">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
<RowDefinition Height="30" />
|
<RowDefinition Height="30" />
|
||||||
@@ -19,9 +39,10 @@
|
|||||||
<ColumnDefinition Width="30*" />
|
<ColumnDefinition Width="30*" />
|
||||||
<ColumnDefinition Width="30*" />
|
<ColumnDefinition Width="30*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Label x:Name="archiveCount" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center">9 folders and 10 files, solid, password-protected</Label>
|
<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 9999 bytes</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 99999 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>
|
@@ -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,28 +46,48 @@ 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)
|
||||||
|
{
|
||||||
|
new Task(() =>
|
||||||
{
|
{
|
||||||
_totalZippedSize = (ulong) new FileInfo(path).Length;
|
_totalZippedSize = (ulong) new FileInfo(path).Length;
|
||||||
|
|
||||||
|
var root = new ArchiveFileEntry(Path.GetFileName(path), true);
|
||||||
|
_fileEntries.Add("", root);
|
||||||
|
|
||||||
LoadItemsFromArchive(path);
|
LoadItemsFromArchive(path);
|
||||||
|
|
||||||
var folders = -1; // do not count root node
|
var folders = -1; // do not count root node
|
||||||
@@ -87,11 +113,21 @@ namespace QuickLook.Plugin.ArchiveViewer
|
|||||||
else
|
else
|
||||||
t = $", {d}{f}";
|
t = $", {d}{f}";
|
||||||
|
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fileListView.SetDataContext(_fileEntries[""].Children.Keys);
|
||||||
archiveCount.Content =
|
archiveCount.Content =
|
||||||
$"{_type} archive{t}";
|
$"{_type} archive{t}";
|
||||||
archiveSizeC.Content =
|
archiveSizeC.Content =
|
||||||
$"Compressed size {((long) _totalZippedSize).ToPrettySize(2)}";
|
$"Compressed size {((long) _totalZippedSize).ToPrettySize(2)}";
|
||||||
archiveSizeU.Content = $"Uncompressed size {((long) sizeU).ToPrettySize(2)}";
|
archiveSizeU.Content = $"Uncompressed size {((long) sizeU).ToPrettySize(2)}";
|
||||||
|
});
|
||||||
|
|
||||||
|
LoadPercent = 100d;
|
||||||
|
}).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadItemsFromArchive(string path)
|
private void LoadItemsFromArchive(string path)
|
||||||
@@ -107,26 +143,30 @@ 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
|
||||||
{
|
{
|
||||||
var archive = ArchiveFactory.Open(stream, new ChardetReaderOptions());
|
var archive = ArchiveFactory.Open(stream, new ChardetReaderOptions());
|
||||||
|
|
||||||
_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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ProcessByLevel(IEntry entry)
|
private void ProcessByLevel(IEntry 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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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)
|
||||||
|
@@ -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" />
|
||||||
|
Reference in New Issue
Block a user