[Peek] Enhance failed preview UX (#25949)

* Peek (#22498)

* Add peek dll project

* add spacebar preview and launch on hotkey press

* add todo

* add process handle to handle continuous press of hotkey

* add tool to stop all powertoys processes

* Add a blank Peek page and update nav menu

* Add some initial content to Peek page including a toggle

* refactor settings parsing

* rename spacebar peek to peek viewer

* rename script to stop powertoys processes

* remove tool

* Adding FileUtils for retrieving selected file in File Explorer

* Remove unnecessary SndPeekSettings

* Add shortcut setting

* Set the shortcut to ctrl+space

* Launching viewer with selected FE file

* Add PeekUI WinUI3 project with interop events

* Moving FileTypeUtils into PeekFileUtils project

* execute winui3 app on hotkey

* Fix paths with spaces

* remove winui3 project

* Resolve comment

* add wpf app with toggle visibility on hotkey

* fix visibility on startup

* remove window properties and add todos

* Fixed hidden extension and system file handling

* wip

* Add working WPF app with FileExplorer querying

* remove c++ projects

* Move native awaiter

* Working Image control with image files

* Resize and move window based on explorer monitor

* Image render, window positioning and sizing clean up

* add window management logic and selection logic

* add extension methods to add circular iterating capability to linkedlistnode

* Add OnArrowKeyPresshandler

* Added titlebar with file name and scaling with titlebar height

* fix flashing window on startup and process kept alive when powertoys exits

* remove wait for debugger loop in ui

* Add KeyIsDown method

* Fix KeyDown issue with Key handled and check for repeat

* Add thumbnail logic

* Add all folder items if only one item is selected

* File type helper

* Using hresult

* Add cancellation and rotation handling

* Use extension instead of path

* fIX CONFLICTS

* Fixing some file type checks

* Add new icon for Peek

* Update page with the new Peek icon

* Initialize IsEnabled and hook ActivationShortcut to dllmain

* add icon to taskbar and titlebar

* Add theme sensitive backgrounds

* rename event handlers

* add settings image

* Move window data into obserable object

* Refactor viewmodel, interop and helpers

* Clean up

* Add loading spinner

* Add todos

* Fix conflicts

* Move native code into its own folder

* Add peek to installer

* Fix building peek and peekui projects

* Replace UWP namespaces to WinAppSDK

* Working WASDK placeholder project

* Add exit when powertoys runner exit

* Working winui3 with image display

* Add WIC project with <TreatWarningAsErros> false for now

* Fit content to window

* Use Size from Windows.Foundation

* Change order

* Add some todos

* Refactored native/interop code and added helpers to imagepreviewer

* Rename projects

* Move some code

* Remove using

Co-authored-by: Michael Salmon <miksalmon@users.noreply.github.com>
Co-authored-by: Michael Salmon 🐟 <michaelpsalmon@outlook.com>
Co-authored-by: Alireza Ebadi Ghajari <alirezae@microsoft.com>
Co-authored-by: Jessie Su <Jessie.Su@microsoft.com>
Co-authored-by: sujessie <102062556+sujessie@users.noreply.github.com>

* Bump Microsoft.Windows.SDK.BuildTools version

* [Peek] Plugin pattern to enable any file type previewing (#22475)

* [Peek] Fetching image size through PropertyStore (#22530)

* Fetching metadata from PropertySTore

* Releasing objects to fix crash

* Creating new PropertyHelper

Co-authored-by: Daniel Chau <dancha@microsoft.com>

* Juliata/filetypes (#22538)

* Using the same list of file extensions as Lightbox's AppxManifest, and ensuring we convert file extension to lowercase

* Add IsFileTypeSupported to IPreviewer

* respond to PR comments

* Add scale awareness to window centering (#22541)

* [Peek] Fix installer builds, project configs and update assets (#22540)

* Update installer

* Fix installer errors

* Fix peek vcxproj

* Add package signing

* Add peek to arm64

* Add back ARM64 toMeasureToolUI

* Add versions to project

* Update assets and icons

* Add correct icon

* [Peek] Enable PropertyStore for offline files (#22567)

* Enabling PropertyStore for offline files

Co-authored-by: Daniel Chau <dancha@microsoft.com>

* [Peek] Adding unsupported file previewer (#22598)

* Unsupported file previewer

* Fix file display info

* Fix property store calls

* Update TODO

* [Peek] Add WebView2 integration (#22506)

* First commit with WIP logic to support WV2 in Peek module

* Minor code cleanup and try/catch block

* Added control to wrap WebView2 logic

* Cleanup

* Added logic to handle HTML previewing
Properly update FilePreview according to file type

* Code cleanup
Updated comments

* Updated comment

* Removed comment

* Code cleanup

* Improved opening of web browser preview to avoid "blank" or "seeing previous page" issue
Removed unused method
Added xaml fallback to guarantee default/starting state

* Removed folder

* Updated factory logic to match master

* address code review

* addressed PR review

* address PR review

* Address PR review

* address PR review

* Address PR review

* [Peek] Add basic file querying and navigation (#22589)

* Refactor to facilitate file data initialization

* Extract file-related code to new FileManager class

* Add temp basic version

* Clean + add todo for cancellations

* Fix various nav-related issues

* Temp - start moving iteration-related code to bg thread

* Minor tweaks

* Add FEHelper todo

* Rename FileManager + various tweaks

* Add basic throttling

* Improve bg thread synchronization

* Clean

* Clean

* Rename based on feedback

* Rename FileQuery

* Rename properties

* Rename remaining fields

* Add todos for nav success/failures

Co-authored-by: Esteban Margaron <emargaron@microsoft.com>

* [Peek] Add customized title bar  (#22600)

* Add basic button UI

* Add function to get default app name and to open file in default app

* Correct error output

* Add filename to titlebar

* Remove titlebar text from Resw

* Add basic button UI

* Add function to get default app name and to open file in default app

* Add filename to titlebar

* Correct error output

* Remove titlebar text from Resw

* Add SetDragRectangles

* Correct logic, update function name

* Add localization

* Cleanup and adaptive width

* Add fileIndex/NumberOfFiles for multiple files activation

* Refine titlebar styles

* Update error message; Return HResult from native methods; Update variable initialisation and string null testing

* Titlebar height and adaptive width refinement

* Add fallback to launch app picker if fail to open default app

* Temp change to hide AppTitle_FileCount

* Update launch button to command; Add keyboard accelerator

* Update titlebar inactive background color

* Update tooltip to add keyboard accelerator

* Add comments to resw file

* Fix accidental deletion from previous merge

Co-authored-by: Jojo Zhou <yizzho@microsoft.com>
Co-authored-by: Yawen Hou <yawenhou@microsoft.com>

* Fix crash

* Fix wrong thread exception

* Make CurrentItemIndex setter private

* Update titlebar filecount text

* Fix titlebar draggable region and interactive region (bump WinAppSdk to latest)

* [Peek] Unsupported File Previewer - Formatting string from resources (#22609)

* Moving to string resource usage

* Moving ReadableStringHelper to common project

* Fix comments

* [Peek] Fix foregrounding (#22633)

* Fixing foregrounding

* Get window handle inside BringToForeground extension method

Co-authored-by: Daniel Chau <dancha@microsoft.com>
Co-authored-by: Samuel Chapleau <sachaple@microsoft.com>

* [Peek] ImagePreviewer - Handle error states (#22637)

* add better preview state handling

* add error handling in imagepreviewer and better state handling

* fix error handling so exception is not bubbled up

* improve performance and hook up unsupported previewer on error

* remove commented code

* address pr comments

* [Peek] add PDF viewing support (#22636)

* [Peek] add PDF viewing support

* Fixed issue which would redirect some HTML and PDF files to external browser

* Fixed refactored interface name

* [Peek] Refine titlebar adaptive width (#22642)

* Adjust adaptive width of titlebar

* Remove visualstate setters for AppTitle_FileCount

Co-authored-by: Jojo Zhou <yizzho@microsoft.com>

* [Peek] New File Explorer tabs break Shell API to get selected files (#22641)

* fix FE tab bug

* remove unnecessary unsafe keyword

* [Peek] add extra logic to properly render PNG files with transparency (#22613)

* [Peek] added extra logic to render PNG files with proper transparency

* Moved logic to ThumbnailHelper
Cleanup

* Created a separated previewer for PNG to only load the preview image with thumbnail logic

* removed unused code

* Updated state loading change

* [Peek] Unsupported File Previewer - Setting Window Size (#22645)

* Adding setting for unsupported file window

* Fix

* [Peek] Add tooltip to File (#22640)

* Add tooltip to File

* Add placeholder text for no tooltip

* Address comments

* Use StringBuilder

Co-authored-by: Jojo Zhou <yizzho@microsoft.com>

* Add full image quality support (#22654)

* [Peek] Window foregrounding simplification and fixes + keep window visible if FE single selection changed (#22657)

* Use different apis to bring to foreground removing remote thread wait and work as well as library loading

* Keep window open if single selected file in FE is different

* Removed unused methods

* [Peek] Add cancellation token OnFilePropertyChanged (#22643)

* Cancel file loading before opening another file

* Add omitted cancellation checks

* Catch task cancelled exception; Add more cancellation checkpoints

* Add cancellation checkpoint beofre GetBitmapFromHBitmapAsync

* Correct typo

* Update to pass cancellation token individually to each async methods

* Add lost cancellationToken source

* Add cancellation token to PngPreviewer

Co-authored-by: Yawen Hou <yawenhou@microsoft.com>

* [Peek] Unsupported File Previewer - Preserve Transparency For File Icons (#22650)

* Preserving transparency or icons

* Remove TODO

Co-authored-by: Samuel Chapleau <sachaple@microsoft.com>

* [Peek] Update some installer build steps + assets update (#22683)

* Fix settings & peek.ui.wpf

* Add back missing icon

* Add missing files and actions to installer

* Keep window open if the selected file in explorer is different (only works for single file selection)

* Undo last

* [Peek] Add copy keyboard accelerator (#22647)

* add copy keyboard accelerator

* Fix comments

Co-authored-by: Samuel Chapleau <sachaple@microsoft.com>

* [Peek] add WV2 improvements (behavior and UX) (#22685)

* [Peek] added logic to get max monitor size for opening WebView2

* Removed ununsed dependency property

* Added workaround for cases where the web page would not finish navigating in a quick timing, for example google.com.

* Remove window extensions from common and use nullable size argument instead

Co-authored-by: Samuel Chapleau <sachaple@microsoft.com>

* [Peek] Merge main, self-contained .NET and fix WebView2 user data dir issue (#22899)

* Merge remote-tracking branch 'origin/main' into peek

* Test sc

* Set WebView2 user data dir

* spellcheck

* Fix comment

* Move check if higher quality image is already loaded to the exact line where we change the Preview bitmap (#23083)

* Fix opening Peek when FE window is set to full name path (#23082)

* Move check for png thubmnail loading priority

* Remove Peek.UI.WPF project

* Remove duplicated method in powertoys setup

* [Peek] Fix selecting files from the correct focused opened File Explorer tab & from Desktop (#23489)

* Get file based on active tab handle instead of window title

* Refactor code to get active tab

* Getting all items from the shell API working again, except for desktop

* Refactor and cleanup com & native code

* Add back removed peek xaml assets in Product.wxs

* Remove some dependencies that do not seem necessary in Product.wxs

* [Peek] Small images (#23554)

* change stretch value

* compare with actual window size

* consider scaling factor

* set max size

* clean up

* clean up

* clean up previewers

* scaling factor in bitmap previewer

* max image size property

* [Peek]Handle errors for HEIC/HEIF and fall back to default previewer if there is no thumbnail (#22684)

* Handle errors when getting filesize by falling back to default previewer

* Bringing back other file types that are fixed with these code changes

---------

Co-authored-by: Samuel Chapleau <sachaple@microsoft.com>

* [Peek] Add unsupported file icon fallback (#23735)

* Refactor icon retrieval, refactor hbitmap to bitmap conversion, add icon fallback

* Add svg to assets in installer

* [Peek] Refactoring of file system models, removal of PngPreviewer, retrieving of folder size via Scripting com reference and other fixes (#23955)

* Refactor icon retrieval, refactor hbitmap to bitmap conversion, add icon fallback

* Add svg to assets in installer

* - Refactor File class into IFileSystemItem, FileItem & FolderItem
- Display size for folders using Scripting namespace
- Remove default app buttons for files or folders not supporting it

* Add better content type via storage apis

* Add check for storagefile in PngPreviewer

* Fix png stretching

* Remove png previewer

* Rename ThumbnailOptions.None to ThumbnailOptions.ResizeToFit

* [Peek] Removed monitor percentage evaluation for the UnsupportedFilePreview control (#24002)

* Remove settings for percentage of windows and keep default min size.

* Fix margin on unsupported control

* Use nullable Size for image size & open file on background thread (#24004)

* [Peek] SVG support (#24237)

* svg previewer

* svg size

* set scaling factor

* set image size

* changed image source type

* non nullable image size

* notify svg previewer changed

* uncomment

* rename BitmapPreviewer

* move svg support

* remove svg previewer

* [Peek] Implementation of a performant and reliable Neighboring Files Query (#24943)

* Use IShellItemArray as the backing array of item

* Finalize and cleanup NFQ implementation

* Cleanup remainder of the code

* Remove unused using

* [Peek] Pin the window position  (#24927)

* [Peek] Telemetry and logging (#25231)

* text preview

* scrolling

* changed size

* Handle errors more gracefuly

* webview2 preview

* common preview project

* previewpane: use common project

* peek: use common

* previewpane: moved md

* peek: md

* previewpane: clean up

* clean up

* moved monaco files

* moved formatters

* rename

* moved common monaco helper

* dev files support

* installer

* removed versions

* warnings: culture info

* warnings: names

* clean up

* warnings: dispose

* warnings: default values

* warnings

* warnings: charset

* warnings: exceptions

* suppress warning

* installer: added peek

* changed peek guid

* monaco folders

* peek deps

* peek files

* peek resources

* removed additional monaco folder

* set host name

* Update installer

* hardcode monaco path

* leave single webview control

* moved path to common

* project

* more meaningful todos

* moved temp folder cleanup

* todo

* extension check

* spell: monaco

* spellcheck

* spellcheck

* fix id

* fix spelling

* key to spelling

* id fix

* Fix monaco resolution at install time

* Fix user install. Add needed files

* installer: remove peek localization files. It's a WinUI app

* installer:fix signing

* removed unused

* settings: flyout enable/disable for Peek

* simplify string

* property changed handle

* [Peek][Settings] Peek OOBE page (#25895)

* Add placeholder error icon

* Only open FilStream in read-only mode; Release propertyStore handle after getting the file properties

(cherry picked from commit 3b1481da2c)

* Update calls to PropertyStoreHelper

* Add disposable property store

* Make GetPropertyStoreFromPath return Disposable property store

* correct typo

* correct typo

* Remove nullable in DisposablePropertyStore

* Add property getters

* Remove usued method

* Correct typo

* Correct typo again...

* Update description

* Add cleaner fallback error

* Fix build

* Cleanup code

* Fix indents

---------

Co-authored-by: Michael Salmon <miksalmon@users.noreply.github.com>
Co-authored-by: Michael Salmon 🐟 <michaelpsalmon@outlook.com>
Co-authored-by: Alireza Ebadi Ghajari <alirezae@microsoft.com>
Co-authored-by: Jessie Su <Jessie.Su@microsoft.com>
Co-authored-by: sujessie <102062556+sujessie@users.noreply.github.com>
Co-authored-by: Daniel Chau <d.chau@alumni.ubc.ca>
Co-authored-by: Daniel Chau <dancha@microsoft.com>
Co-authored-by: jth-ms <73617023+jth-ms@users.noreply.github.com>
Co-authored-by: Robson <rp.pontin@gmail.com>
Co-authored-by: estebanm123 <49930791+estebanm123@users.noreply.github.com>
Co-authored-by: Esteban Margaron <emargaron@microsoft.com>
Co-authored-by: Yawen Hou <Sytta@users.noreply.github.com>
Co-authored-by: Jojo Zhou <yizzho@microsoft.com>
Co-authored-by: Yawen Hou <yawenhou@microsoft.com>
Co-authored-by: Jojo Zhou <39350350+Joanna-Zhou@users.noreply.github.com>
Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com>
Co-authored-by: Seraphima Zykova <zykovas91@gmail.com>
Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
This commit is contained in:
Samuel Chapleau 2023-05-11 14:46:39 -07:00 committed by GitHub
parent dab3326520
commit 50984fcd47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 406 additions and 151 deletions

View File

@ -0,0 +1,21 @@
// 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 Microsoft.UI.Xaml;
namespace Peek.Common.Converters
{
public static class VisibilityConverter
{
public static Visibility Convert(bool b)
{
return b ? Visibility.Visible : Visibility.Collapsed;
}
public static Visibility Invert(bool b)
{
return b ? Visibility.Collapsed : Visibility.Visible;
}
}
}

View File

@ -85,6 +85,8 @@ namespace Peek.Common.Extensions
{
ulong sizeInBytes = 0;
try
{
switch (item)
{
case FolderItem _:
@ -96,6 +98,11 @@ namespace Peek.Common.Extensions
sizeInBytes = item.FileSizeBytes;
break;
}
}
catch
{
sizeInBytes = 0;
}
return sizeInBytes;
}

View File

@ -17,11 +17,14 @@ namespace Peek.Common.Models
{
private StorageFile? storageFile;
public FileItem(string path)
public FileItem(string path, string name)
{
Path = path;
Name = name;
}
public string Name { get; init; }
public string Path { get; init; }
public async Task<IStorageItem?> GetStorageItemAsync()

View File

@ -17,11 +17,14 @@ namespace Peek.Common.Models
{
private StorageFolder? storageFolder;
public FolderItem(string path)
public FolderItem(string path, string name)
{
Path = path;
Name = name;
}
public string Name { get; init; }
public string Path { get; init; }
public async Task<IStorageItem?> GetStorageItemAsync()

View File

@ -15,11 +15,27 @@ namespace Peek.Common.Models
{
public interface IFileSystemItem
{
public DateTime DateModified => System.IO.File.GetCreationTime(Path);
public DateTime? DateModified
{
get
{
DateTime? dateModified = null;
try
{
dateModified = System.IO.File.GetCreationTime(Path);
}
catch
{
dateModified = null;
}
return dateModified;
}
}
public string Extension => System.IO.Path.GetExtension(Path).ToLower(CultureInfo.InvariantCulture);
public string Name => System.IO.Path.GetFileName(Path);
public string Name { get; init; }
public string Path { get; init; }

View File

@ -1,44 +0,0 @@
// 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.Controls;
using Microsoft.UI.Xaml.Media;
using Peek.Common.Helpers;
namespace Peek.FilePreviewer.Controls
{
[INotifyPropertyChanged]
public sealed partial class UnsupportedFilePreview : UserControl
{
[ObservableProperty]
private ImageSource? iconPreview;
[ObservableProperty]
private string? fileName;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FormattedFileType))]
private string? fileType;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FormattedFileSize))]
private string? fileSize;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FormattedDateModified))]
private string? dateModified;
public string FormattedFileType => ReadableStringHelper.FormatResourceString("UnsupportedFile_FileType", FileType);
public string FormattedFileSize => ReadableStringHelper.FormatResourceString("UnsupportedFile_FileSize", FileSize);
public string FormattedDateModified => ReadableStringHelper.FormatResourceString("UnsupportedFile_DateModified", DateModified);
public UnsupportedFilePreview()
{
this.InitializeComponent();
}
}
}

View File

@ -0,0 +1,47 @@
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
<!-- Licensed under the MIT License. -->
<UserControl
x:Class="Peek.FilePreviewer.Controls.FailedFallbackPreviewControl"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<IconSourceElement
Grid.Row="0"
Margin="3"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{StaticResource TextFillColorSecondaryBrush}">
<IconSourceElement.IconSource>
<FontIconSource FontSize="135" Glyph="&#xE783;" />
</IconSourceElement.IconSource>
</IconSourceElement>
<TextBlock
x:Uid="FailedFallbackTextBlock"
Grid.Row="1"
Margin="3"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{StaticResource TextFillColorSecondaryBrush}"
TextWrapping="Wrap" />
<HyperlinkButton
x:Uid="FailedFallbackReportBugHyperlinkButton"
Grid.Row="2"
Margin="3"
HorizontalAlignment="Center"
VerticalAlignment="Center"
NavigateUri="https://aka.ms/powerToysReportBug" />
</Grid>
</UserControl>

View File

@ -0,0 +1,16 @@
// 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 Microsoft.UI.Xaml.Controls;
namespace Peek.FilePreviewer.Controls
{
public sealed partial class FailedFallbackPreviewControl : UserControl
{
public FailedFallbackPreviewControl()
{
InitializeComponent();
}
}
}

View File

@ -2,29 +2,26 @@
<!-- Licensed under the MIT License. -->
<UserControl
x:Class="Peek.FilePreviewer.Controls.UnsupportedFilePreview"
x:Class="Peek.FilePreviewer.Controls.InformationalPreviewControl"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
Margin="48"
HorizontalAlignment="Stretch"
VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="Icon" Width="Auto" />
<ColumnDefinition x:Name="FileInfo" Width="*" />
</Grid.ColumnDefinitions>
<Image
x:Name="PreviewImage"
x:Name="FileIcon"
Grid.Column="0"
Width="180"
Height="180"
Margin="0,24,24,24"
Source="{x:Bind IconPreview, Mode=OneWay}" />
Source="{x:Bind Source.IconPreview, Mode=OneWay}" />
<StackPanel
Grid.Column="1"
@ -34,11 +31,11 @@
<TextBlock
FontSize="26"
FontWeight="SemiBold"
Text="{x:Bind FileName, Mode=OneWay}"
Text="{x:Bind Source.FileName, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
<TextBlock Text="{x:Bind FormattedFileType, Mode=OneWay}" />
<TextBlock Text="{x:Bind FormattedFileSize, Mode=OneWay}" />
<TextBlock Text="{x:Bind FormattedDateModified, Mode=OneWay}" />
<TextBlock Text="{x:Bind FormatFileType(Source.FileType), Mode=OneWay}" />
<TextBlock Text="{x:Bind FormatFileSize(Source.FileSize), Mode=OneWay}" />
<TextBlock Text="{x:Bind FormatFileDateModified(Source.DateModified), Mode=OneWay}" />
</StackPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,38 @@
// 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;
using Microsoft.UI.Xaml.Controls;
using Peek.Common.Helpers;
using Peek.FilePreviewer.Models;
namespace Peek.FilePreviewer.Controls
{
public sealed partial class InformationalPreviewControl : UserControl
{
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
nameof(Source),
typeof(UnsupportedFilePreviewData),
typeof(InformationalPreviewControl),
new PropertyMetadata(null));
public UnsupportedFilePreviewData? Source
{
get { return (UnsupportedFilePreviewData)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public InformationalPreviewControl()
{
InitializeComponent();
}
public string FormatFileType(string? fileType) => ReadableStringHelper.FormatResourceString("UnsupportedFile_FileType", fileType);
public string FormatFileSize(string? fileSize) => ReadableStringHelper.FormatResourceString("UnsupportedFile_FileSize", fileSize);
public string FormatFileDateModified(string? fileDateModified) => ReadableStringHelper.FormatResourceString("UnsupportedFile_DateModified", fileDateModified);
}
}

View File

@ -0,0 +1,27 @@
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
<!-- Licensed under the MIT License. -->
<UserControl
x:Class="Peek.FilePreviewer.Controls.UnsupportedFilePreview"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Peek.FilePreviewer.Controls"
xmlns:conv="using:Peek.Common.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prev="using:Peek.FilePreviewer.Previewers"
mc:Ignorable="d">
<Grid
Margin="48"
HorizontalAlignment="Stretch"
VerticalAlignment="Center">
<controls:InformationalPreviewControl
x:Name="InformationPreview"
Source="{x:Bind Source, Mode=OneWay}"
Visibility="{x:Bind IsVisibleIfStatesMatch(LoadingState, prev:PreviewState.Loaded), Mode=OneWay}" />
<controls:FailedFallbackPreviewControl x:Name="FailedFallbackPreview" Visibility="{x:Bind IsVisibleIfStatesMatch(LoadingState, prev:PreviewState.Error), Mode=OneWay}" />
</Grid>
</UserControl>

View File

@ -0,0 +1,48 @@
// 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;
using Microsoft.UI.Xaml.Controls;
using Peek.Common.Converters;
using Peek.FilePreviewer.Models;
using Peek.FilePreviewer.Previewers;
namespace Peek.FilePreviewer.Controls
{
[INotifyPropertyChanged]
public sealed partial class UnsupportedFilePreview : UserControl
{
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
nameof(Source),
typeof(UnsupportedFilePreviewData),
typeof(UnsupportedFilePreview),
new PropertyMetadata(null));
public static readonly DependencyProperty LoadingStateProperty = DependencyProperty.Register(
nameof(LoadingState),
typeof(PreviewState),
typeof(UnsupportedFilePreview),
new PropertyMetadata(PreviewState.Uninitialized));
public UnsupportedFilePreviewData? Source
{
get { return (UnsupportedFilePreviewData)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public PreviewState? LoadingState
{
get { return (PreviewState)GetValue(LoadingStateProperty); }
set { SetValue(LoadingStateProperty, value); }
}
public UnsupportedFilePreview()
{
InitializeComponent();
}
public Visibility IsVisibleIfStatesMatch(PreviewState? a, PreviewState? b) => VisibilityConverter.Convert(a == b);
}
}

View File

@ -30,19 +30,16 @@
x:Name="BrowserPreview"
x:Load="True"
DOMContentLoaded="BrowserPreview_DOMContentLoaded"
IsDevFilePreview="{x:Bind BrowserPreviewer.IsDevFilePreview, Mode=OneWay}"
NavigationCompleted="PreviewBrowser_NavigationCompleted"
Source="{x:Bind BrowserPreviewer.Preview, Mode=OneWay}"
IsDevFilePreview="{x:Bind BrowserPreviewer.IsDevFilePreview, Mode=OneWay}"
Visibility="{x:Bind IsPreviewVisible(BrowserPreviewer, Previewer.State), Mode=OneWay, FallbackValue=Collapsed}" />
<controls:UnsupportedFilePreview
x:Name="UnsupportedFilePreview"
DateModified="{x:Bind UnsupportedFilePreviewer.DateModified, Mode=OneWay}"
FileName="{x:Bind UnsupportedFilePreviewer.FileName, Mode=OneWay}"
FileSize="{x:Bind UnsupportedFilePreviewer.FileSize, Mode=OneWay}"
FileType="{x:Bind UnsupportedFilePreviewer.FileType, Mode=OneWay}"
IconPreview="{x:Bind UnsupportedFilePreviewer.IconPreview, Mode=OneWay}"
Visibility="{x:Bind IsPreviewVisible(UnsupportedFilePreviewer, Previewer.State), Mode=OneWay}" />
LoadingState="{x:Bind UnsupportedFilePreviewer.State, Mode=OneWay}"
Source="{x:Bind UnsupportedFilePreviewer.Preview, Mode=OneWay}"
Visibility="{x:Bind IsUnsupportedPreviewVisible(UnsupportedFilePreviewer, Previewer.State), Mode=OneWay}" />
</Grid>
<UserControl.KeyboardAccelerators>
<KeyboardAccelerator

View File

@ -78,11 +78,14 @@ namespace Peek.FilePreviewer
_cancellationTokenSource.Cancel();
_cancellationTokenSource = new();
if (Previewer is not IUnsupportedFilePreviewer)
{
Previewer = previewerFactory.CreateDefaultPreviewer(Item);
await UpdatePreviewAsync(_cancellationTokenSource.Token);
}
}
}
}
public IImagePreviewer? ImagePreviewer => Previewer as IImagePreviewer;
@ -121,6 +124,12 @@ namespace Peek.FilePreviewer
return isValidPreview ? Visibility.Visible : Visibility.Collapsed;
}
public Visibility IsUnsupportedPreviewVisible(IUnsupportedFilePreviewer? previewer, PreviewState state)
{
var isValidPreview = previewer != null && (MatchPreviewState(state, PreviewState.Loaded) || MatchPreviewState(state, PreviewState.Error));
return isValidPreview ? Visibility.Visible : Visibility.Collapsed;
}
private async Task OnItemPropertyChanged()
{
// Cancel previous loading task
@ -259,7 +268,7 @@ namespace Peek.FilePreviewer
string fileTypeFormatted = string.IsNullOrEmpty(fileType) ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_FileType", fileType);
sb.Append(fileTypeFormatted);
string dateModified = Item.DateModified.ToString(CultureInfo.CurrentCulture);
string dateModified = Item.DateModified?.ToString(CultureInfo.CurrentCulture) ?? string.Empty;
string dateModifiedFormatted = string.IsNullOrEmpty(dateModified) ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_DateModified", dateModified);
sb.Append(dateModifiedFormatted);

View File

@ -0,0 +1,27 @@
// 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.Models
{
public partial class UnsupportedFilePreviewData : ObservableObject
{
[ObservableProperty]
private ImageSource? iconPreview;
[ObservableProperty]
private string? fileName;
[ObservableProperty]
private string? fileType;
[ObservableProperty]
private string? fileSize;
[ObservableProperty]
private string? dateModified;
}
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\Version.props" />
<PropertyGroup>
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
@ -8,8 +8,15 @@
<UseWinUI>true</UseWinUI>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PRIResource Include="..\Peek.UI\Strings\en-US\Resources.resw" Link="Strings\en-US\Resources.resw">
<SubType>Designer</SubType>
</PRIResource>
</ItemGroup>
<ItemGroup>
<None Remove="Controls\BrowserControl.xaml" />
<None Remove="Controls\UnsupportedFilePreview\FailedFallbackPreviewControl.xaml" />
<None Remove="Controls\UnsupportedFilePreview\InformationalPreviewControl.xaml" />
<None Remove="FilePreview.xaml" />
<None Remove="UnsupportedFilePreview.xaml" />
</ItemGroup>
@ -29,6 +36,12 @@
<ProjectReference Include="..\WIC\WIC.csproj" />
</ItemGroup>
<ItemGroup>
<Page Update="Controls\UnsupportedFilePreview\FailedFallbackPreviewControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="FilePreview.xaml">
<Generator>MSBuild:Compile</Generator>
@ -48,6 +61,8 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Previewers\DrivePreviewer\" />
<Page Update="Controls\UnsupportedFilePreview\InformationalPreviewControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
</Project>

View File

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using Peek.Common;
using Peek.Common.Extensions;
using Peek.Common.Models;
namespace Peek.FilePreviewer.Previewers.Helpers
@ -20,8 +21,14 @@ namespace Peek.FilePreviewer.Previewers.Helpers
public static async Task<ImageSource?> GetIconAsync(string fileName, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(fileName))
{
return null;
}
ImageSource? imageSource = null;
IShellItem? nativeShellItem = null;
try
{
Guid shellItem2Guid = new(IShellItem2Guid);
@ -39,15 +46,7 @@ namespace Peek.FilePreviewer.Previewers.Helpers
cancellationToken.ThrowIfCancellationRequested();
if (hr == HResult.Ok)
{
imageSource = await BitmapHelper.GetBitmapFromHBitmapAsync(hbitmap, true, cancellationToken);
}
else
{
var svgImageSource = new SvgImageSource(new Uri("ms-appx:///Assets/DefaultFileIcon.svg"));
imageSource = svgImageSource;
}
imageSource = hr == HResult.Ok ? await BitmapHelper.GetBitmapFromHBitmapAsync(hbitmap, true, cancellationToken) : null;
}
finally
{

View File

@ -156,7 +156,7 @@ namespace Peek.FilePreviewer.Previewers
{
cancellationToken.ThrowIfCancellationRequested();
var hr = ThumbnailHelper.GetThumbnail(Path.GetFullPath(Item.Path), out IntPtr hbitmap, ThumbnailHelper.LowQualityThumbnailSize);
var hr = ThumbnailHelper.GetThumbnail(Item.Path, out IntPtr hbitmap, ThumbnailHelper.LowQualityThumbnailSize);
if (hr != HResult.Ok)
{
Logger.LogError("Error loading low quality thumbnail - hresult: " + hr);
@ -183,7 +183,7 @@ namespace Peek.FilePreviewer.Previewers
{
cancellationToken.ThrowIfCancellationRequested();
var hr = ThumbnailHelper.GetThumbnail(Path.GetFullPath(Item.Path), out IntPtr hbitmap, ThumbnailHelper.HighQualityThumbnailSize);
var hr = ThumbnailHelper.GetThumbnail(Item.Path, out IntPtr hbitmap, ThumbnailHelper.HighQualityThumbnailSize);
if (hr != HResult.Ok)
{
Logger.LogError("Error loading high quality thumbnail - hresult: " + hr);

View File

@ -3,19 +3,12 @@
// See the LICENSE file in the project root for more information.
using Microsoft.UI.Xaml.Media;
using Peek.FilePreviewer.Models;
namespace Peek.FilePreviewer.Previewers
{
public interface IUnsupportedFilePreviewer : IPreviewer
{
public ImageSource? IconPreview { get; }
public string? FileName { get; }
public string? FileType { get; }
public string? FileSize { get; }
public string? DateModified { get; }
public UnsupportedFilePreviewData? Preview { get; }
}
}

View File

@ -10,10 +10,11 @@ using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using Peek.Common.Extensions;
using Peek.Common.Helpers;
using Peek.Common.Models;
using Peek.FilePreviewer.Models;
using Peek.FilePreviewer.Previewers.Helpers;
using Windows.Foundation;
@ -22,19 +23,7 @@ namespace Peek.FilePreviewer.Previewers
public partial class UnsupportedFilePreviewer : ObservableObject, IUnsupportedFilePreviewer, IDisposable
{
[ObservableProperty]
private ImageSource? iconPreview;
[ObservableProperty]
private string? fileName;
[ObservableProperty]
private string? fileType;
[ObservableProperty]
private string? fileSize;
[ObservableProperty]
private string? dateModified;
private UnsupportedFilePreviewData preview = new UnsupportedFilePreviewData();
[ObservableProperty]
private PreviewState state;
@ -42,12 +31,12 @@ namespace Peek.FilePreviewer.Previewers
public UnsupportedFilePreviewer(IFileSystemItem file)
{
Item = file;
FileName = file.Name;
DateModified = file.DateModified.ToString(CultureInfo.CurrentCulture);
Preview.FileName = file.Name;
Preview.DateModified = file.DateModified?.ToString(CultureInfo.CurrentCulture);
Dispatcher = DispatcherQueue.GetForCurrentThread();
}
public bool IsPreviewLoaded => iconPreview != null;
public bool IsPreviewLoaded => Preview.IconPreview != null;
private IFileSystemItem Item { get; }
@ -83,6 +72,10 @@ namespace Peek.FilePreviewer.Previewers
{
State = PreviewState.Error;
}
else
{
State = PreviewState.Loaded;
}
}
public async Task CopyAsync()
@ -94,23 +87,35 @@ namespace Peek.FilePreviewer.Previewers
});
}
public Task<bool> LoadIconPreviewAsync(CancellationToken cancellationToken)
public async Task<bool> LoadIconPreviewAsync(CancellationToken cancellationToken)
{
return TaskExtension.RunSafe(async () =>
bool isIconValid = false;
var isTaskSuccessful = await TaskExtension.RunSafe(async () =>
{
cancellationToken.ThrowIfCancellationRequested();
await Dispatcher.RunOnUiThread(async () =>
{
cancellationToken.ThrowIfCancellationRequested();
var iconBitmap = await IconHelper.GetIconAsync(Path.GetFullPath(Item.Path), cancellationToken);
IconPreview = iconBitmap;
var iconBitmap = await IconHelper.GetIconAsync(Item.Path, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
isIconValid = iconBitmap != null;
Preview.IconPreview = iconBitmap ?? new SvgImageSource(new Uri("ms-appx:///Assets/DefaultFileIcon.svg"));
});
});
return isIconValid && isTaskSuccessful;
}
public Task<bool> LoadDisplayInfoAsync(CancellationToken cancellationToken)
public async Task<bool> LoadDisplayInfoAsync(CancellationToken cancellationToken)
{
return TaskExtension.RunSafe(async () =>
bool isDisplayValid = false;
var isTaskSuccessful = await TaskExtension.RunSafe(async () =>
{
// File Properties
cancellationToken.ThrowIfCancellationRequested();
@ -118,31 +123,32 @@ namespace Peek.FilePreviewer.Previewers
var bytes = await Task.Run(Item.GetSizeInBytes);
cancellationToken.ThrowIfCancellationRequested();
var type = await Task.Run(Item.GetContentTypeAsync);
cancellationToken.ThrowIfCancellationRequested();
var readableFileSize = ReadableStringHelper.BytesToReadableString(bytes);
isDisplayValid = type != null;
await Dispatcher.RunOnUiThread(() =>
{
FileSize = ReadableStringHelper.BytesToReadableString(bytes);
FileType = type;
Preview.FileSize = readableFileSize;
Preview.FileType = type;
return Task.CompletedTask;
});
});
}
partial void OnIconPreviewChanged(ImageSource? value)
{
if (IconPreview != null)
{
State = PreviewState.Loaded;
}
return isDisplayValid && isTaskSuccessful;
}
private bool HasFailedLoadingPreview()
{
var hasFailedLoadingIconPreview = !(IconPreviewTask?.Result ?? true);
var hasFailedLoadingDisplayInfo = !(DisplayInfoTask?.Result ?? true);
var isLoadingIconPreviewSuccessful = IconPreviewTask?.Result ?? false;
var isLoadingDisplayInfoSuccessful = DisplayInfoTask?.Result ?? false;
return hasFailedLoadingIconPreview && hasFailedLoadingDisplayInfo;
return !isLoadingIconPreviewSuccessful || !isLoadingDisplayInfoSuccessful;
}
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
using Peek.Common.Helpers;
using Peek.Common.Models;
@ -12,6 +13,14 @@ namespace Peek.UI.Extensions
public static class IShellItemExtensions
{
public static IFileSystemItem ToIFileSystemItem(this IShellItem shellItem)
{
string path = shellItem.GetPath();
string name = shellItem.GetName();
return File.Exists(path) ? new FileItem(path, name) : new FolderItem(path, name);
}
private static string GetPath(this IShellItem shellItem)
{
string path = string.Empty;
try
@ -21,10 +30,27 @@ namespace Peek.UI.Extensions
catch (Exception ex)
{
// TODO: Handle cases that do not have a file system path like Recycle Bin.
path = string.Empty;
Logger.LogError("Getting path failed. " + ex.Message);
}
return File.Exists(path) ? new FileItem(path) : new FolderItem(path);
return path;
}
private static string GetName(this IShellItem shellItem)
{
string name = string.Empty;
try
{
name = shellItem.GetDisplayName(Windows.Win32.UI.Shell.SIGDN.SIGDN_NORMALDISPLAY);
}
catch (Exception ex)
{
name = string.Empty;
Logger.LogError("Getting path failed. " + ex.Message);
}
return name;
}
}
}

View File

@ -213,4 +213,12 @@
<value>Unpin the window</value>
<comment>Tooltip for button to unpin the Peek window.</comment>
</data>
<data name="FailedFallbackReportBugHyperlinkButton.Content" xml:space="preserve">
<value>Report a bug</value>
<comment>Failed fallback preview Report bug hyperlink button content.</comment>
</data>
<data name="FailedFallbackTextBlock.Text" xml:space="preserve">
<value>An error occurred while previewing this file.</value>
<comment>Failed fallback preview text.</comment>
</data>
</root>

View File

@ -22,9 +22,7 @@
</ic:EventTriggerBehavior>
</i:Interaction.Behaviors>
<Grid
x:Name="RootGrid"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid x:Name="RootGrid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<NavigationView
x:Name="navigationView"
@ -36,15 +34,11 @@
IsTitleBarAutoPaddingEnabled="False"
PaneClosed="NavigationView_PaneClosed"
PaneOpened="NavigationView_PaneOpened"
SelectionChanged="NavigationView_SelectionChanged"
SelectedItem="{x:Bind ViewModel.Selected, Mode=OneWay}">
SelectedItem="{x:Bind ViewModel.Selected, Mode=OneWay}"
SelectionChanged="NavigationView_SelectionChanged">
<NavigationView.Resources>
<SolidColorBrush
x:Key="NavigationViewContentBackground"
Color="Transparent" />
<SolidColorBrush
x:Key="NavigationViewContentGridBorderBrush"
Color="Transparent" />
<SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent" />
<SolidColorBrush x:Key="NavigationViewContentGridBorderBrush" Color="Transparent" />
</NavigationView.Resources>
<NavigationView.MenuItems>
<NavigationViewItem
@ -108,7 +102,7 @@
<NavigationViewItem
x:Uid="Shell_Peek"
helpers:NavHelper.NavigateTo="views:PeekPage"
Icon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsPeek.png}"/>
Icon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsPeek.png}" />
<NavigationViewItem
x:Uid="Shell_PowerRename"
@ -154,11 +148,13 @@
<StackPanel Orientation="Vertical">
<NavigationViewItem
x:Uid="OOBE_NavViewItem"
Icon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily}, Glyph=&#xF133;}"
Icon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily},
Glyph=&#xF133;}"
Tapped="OOBEItem_Tapped" />
<NavigationViewItem
x:Uid="Feedback_NavViewItem"
Icon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily}, Glyph=&#xED15;}"
Icon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily},
Glyph=&#xED15;}"
Tapped="FeedbackItem_Tapped" />
</StackPanel>
</NavigationView.PaneFooter>