From 50984fcd475dc502e8384b122d039b27702700f7 Mon Sep 17 00:00:00 2001 From: Samuel Chapleau Date: Thu, 11 May 2023 14:46:39 -0700 Subject: [PATCH] [Peek] Enhance failed preview UX (#25949) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 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 Co-authored-by: Michael Salmon 🐟 Co-authored-by: Alireza Ebadi Ghajari Co-authored-by: Jessie Su 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 * 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 * [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 * [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 Co-authored-by: Yawen Hou * 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 Co-authored-by: Samuel Chapleau * [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 * [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 * 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 * [Peek] Unsupported File Previewer - Preserve Transparency For File Icons (#22650) * Preserving transparency or icons * Remove TODO Co-authored-by: Samuel Chapleau * [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 * [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 * [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 * [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 3b1481da2c2e8ffa5ae1426d9cd22f7fb7a1240e) * 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 Co-authored-by: Michael Salmon 🐟 Co-authored-by: Alireza Ebadi Ghajari Co-authored-by: Jessie Su Co-authored-by: sujessie <102062556+sujessie@users.noreply.github.com> Co-authored-by: Daniel Chau Co-authored-by: Daniel Chau Co-authored-by: jth-ms <73617023+jth-ms@users.noreply.github.com> Co-authored-by: Robson Co-authored-by: estebanm123 <49930791+estebanm123@users.noreply.github.com> Co-authored-by: Esteban Margaron Co-authored-by: Yawen Hou Co-authored-by: Jojo Zhou Co-authored-by: Yawen Hou 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 Co-authored-by: Stefan Markovic Co-authored-by: Jaime Bernardo --- .../Converters/VisibilityConverter.cs | 21 +++++ .../Extensions/IFileSystemItemExtensions.cs | 25 +++--- .../peek/Peek.Common/Models/FileItem.cs | 5 +- .../peek/Peek.Common/Models/FolderItem.cs | 5 +- .../Peek.Common/Models/IFileSystemItem.cs | 20 ++++- .../Controls/UnsupportedFilePreview.xaml.cs | 44 ----------- .../FailedFallbackPreviewControl.xaml | 47 ++++++++++++ .../FailedFallbackPreviewControl.xaml.cs | 16 ++++ .../InformationalPreviewControl.xaml} | 19 ++--- .../InformationalPreviewControl.xaml.cs | 38 ++++++++++ .../UnsupportedFilePreview.xaml | 27 +++++++ .../UnsupportedFilePreview.xaml.cs | 48 ++++++++++++ .../peek/Peek.FilePreviewer/FilePreview.xaml | 11 +-- .../Peek.FilePreviewer/FilePreview.xaml.cs | 15 +++- .../Models/UnsupportedFilePreviewData.cs | 27 +++++++ .../Peek.FilePreviewer.csproj | 19 ++++- .../Previewers/Helpers/IconHelper.cs | 17 ++--- .../ImagePreviewer/ImagePreviewer.cs | 4 +- .../Interfaces/IUnsupportedFilePreviewer.cs | 11 +-- .../UnsupportedFilePreviewer.cs | 76 ++++++++++--------- .../Extensions/IShellItemExtensions.cs | 28 ++++++- .../peek/Peek.UI/Strings/en-us/Resources.resw | 8 ++ .../Settings.UI/Views/ShellPage.xaml | 26 +++---- 23 files changed, 406 insertions(+), 151 deletions(-) create mode 100644 src/modules/peek/Peek.Common/Converters/VisibilityConverter.cs delete mode 100644 src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview.xaml.cs create mode 100644 src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/FailedFallbackPreviewControl.xaml create mode 100644 src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/FailedFallbackPreviewControl.xaml.cs rename src/modules/peek/Peek.FilePreviewer/Controls/{UnsupportedFilePreview.xaml => UnsupportedFilePreview/InformationalPreviewControl.xaml} (65%) create mode 100644 src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/InformationalPreviewControl.xaml.cs create mode 100644 src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/UnsupportedFilePreview.xaml create mode 100644 src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/UnsupportedFilePreview.xaml.cs create mode 100644 src/modules/peek/Peek.FilePreviewer/Models/UnsupportedFilePreviewData.cs diff --git a/src/modules/peek/Peek.Common/Converters/VisibilityConverter.cs b/src/modules/peek/Peek.Common/Converters/VisibilityConverter.cs new file mode 100644 index 0000000000..9a2306864e --- /dev/null +++ b/src/modules/peek/Peek.Common/Converters/VisibilityConverter.cs @@ -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; + } + } +} diff --git a/src/modules/peek/Peek.Common/Extensions/IFileSystemItemExtensions.cs b/src/modules/peek/Peek.Common/Extensions/IFileSystemItemExtensions.cs index 8ea7f0c826..50cc7f9c93 100644 --- a/src/modules/peek/Peek.Common/Extensions/IFileSystemItemExtensions.cs +++ b/src/modules/peek/Peek.Common/Extensions/IFileSystemItemExtensions.cs @@ -85,16 +85,23 @@ namespace Peek.Common.Extensions { ulong sizeInBytes = 0; - switch (item) + try { - case FolderItem _: - FileSystemObject fileSystemObject = new FileSystemObject(); - Folder folder = fileSystemObject.GetFolder(item.Path); - sizeInBytes = (ulong)folder.Size; - break; - case FileItem _: - sizeInBytes = item.FileSizeBytes; - break; + switch (item) + { + case FolderItem _: + FileSystemObject fileSystemObject = new FileSystemObject(); + Folder folder = fileSystemObject.GetFolder(item.Path); + sizeInBytes = (ulong)folder.Size; + break; + case FileItem _: + sizeInBytes = item.FileSizeBytes; + break; + } + } + catch + { + sizeInBytes = 0; } return sizeInBytes; diff --git a/src/modules/peek/Peek.Common/Models/FileItem.cs b/src/modules/peek/Peek.Common/Models/FileItem.cs index 25d06a9d09..da146f3e47 100644 --- a/src/modules/peek/Peek.Common/Models/FileItem.cs +++ b/src/modules/peek/Peek.Common/Models/FileItem.cs @@ -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 GetStorageItemAsync() diff --git a/src/modules/peek/Peek.Common/Models/FolderItem.cs b/src/modules/peek/Peek.Common/Models/FolderItem.cs index 11244a03af..77780fb575 100644 --- a/src/modules/peek/Peek.Common/Models/FolderItem.cs +++ b/src/modules/peek/Peek.Common/Models/FolderItem.cs @@ -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 GetStorageItemAsync() diff --git a/src/modules/peek/Peek.Common/Models/IFileSystemItem.cs b/src/modules/peek/Peek.Common/Models/IFileSystemItem.cs index 544f933f36..e455ca243d 100644 --- a/src/modules/peek/Peek.Common/Models/IFileSystemItem.cs +++ b/src/modules/peek/Peek.Common/Models/IFileSystemItem.cs @@ -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; } diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview.xaml.cs b/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview.xaml.cs deleted file mode 100644 index e86a8250c7..0000000000 --- a/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview.xaml.cs +++ /dev/null @@ -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(); - } - } -} diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/FailedFallbackPreviewControl.xaml b/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/FailedFallbackPreviewControl.xaml new file mode 100644 index 0000000000..f49062646b --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/FailedFallbackPreviewControl.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/FailedFallbackPreviewControl.xaml.cs b/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/FailedFallbackPreviewControl.xaml.cs new file mode 100644 index 0000000000..b307263ef7 --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/FailedFallbackPreviewControl.xaml.cs @@ -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(); + } + } +} diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview.xaml b/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/InformationalPreviewControl.xaml similarity index 65% rename from src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview.xaml rename to src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/InformationalPreviewControl.xaml index 4d7c5c588c..20a572a249 100644 --- a/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview.xaml +++ b/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/InformationalPreviewControl.xaml @@ -2,29 +2,26 @@ - + + Source="{x:Bind Source.IconPreview, Mode=OneWay}" /> - - - + + + diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/InformationalPreviewControl.xaml.cs b/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/InformationalPreviewControl.xaml.cs new file mode 100644 index 0000000000..478218202f --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/InformationalPreviewControl.xaml.cs @@ -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); + } +} diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/UnsupportedFilePreview.xaml b/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/UnsupportedFilePreview.xaml new file mode 100644 index 0000000000..b77fa3c511 --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/UnsupportedFilePreview.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/UnsupportedFilePreview.xaml.cs b/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/UnsupportedFilePreview.xaml.cs new file mode 100644 index 0000000000..accd18e845 --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Controls/UnsupportedFilePreview/UnsupportedFilePreview.xaml.cs @@ -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); + } +} diff --git a/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml b/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml index 9490cecf7b..c64410551d 100644 --- a/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml +++ b/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml @@ -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}" /> + LoadingState="{x:Bind UnsupportedFilePreviewer.State, Mode=OneWay}" + Source="{x:Bind UnsupportedFilePreviewer.Preview, Mode=OneWay}" + Visibility="{x:Bind IsUnsupportedPreviewVisible(UnsupportedFilePreviewer, Previewer.State), Mode=OneWay}" /> + net7.0-windows10.0.19041.0 @@ -8,8 +8,15 @@ true enable + + + Designer + + + + @@ -29,6 +36,12 @@ + + + MSBuild:Compile + + + MSBuild:Compile @@ -48,6 +61,8 @@ - + + MSBuild:Compile + diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/Helpers/IconHelper.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/Helpers/IconHelper.cs index 099d1f7d47..34ddde5dfa 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/Helpers/IconHelper.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/Helpers/IconHelper.cs @@ -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 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 { diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/ImagePreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/ImagePreviewer.cs index fef92f6115..16ebee4c0b 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/ImagePreviewer.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/ImagePreviewer.cs @@ -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); diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/IUnsupportedFilePreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/IUnsupportedFilePreviewer.cs index 8c90d54ca3..668a4aaa5d 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/IUnsupportedFilePreviewer.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/IUnsupportedFilePreviewer.cs @@ -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; } } } diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/UnsupportedFilePreviewer/UnsupportedFilePreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/UnsupportedFilePreviewer/UnsupportedFilePreviewer.cs index 5a3c84a84f..8fa67383a5 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/UnsupportedFilePreviewer/UnsupportedFilePreviewer.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/UnsupportedFilePreviewer/UnsupportedFilePreviewer.cs @@ -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 LoadIconPreviewAsync(CancellationToken cancellationToken) + public async Task 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 LoadDisplayInfoAsync(CancellationToken cancellationToken) + public async Task 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; } } } diff --git a/src/modules/peek/Peek.UI/Extensions/IShellItemExtensions.cs b/src/modules/peek/Peek.UI/Extensions/IShellItemExtensions.cs index 32fdbd9aee..753b7a61ce 100644 --- a/src/modules/peek/Peek.UI/Extensions/IShellItemExtensions.cs +++ b/src/modules/peek/Peek.UI/Extensions/IShellItemExtensions.cs @@ -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; } } } diff --git a/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw b/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw index 49e67578f9..b940d7968f 100644 --- a/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw +++ b/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw @@ -213,4 +213,12 @@ Unpin the window Tooltip for button to unpin the Peek window. + + Report a bug + Failed fallback preview Report bug hyperlink button content. + + + An error occurred while previewing this file. + Failed fallback preview text. + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/Views/ShellPage.xaml b/src/settings-ui/Settings.UI/Views/ShellPage.xaml index c82a555699..3045320934 100644 --- a/src/settings-ui/Settings.UI/Views/ShellPage.xaml +++ b/src/settings-ui/Settings.UI/Views/ShellPage.xaml @@ -22,9 +22,7 @@ - + + SelectedItem="{x:Bind ViewModel.Selected, Mode=OneWay}" + SelectionChanged="NavigationView_SelectionChanged"> - - + + + Icon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsPeek.png}" /> + Icon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsRegistryPreview.png}" />