[Peek]Add Drive Previewer (#31476)

* Add drives previewer to Peek

* minor fixes

* fix spellcheck
This commit is contained in:
Davide Giacometti 2024-02-20 14:56:44 +01:00 committed by GitHub
parent 92c85630a9
commit 7c91dada64
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 430 additions and 3 deletions

View File

@ -367,6 +367,7 @@ DUMMYUNIONNAME
dutil
DVASPECT
DVASPECTINFO
DVD
DVH
DVHD
dvr

View File

@ -13,7 +13,7 @@ namespace Peek.Common.Helpers
private const int MaxDigitsToDisplay = 3;
private const int PowerFactor = 1024;
public static string BytesToReadableString(ulong bytes)
public static string BytesToReadableString(ulong bytes, bool showTotalBytes = true)
{
string totalBytesDisplays = (bytes == 1) ?
ResourceLoaderInstance.ResourceLoader.GetString("ReadableString_ByteString") :
@ -41,7 +41,7 @@ namespace Peek.Common.Helpers
string formatSpecifier = GetFormatSpecifierString(index, number, bytes, precision);
return bytes == 0
return bytes == 0 || !showTotalBytes
? string.Format(CultureInfo.CurrentCulture, formatSpecifier, number)
: string.Format(CultureInfo.CurrentCulture, formatSpecifier + totalBytesDisplays, number, bytes);
}

View File

@ -0,0 +1,114 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
<!-- Licensed under the MIT License. See LICENSE in the project root for license information. -->
<UserControl
x:Class="Peek.FilePreviewer.Controls.DriveControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Peek.FilePreviewer.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
SizeChanged="SizeChanged_Handler"
mc:Ignorable="d">
<Grid
MaxWidth="1000"
Margin="48"
VerticalAlignment="Center"
ColumnSpacing="16"
RowSpacing="16">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image
Grid.Row="0"
Grid.Column="0"
Width="180"
Height="180"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Source="{x:Bind Source.IconPreview, Mode=OneWay}" />
<StackPanel
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Spacing="5">
<TextBlock
FontSize="26"
FontWeight="SemiBold"
Text="{x:Bind Source.Name, Mode=OneWay}"
TextTrimming="CharacterEllipsis">
<ToolTipService.ToolTip>
<ToolTip Content="{x:Bind Source.Name, Mode=OneWay}" />
</ToolTipService.ToolTip>
</TextBlock>
<TextBlock Text="{x:Bind FormatType(Source.Type), Mode=OneWay}">
<ToolTipService.ToolTip>
<ToolTip Content="{x:Bind FormatType(Source.Type), Mode=OneWay}" />
</ToolTipService.ToolTip>
</TextBlock>
<TextBlock Text="{x:Bind FormatFileSystem(Source.FileSystem), Mode=OneWay}">
<ToolTipService.ToolTip>
<ToolTip Content="{x:Bind FormatFileSystem(Source.FileSystem), Mode=OneWay}" />
</ToolTipService.ToolTip>
</TextBlock>
<TextBlock Text="{x:Bind FormatCapacity(Source.Capacity), Mode=OneWay}">
<ToolTipService.ToolTip>
<ToolTip Content="{x:Bind FormatCapacity(Source.Capacity), Mode=OneWay}" />
</ToolTipService.ToolTip>
</TextBlock>
</StackPanel>
<Grid
Grid.Row="1"
Grid.ColumnSpan="2"
RowSpacing="5">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border
x:Name="CapacityBar"
Grid.Row="0"
Grid.ColumnSpan="2"
Height="20"
Background="{ThemeResource AccentFillColorDisabledBrush}"
CornerRadius="10" />
<Border
Grid.Row="0"
Grid.ColumnSpan="2"
Height="20"
Background="{ThemeResource AccentFillColorDefaultBrush}"
CornerRadius="10">
<Border.Clip>
<RectangleGeometry Rect="{x:Bind SpaceBarClip, Mode=OneWay}" />
</Border.Clip>
</Border>
<TextBlock
Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind FormatUsedSpace(Source.UsedSpace), Mode=OneWay}" />
<TextBlock
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind FormatFreeSpace(Source.FreeSpace), Mode=OneWay}" />
</Grid>
</Grid>
</UserControl>

View File

@ -0,0 +1,81 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Globalization;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Peek.Common.Helpers;
using Peek.FilePreviewer.Previewers.Drive.Models;
using Windows.Foundation;
namespace Peek.FilePreviewer.Controls
{
[INotifyPropertyChanged]
public sealed partial class DriveControl : UserControl
{
[ObservableProperty]
private Rect _spaceBarClip;
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
nameof(Source),
typeof(DrivePreviewData),
typeof(DriveControl),
new PropertyMetadata(null, new PropertyChangedCallback((d, e) => ((DriveControl)d).UpdateSpaceBar())));
public DrivePreviewData? Source
{
get { return (DrivePreviewData)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public DriveControl()
{
this.InitializeComponent();
}
public string FormatType(string type)
{
return string.Format(CultureInfo.CurrentCulture, ResourceLoaderInstance.ResourceLoader.GetString("Drive_Type"), type);
}
public string FormatFileSystem(string fileSystem)
{
return string.Format(CultureInfo.CurrentCulture, ResourceLoaderInstance.ResourceLoader.GetString("Drive_FileSystem"), fileSystem);
}
public string FormatCapacity(ulong capacity)
{
return string.Format(CultureInfo.CurrentCulture, ResourceLoaderInstance.ResourceLoader.GetString("Drive_Capacity"), ReadableStringHelper.BytesToReadableString(capacity, false));
}
public string FormatFreeSpace(ulong freeSpace)
{
return string.Format(CultureInfo.CurrentCulture, ResourceLoaderInstance.ResourceLoader.GetString("Drive_FreeSpace"), ReadableStringHelper.BytesToReadableString(freeSpace, false));
}
public string FormatUsedSpace(ulong usedSpace)
{
return string.Format(CultureInfo.CurrentCulture, ResourceLoaderInstance.ResourceLoader.GetString("Drive_UsedSpace"), ReadableStringHelper.BytesToReadableString(usedSpace, false));
}
private void SizeChanged_Handler(object sender, SizeChangedEventArgs e)
{
UpdateSpaceBar();
}
private void UpdateSpaceBar()
{
if (Source != null && Source.PercentageUsage > 0)
{
var usedWidth = CapacityBar.ActualWidth * Source!.PercentageUsage;
SpaceBarClip = new(0, 0, usedWidth, 20);
}
else
{
SpaceBarClip = new(0, 0, 0, 0);
}
}
}
}

View File

@ -70,6 +70,11 @@
Source="{x:Bind ArchivePreviewer.Tree, Mode=OneWay}"
Visibility="{x:Bind IsPreviewVisible(ArchivePreviewer, Previewer.State), Mode=OneWay}" />
<controls:DriveControl
x:Name="DrivePreview"
Source="{x:Bind DrivePreviewer.Preview, Mode=OneWay}"
Visibility="{x:Bind IsPreviewVisible(DrivePreviewer, Previewer.State), Mode=OneWay}" />
<controls:UnsupportedFilePreview
x:Name="UnsupportedFilePreview"
LoadingState="{x:Bind UnsupportedFilePreviewer.State, Mode=OneWay}"

View File

@ -51,8 +51,8 @@ namespace Peek.FilePreviewer
[NotifyPropertyChangedFor(nameof(BrowserPreviewer))]
[NotifyPropertyChangedFor(nameof(ArchivePreviewer))]
[NotifyPropertyChangedFor(nameof(ShellPreviewHandlerPreviewer))]
[NotifyPropertyChangedFor(nameof(DrivePreviewer))]
[NotifyPropertyChangedFor(nameof(UnsupportedFilePreviewer))]
private IPreviewer? previewer;
[ObservableProperty]
@ -100,6 +100,8 @@ namespace Peek.FilePreviewer
public IShellPreviewHandlerPreviewer? ShellPreviewHandlerPreviewer => Previewer as IShellPreviewHandlerPreviewer;
public IDrivePreviewer? DrivePreviewer => Previewer as IDrivePreviewer;
public IUnsupportedFilePreviewer? UnsupportedFilePreviewer => Previewer as IUnsupportedFilePreviewer;
public IFileSystemItem Item
@ -152,12 +154,14 @@ namespace Peek.FilePreviewer
VideoPreview.Visibility = Visibility.Collapsed;
BrowserPreview.Visibility = Visibility.Collapsed;
ArchivePreview.Visibility = Visibility.Collapsed;
DrivePreview.Visibility = Visibility.Collapsed;
UnsupportedFilePreview.Visibility = Visibility.Collapsed;
ImagePreview.FlowDirection = FlowDirection.LeftToRight;
VideoPreview.FlowDirection = FlowDirection.LeftToRight;
BrowserPreview.FlowDirection = FlowDirection.LeftToRight;
ArchivePreview.FlowDirection = FlowDirection.LeftToRight;
DrivePreview.FlowDirection = FlowDirection.LeftToRight;
UnsupportedFilePreview.FlowDirection = FlowDirection.LeftToRight;
return;
@ -224,6 +228,7 @@ namespace Peek.FilePreviewer
ImagePreview.Source = null;
ArchivePreview.Source = null;
BrowserPreview.Source = null;
DrivePreview.Source = null;
ShellPreviewHandlerPreviewer?.Clear();
ShellPreviewHandlerPreview.Source = null;

View File

@ -18,6 +18,7 @@
<ItemGroup>
<None Remove="Controls\ArchiveControl.xaml" />
<None Remove="Controls\BrowserControl.xaml" />
<None Remove="Controls\DriveControl.xaml" />
<None Remove="Controls\ShellPreviewHandlerControl.xaml" />
<None Remove="Controls\UnsupportedFilePreview\FailedFallbackPreviewControl.xaml" />
<None Remove="Controls\UnsupportedFilePreview\InformationalPreviewControl.xaml" />
@ -46,6 +47,12 @@
<ProjectReference Include="..\Peek.Common\Peek.Common.csproj" />
</ItemGroup>
<ItemGroup>
<Page Update="Controls\DriveControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\ShellPreviewHandlerControl.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@ -0,0 +1,109 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml.Media.Imaging;
using Peek.Common.Helpers;
using Peek.Common.Models;
using Peek.FilePreviewer.Models;
using Peek.FilePreviewer.Previewers.Drive.Models;
using Peek.FilePreviewer.Previewers.Helpers;
using Peek.FilePreviewer.Previewers.Interfaces;
using Windows.Foundation;
namespace Peek.FilePreviewer.Previewers.Drive
{
public partial class DrivePreviewer : ObservableObject, IDrivePreviewer
{
[ObservableProperty]
private PreviewState _state;
[ObservableProperty]
private DrivePreviewData? _preview;
private IFileSystemItem Item { get; }
public DrivePreviewer(IFileSystemItem file)
{
Item = file;
}
public async Task CopyAsync()
{
// Nothing to copy for a drive
await Task.CompletedTask;
}
public Task<PreviewSize> GetPreviewSizeAsync(CancellationToken cancellationToken)
{
var size = new Size(680, 400);
var previewSize = new PreviewSize { MonitorSize = size, UseEffectivePixels = true };
return Task.FromResult(previewSize);
}
public async Task LoadPreviewAsync(CancellationToken cancellationToken)
{
State = PreviewState.Loading;
var preview = new DrivePreviewData();
preview.Name = Item.Name;
var drive = new DriveInfo(Item.Path);
preview.Type = GetDriveTypeDescription(drive.DriveType);
if (drive.IsReady)
{
preview.FileSystem = drive.DriveFormat;
preview.Capacity = drive.TotalSize > 0
? (ulong)drive.TotalSize
: 0;
preview.FreeSpace = drive.AvailableFreeSpace > 0
? (ulong)drive.AvailableFreeSpace
: 0;
preview.UsedSpace = preview.Capacity - preview.FreeSpace;
if (preview.Capacity > 0)
{
preview.PercentageUsage = (double)preview.UsedSpace / preview.Capacity;
}
}
else
{
preview.FileSystem = ResourceLoaderInstance.ResourceLoader.GetString("Drive_Unknown");
}
cancellationToken.ThrowIfCancellationRequested();
var iconBitmap = await IconHelper.GetIconAsync(Item.Path, cancellationToken);
preview.IconPreview = iconBitmap ?? new SvgImageSource(new Uri("ms-appx:///Assets/Peek/DefaultFileIcon.svg"));
Preview = preview;
State = PreviewState.Loaded;
}
public static bool IsPathSupported(string path)
{
return DriveInfo.GetDrives().Any(d => d.Name == path);
}
private string GetDriveTypeDescription(DriveType driveType) => driveType switch
{
DriveType.Unknown => ResourceLoaderInstance.ResourceLoader.GetString("Drive_Unknown"),
DriveType.NoRootDirectory => ResourceLoaderInstance.ResourceLoader.GetString("Drive_Unknown"), // You shouldn't be able to preview an unmounted drives
DriveType.Removable => ResourceLoaderInstance.ResourceLoader.GetString("Drive_Type_Removable"),
DriveType.Fixed => ResourceLoaderInstance.ResourceLoader.GetString("Drive_Type_Fixed"),
DriveType.Network => ResourceLoaderInstance.ResourceLoader.GetString("Drive_Type_Network"),
DriveType.CDRom => ResourceLoaderInstance.ResourceLoader.GetString("Drive_Type_Optical"),
DriveType.Ram => ResourceLoaderInstance.ResourceLoader.GetString("Drive_Type_RAM_Disk"),
_ => ResourceLoaderInstance.ResourceLoader.GetString("Drive_Unknown"),
};
}
}

View File

@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml.Media;
namespace Peek.FilePreviewer.Previewers.Drive.Models
{
public partial class DrivePreviewData : ObservableObject
{
[ObservableProperty]
private ImageSource? iconPreview;
[ObservableProperty]
private string _name;
[ObservableProperty]
private string _type;
[ObservableProperty]
private string _fileSystem;
[ObservableProperty]
private ulong _capacity;
[ObservableProperty]
private ulong _usedSpace;
[ObservableProperty]
private ulong _freeSpace;
/// <summary>
/// Represents the usage percentage of the drive, ranging from 0 to 1
/// </summary>
[ObservableProperty]
private double _percentageUsage;
public DrivePreviewData()
{
Name = string.Empty;
Type = string.Empty;
FileSystem = string.Empty;
}
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Peek.FilePreviewer.Previewers.Drive.Models;
namespace Peek.FilePreviewer.Previewers.Interfaces
{
public interface IDrivePreviewer : IPreviewer
{
public DrivePreviewData? Preview { get; }
}
}

View File

@ -8,6 +8,7 @@ using Peek.Common.Extensions;
using Peek.Common.Models;
using Peek.FilePreviewer.Models;
using Peek.FilePreviewer.Previewers.Archives;
using Peek.FilePreviewer.Previewers.Drive;
using Peek.UI.Telemetry.Events;
namespace Peek.FilePreviewer.Previewers
@ -43,6 +44,10 @@ namespace Peek.FilePreviewer.Previewers
{
return new ShellPreviewHandlerPreviewer(file);
}
else if (DrivePreviewer.IsPathSupported(file.Path))
{
return new DrivePreviewer(file);
}
// Other previewer types check their supported file types here
return CreateDefaultPreviewer(file);

View File

@ -265,4 +265,45 @@
<value>{0} - Peek</value>
<comment>Title of the Peek window. {0} is the name of the currently previewed item."Peek" is the name of the utility.</comment>
</data>
<data name="Drive_FreeSpace" xml:space="preserve">
<value>{0} free</value>
<comment>{0} is the free space of the drive</comment>
</data>
<data name="Drive_UsedSpace" xml:space="preserve">
<value>{0} used</value>
<comment>{0} is the used space of the drive</comment>
</data>
<data name="Drive_Capacity" xml:space="preserve">
<value>Capacity: {0}</value>
<comment>{0} is the capacity of the drive</comment>
</data>
<data name="Drive_FileSystem" xml:space="preserve">
<value>File System: {0}</value>
<comment>{0} is the file system of the drive</comment>
</data>
<data name="Drive_Type" xml:space="preserve">
<value>Type: {0}</value>
<comment>{0} is the type of the drive</comment>
</data>
<data name="Drive_Type_Fixed" xml:space="preserve">
<value>Fixed</value>
</data>
<data name="Drive_Type_Network" xml:space="preserve">
<value>Network</value>
</data>
<data name="Drive_Type_Optical" xml:space="preserve">
<value>Optical</value>
<comment>"Optical" indicates CD or DVD drives</comment>
</data>
<data name="Drive_Type_RAM_Disk" xml:space="preserve">
<value>RAM Disk</value>
</data>
<data name="Drive_Type_Removable" xml:space="preserve">
<value>Removable</value>
<comment>"Removal" indicates USB or external hard drives</comment>
</data>
<data name="Drive_Unknown" xml:space="preserve">
<value>Unknown</value>
<comment>Used for unknown drive type or file system</comment>
</data>
</root>