From bc0811e6a11611a8bf060ef9c11200a6e5046385 Mon Sep 17 00:00:00 2001 From: Ani <115020168+drawbyperpetual@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:40:25 +0200 Subject: [PATCH 01/28] [Peek]Support for special folders like Recycle Bin and My PC (#33310) * Peek support for special folders * Renamed ThisComputer->ThisPC * Used different variable name to avoid spellcheck issues * Made label of empty fields hidden * Removed ThisPC special handling and last modified date of recycle bin --- Directory.Build.props | 2 +- .../peek/Peek.Common/Helpers/PathHelper.cs | 12 + .../peek/Peek.Common/Helpers/ThreadHelper.cs | 26 +++ .../peek/Peek.Common/Models/FileItem.cs | 2 + .../peek/Peek.Common/Models/FolderItem.cs | 14 +- .../Peek.Common/Models/IFileSystemItem.cs | 13 +- ...cialFolderInformationalPreviewControl.xaml | 57 +++++ ...lFolderInformationalPreviewControl.xaml.cs | 41 ++++ .../SpecialFolderPreview.xaml | 25 ++ .../SpecialFolderPreview.xaml.cs | 47 ++++ .../peek/Peek.FilePreviewer/FilePreview.xaml | 6 + .../Peek.FilePreviewer/FilePreview.xaml.cs | 3 + .../Models/SpecialFolderPreviewData.cs | 26 +++ .../Peek.FilePreviewer.csproj | 26 +++ .../Interfaces/ISpecialFolderPreviewer.cs | 12 + .../Previewers/PreviewerFactory.cs | 4 + .../Helpers/KnownSpecialFolders.cs | 58 +++++ .../SpecialFolderPreviewer.cs | 215 ++++++++++++++++++ .../UnsupportedFilePreviewer.cs | 2 - .../Extensions/IShellItemExtensions.cs | 62 +++-- 20 files changed, 599 insertions(+), 54 deletions(-) create mode 100644 src/modules/peek/Peek.Common/Helpers/PathHelper.cs create mode 100644 src/modules/peek/Peek.Common/Helpers/ThreadHelper.cs create mode 100644 src/modules/peek/Peek.FilePreviewer/Controls/SpecialFolderPreview/SpecialFolderInformationalPreviewControl.xaml create mode 100644 src/modules/peek/Peek.FilePreviewer/Controls/SpecialFolderPreview/SpecialFolderInformationalPreviewControl.xaml.cs create mode 100644 src/modules/peek/Peek.FilePreviewer/Controls/SpecialFolderPreview/SpecialFolderPreview.xaml create mode 100644 src/modules/peek/Peek.FilePreviewer/Controls/SpecialFolderPreview/SpecialFolderPreview.xaml.cs create mode 100644 src/modules/peek/Peek.FilePreviewer/Models/SpecialFolderPreviewData.cs create mode 100644 src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/ISpecialFolderPreviewer.cs create mode 100644 src/modules/peek/Peek.FilePreviewer/Previewers/SpecialFolderPreviewer/Helpers/KnownSpecialFolders.cs create mode 100644 src/modules/peek/Peek.FilePreviewer/Previewers/SpecialFolderPreviewer/SpecialFolderPreviewer.cs diff --git a/Directory.Build.props b/Directory.Build.props index e32e82a180..da2760b068 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -85,7 +85,7 @@ false - 202310210737 + 202406130737 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/SpecialFolderPreview/SpecialFolderInformationalPreviewControl.xaml.cs b/src/modules/peek/Peek.FilePreviewer/Controls/SpecialFolderPreview/SpecialFolderInformationalPreviewControl.xaml.cs new file mode 100644 index 0000000000..9ae7a610ae --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Controls/SpecialFolderPreview/SpecialFolderInformationalPreviewControl.xaml.cs @@ -0,0 +1,41 @@ +// 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; +using Microsoft.UI.Xaml.Controls; +using Peek.Common.Helpers; +using Peek.FilePreviewer.Models; + +namespace Peek.FilePreviewer.Controls; + +public sealed partial class SpecialFolderInformationalPreviewControl : UserControl +{ + public static readonly DependencyProperty SourceProperty = DependencyProperty.Register( + nameof(Source), + typeof(SpecialFolderPreviewData), + typeof(SpecialFolderInformationalPreviewControl), + new PropertyMetadata(null)); + + public SpecialFolderPreviewData? Source + { + get { return (SpecialFolderPreviewData)GetValue(SourceProperty); } + set { SetValue(SourceProperty, value); } + } + + public SpecialFolderInformationalPreviewControl() + { + InitializeComponent(); + } + + public string FormatFileType(string? fileType) => FormatField("UnsupportedFile_FileType", fileType); + + public string FormatFileSize(string? fileSize) => FormatField("UnsupportedFile_FileSize", fileSize); + + public string FormatFileDateModified(string? fileDateModified) => FormatField("UnsupportedFile_DateModified", fileDateModified); + + private static string FormatField(string resourceId, string? fieldValue) + { + return string.IsNullOrWhiteSpace(fieldValue) ? string.Empty : ReadableStringHelper.FormatResourceString(resourceId, fieldValue); + } +} diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/SpecialFolderPreview/SpecialFolderPreview.xaml b/src/modules/peek/Peek.FilePreviewer/Controls/SpecialFolderPreview/SpecialFolderPreview.xaml new file mode 100644 index 0000000000..ec08d0396d --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Controls/SpecialFolderPreview/SpecialFolderPreview.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/SpecialFolderPreview/SpecialFolderPreview.xaml.cs b/src/modules/peek/Peek.FilePreviewer/Controls/SpecialFolderPreview/SpecialFolderPreview.xaml.cs new file mode 100644 index 0000000000..b1a468c3fa --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Controls/SpecialFolderPreview/SpecialFolderPreview.xaml.cs @@ -0,0 +1,47 @@ +// 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 SpecialFolderPreview : UserControl +{ + public static readonly DependencyProperty SourceProperty = DependencyProperty.Register( + nameof(Source), + typeof(SpecialFolderPreviewData), + typeof(SpecialFolderPreview), + new PropertyMetadata(null)); + + public static readonly DependencyProperty LoadingStateProperty = DependencyProperty.Register( + nameof(LoadingState), + typeof(PreviewState), + typeof(SpecialFolderPreview), + new PropertyMetadata(PreviewState.Uninitialized)); + + public SpecialFolderPreviewData? Source + { + get { return (SpecialFolderPreviewData)GetValue(SourceProperty); } + set { SetValue(SourceProperty, value); } + } + + public PreviewState? LoadingState + { + get { return (PreviewState)GetValue(LoadingStateProperty); } + set { SetValue(LoadingStateProperty, value); } + } + + public SpecialFolderPreview() + { + 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 67ffe62d84..5d47fbe962 100644 --- a/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml +++ b/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml @@ -83,6 +83,12 @@ Source="{x:Bind DrivePreviewer.Preview, Mode=OneWay}" Visibility="{x:Bind IsPreviewVisible(DrivePreviewer, Previewer.State), Mode=OneWay}" /> + + Previewer as IDrivePreviewer; + public ISpecialFolderPreviewer? SpecialFolderPreviewer => Previewer as ISpecialFolderPreviewer; + public IUnsupportedFilePreviewer? UnsupportedFilePreviewer => Previewer as IUnsupportedFilePreviewer; public IFileSystemItem Item diff --git a/src/modules/peek/Peek.FilePreviewer/Models/SpecialFolderPreviewData.cs b/src/modules/peek/Peek.FilePreviewer/Models/SpecialFolderPreviewData.cs new file mode 100644 index 0000000000..c6549ba50a --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Models/SpecialFolderPreviewData.cs @@ -0,0 +1,26 @@ +// 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 SpecialFolderPreviewData : ObservableObject +{ + [ObservableProperty] + private ImageSource? iconPreview; + + [ObservableProperty] + private string? fileName; + + [ObservableProperty] + private string? fileType; + + [ObservableProperty] + private string? fileSize; + + [ObservableProperty] + private string? dateModified; +} diff --git a/src/modules/peek/Peek.FilePreviewer/Peek.FilePreviewer.csproj b/src/modules/peek/Peek.FilePreviewer/Peek.FilePreviewer.csproj index 8ffcb6cc8a..495a21289f 100644 --- a/src/modules/peek/Peek.FilePreviewer/Peek.FilePreviewer.csproj +++ b/src/modules/peek/Peek.FilePreviewer/Peek.FilePreviewer.csproj @@ -21,6 +21,8 @@ + + @@ -42,6 +44,27 @@ + + + 0 + 1 + 50a7e9b0-70ef-11d1-b75a-00a0c90564fe + 0 + tlbimp + false + true + + + 1 + 1 + eab22ac0-30c1-11cf-a7eb-0000c05bae0b + 0 + tlbimp + false + true + + + @@ -71,6 +94,9 @@ MSBuild:Compile + + MSBuild:Compile + diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/ISpecialFolderPreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/ISpecialFolderPreviewer.cs new file mode 100644 index 0000000000..a5d663a2e4 --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/ISpecialFolderPreviewer.cs @@ -0,0 +1,12 @@ +// 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.Models; + +namespace Peek.FilePreviewer.Previewers; + +public interface ISpecialFolderPreviewer : IPreviewer +{ + public SpecialFolderPreviewData? Preview { get; } +} diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/PreviewerFactory.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/PreviewerFactory.cs index 158521b3ca..cce79a6235 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/PreviewerFactory.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/PreviewerFactory.cs @@ -53,6 +53,10 @@ namespace Peek.FilePreviewer.Previewers { return new DrivePreviewer(item); } + else if (SpecialFolderPreviewer.IsItemSupported(item)) + { + return new SpecialFolderPreviewer(item); + } // Other previewer types check their supported file types here return CreateDefaultPreviewer(item); diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/SpecialFolderPreviewer/Helpers/KnownSpecialFolders.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/SpecialFolderPreviewer/Helpers/KnownSpecialFolders.cs new file mode 100644 index 0000000000..930cc5cc60 --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/SpecialFolderPreviewer/Helpers/KnownSpecialFolders.cs @@ -0,0 +1,58 @@ +// 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.Collections.Generic; +using System.Linq; +using Peek.Common; +using Peek.Common.Models; + +namespace Peek.FilePreviewer.Previewers; + +public enum KnownSpecialFolder +{ + None, + RecycleBin, +} + +public static class KnownSpecialFolders +{ + private static readonly Lazy> FoldersByParsingNameDict = new(GetFoldersByParsingName); + + public static IReadOnlyDictionary FoldersByParsingName => FoldersByParsingNameDict.Value; + + private static Dictionary GetFoldersByParsingName() + { + var folders = new (KnownSpecialFolder Folder, string? ParsingName)[] + { + (KnownSpecialFolder.RecycleBin, GetParsingName("shell:RecycleBinFolder")), + }; + + return folders.Where(folder => !string.IsNullOrEmpty(folder.ParsingName)) + .ToDictionary(folder => folder.ParsingName!, folder => folder.Folder); + } + + private static string? GetParsingName(string shellName) + { + try + { + return CreateShellItemFromShellName(shellName)?.GetDisplayName(Windows.Win32.UI.Shell.SIGDN.SIGDN_DESKTOPABSOLUTEPARSING); + } + catch (Exception) + { + return null; + } + } + + private static IShellItem? CreateShellItemFromShellName(string shellName) + { + // Based on https://stackoverflow.com/a/42966899 + const string ShellItem = "43826d1e-e718-42ee-bc55-a1e261c37bfe"; + + Guid shellItem2Guid = new(ShellItem); + int retCode = NativeMethods.SHCreateItemFromParsingName(shellName, IntPtr.Zero, ref shellItem2Guid, out IShellItem? nativeShellItem); + + return retCode == 0 ? nativeShellItem : null; + } +} diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/SpecialFolderPreviewer/SpecialFolderPreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/SpecialFolderPreviewer/SpecialFolderPreviewer.cs new file mode 100644 index 0000000000..124071e54a --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/SpecialFolderPreviewer/SpecialFolderPreviewer.cs @@ -0,0 +1,215 @@ +// 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.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using ManagedCommon; +using Microsoft.UI.Dispatching; +using Microsoft.UI.Xaml; +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; + +namespace Peek.FilePreviewer.Previewers; + +public partial class SpecialFolderPreviewer : ObservableObject, ISpecialFolderPreviewer, IDisposable +{ + private readonly DispatcherTimer _syncDetailsDispatcherTimer = new(); + private ulong _folderSize; + private DateTime? _dateModified; + + [ObservableProperty] + private SpecialFolderPreviewData preview = new(); + + [ObservableProperty] + private PreviewState state; + + public SpecialFolderPreviewer(IFileSystemItem file) + { + _syncDetailsDispatcherTimer.Interval = TimeSpan.FromMilliseconds(500); + _syncDetailsDispatcherTimer.Tick += DetailsDispatcherTimer_Tick; + + Item = file; + Preview.FileName = file.Name; + Dispatcher = DispatcherQueue.GetForCurrentThread(); + } + + public static bool IsItemSupported(IFileSystemItem item) + { + // Always allow know special folders. + bool isKnownSpecialFolder = KnownSpecialFolders.FoldersByParsingName.ContainsKey(item.ParsingName); + + // Allow empty paths unless Unc; icons don't load correctly for Unc paths. + bool isEmptyNonUncPath = string.IsNullOrEmpty(item.Path) && !PathHelper.IsUncPath(item.ParsingName); + + return isKnownSpecialFolder || isEmptyNonUncPath; + } + + private IFileSystemItem Item { get; } + + private DispatcherQueue Dispatcher { get; } + + public void Dispose() + { + _syncDetailsDispatcherTimer.Tick -= DetailsDispatcherTimer_Tick; + GC.SuppressFinalize(this); + } + + public Task GetPreviewSizeAsync(CancellationToken cancellationToken) + { + Size? size = new(680, 500); + var previewSize = new PreviewSize { MonitorSize = size, UseEffectivePixels = true }; + return Task.FromResult(previewSize); + } + + public async Task LoadPreviewAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + State = PreviewState.Loading; + + var tasks = await Task.WhenAll(LoadIconPreviewAsync(cancellationToken), LoadDisplayInfoAsync(cancellationToken)); + + State = tasks.All(task => task) ? PreviewState.Loaded : PreviewState.Error; + } + + public async Task CopyAsync() + { + await Dispatcher.RunOnUiThread(async () => + { + var storageItem = await Item.GetStorageItemAsync(); + ClipboardHelper.SaveToClipboard(storageItem); + }); + } + + public async Task LoadIconPreviewAsync(CancellationToken cancellationToken) + { + bool isIconValid = false; + + var isTaskSuccessful = await TaskExtension.RunSafe(async () => + { + cancellationToken.ThrowIfCancellationRequested(); + + await Dispatcher.RunOnUiThread(async () => + { + cancellationToken.ThrowIfCancellationRequested(); + + var iconBitmap = await IconHelper.GetIconAsync(Item.ParsingName, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + + isIconValid = iconBitmap != null; + Preview.IconPreview = iconBitmap ?? new SvgImageSource(new Uri("ms-appx:///Assets/Peek/DefaultFileIcon.svg")); + }); + }); + + return isIconValid && isTaskSuccessful; + } + + public async Task LoadDisplayInfoAsync(CancellationToken cancellationToken) + { + bool isDisplayValid = false; + + var isTaskSuccessful = await TaskExtension.RunSafe(async () => + { + cancellationToken.ThrowIfCancellationRequested(); + + var fileType = await Task.Run(Item.GetContentTypeAsync); + + cancellationToken.ThrowIfCancellationRequested(); + + isDisplayValid = fileType != null; + + await Dispatcher.RunOnUiThread(() => + { + Preview.FileType = fileType; + return Task.CompletedTask; + }); + + RunUpdateDetailsWorkflow(cancellationToken); + }); + + return isDisplayValid && isTaskSuccessful; + } + + private void RunUpdateDetailsWorkflow(CancellationToken cancellationToken) + { + Task.Run( + async () => + { + try + { + await Dispatcher.RunOnUiThread(_syncDetailsDispatcherTimer.Start); + ComputeDetails(cancellationToken); + } + catch (OperationCanceledException) + { + } + catch (Exception ex) + { + Logger.LogError("Failed to update special folder details", ex); + } + finally + { + await Dispatcher.RunOnUiThread(_syncDetailsDispatcherTimer.Stop); + } + + await Dispatcher.RunOnUiThread(SyncDetails); + }, + cancellationToken); + } + + private void ComputeDetails(CancellationToken cancellationToken) + { + _dateModified = Item.DateModified; + + switch (KnownSpecialFolders.FoldersByParsingName.GetValueOrDefault(Item.ParsingName, KnownSpecialFolder.None)) + { + case KnownSpecialFolder.None: + break; + + case KnownSpecialFolder.RecycleBin: + ThreadHelper.RunOnSTAThread(() => { ComputeRecycleBinDetails(cancellationToken); }); + cancellationToken.ThrowIfCancellationRequested(); + break; + } + } + + private void ComputeRecycleBinDetails(CancellationToken cancellationToken) + { + var shell = new Shell32.Shell(); + var recycleBin = shell.NameSpace(10); // CSIDL_BITBUCKET + + foreach (dynamic item in recycleBin.Items()) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + _folderSize += Convert.ToUInt64(item.Size); + } + } + + private void SyncDetails() + { + Preview.FileSize = _folderSize == 0 ? string.Empty : ReadableStringHelper.BytesToReadableString(_folderSize); + Preview.DateModified = _dateModified?.ToString(CultureInfo.CurrentCulture) ?? string.Empty; + } + + private void DetailsDispatcherTimer_Tick(object? sender, object e) + { + SyncDetails(); + } +} diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/UnsupportedFilePreviewer/UnsupportedFilePreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/UnsupportedFilePreviewer/UnsupportedFilePreviewer.cs index 1b6b526bd0..185ca19ee9 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/UnsupportedFilePreviewer/UnsupportedFilePreviewer.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/UnsupportedFilePreviewer/UnsupportedFilePreviewer.cs @@ -45,8 +45,6 @@ namespace Peek.FilePreviewer.Previewers Dispatcher = DispatcherQueue.GetForCurrentThread(); } - public bool IsPreviewLoaded => Preview.IconPreview != null; - private IFileSystemItem Item { get; } private DispatcherQueue Dispatcher { get; } diff --git a/src/modules/peek/Peek.UI/Extensions/IShellItemExtensions.cs b/src/modules/peek/Peek.UI/Extensions/IShellItemExtensions.cs index 3d2fd2e57f..7e4eee002b 100644 --- a/src/modules/peek/Peek.UI/Extensions/IShellItemExtensions.cs +++ b/src/modules/peek/Peek.UI/Extensions/IShellItemExtensions.cs @@ -7,49 +7,41 @@ using System.IO; using ManagedCommon; using Peek.Common.Models; -namespace Peek.UI.Extensions +namespace Peek.UI.Extensions; + +public static class IShellItemExtensions { - public static class IShellItemExtensions + public static IFileSystemItem ToIFileSystemItem(this IShellItem shellItem) { - public static IFileSystemItem ToIFileSystemItem(this IShellItem shellItem) - { - string path = shellItem.GetPath(); - string name = shellItem.GetName(); + string path = shellItem.GetPath(); + string name = shellItem.GetName(); - return File.Exists(path) ? new FileItem(path, name) : new FolderItem(path, name); + return File.Exists(path) ? new FileItem(path, name) : new FolderItem(path, name, shellItem.GetParsingName()); + } + + private static string GetPath(this IShellItem shellItem) => + shellItem.GetNameCore(Windows.Win32.UI.Shell.SIGDN.SIGDN_FILESYSPATH, logError: false); + + private static string GetName(this IShellItem shellItem) => + shellItem.GetNameCore(Windows.Win32.UI.Shell.SIGDN.SIGDN_NORMALDISPLAY, logError: true); + + private static string GetParsingName(this IShellItem shellItem) => + shellItem.GetNameCore(Windows.Win32.UI.Shell.SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, logError: true); + + private static string GetNameCore(this IShellItem shellItem, Windows.Win32.UI.Shell.SIGDN displayNameType, bool logError) + { + try + { + return shellItem.GetDisplayName(displayNameType); } - - private static string GetPath(this IShellItem shellItem) + catch (Exception ex) { - string path = string.Empty; - try + if (logError) { - path = shellItem.GetDisplayName(Windows.Win32.UI.Shell.SIGDN.SIGDN_FILESYSPATH); - } - 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); + Logger.LogError($"Getting {Enum.GetName(displayNameType)} failed. {ex.Message}"); } - 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; + return string.Empty; } } } From cdf5677eb9c6ab35d7355c241cfd0c079270c42b Mon Sep 17 00:00:00 2001 From: PesBandi <127593627+PesBandi@users.noreply.github.com> Date: Fri, 14 Jun 2024 17:09:52 +0200 Subject: [PATCH 02/28] [Monaco]Improve `.gitignore` definition (#33263) * Update gitignore.js * Update gitignore.js * Add custom color for negations * Add custom color for negations * Regex refactoring * Regex refactoring again * Move customTokenColors to a separate file * Move customTokenColors to a separate file * Update devdocs * Use kebab case for token names * Update negation color * Update index.html formatting --- doc/devdocs/common/FilePreviewCommon.md | 11 ++++- installer/PowerToysSetup/generateFileList.ps1 | 2 +- .../Monaco/customLanguages/gitignore.js | 4 +- .../Assets/Monaco/customTokenColors.js | 3 ++ .../Assets/Monaco/index.html | 45 +++++++++++-------- .../FilePreviewCommon.csproj | 3 ++ 6 files changed, 46 insertions(+), 22 deletions(-) create mode 100644 src/common/FilePreviewCommon/Assets/Monaco/customTokenColors.js diff --git a/doc/devdocs/common/FilePreviewCommon.md b/doc/devdocs/common/FilePreviewCommon.md index 7293e8c978..aee9de2b2a 100644 --- a/doc/devdocs/common/FilePreviewCommon.md +++ b/doc/devdocs/common/FilePreviewCommon.md @@ -47,7 +47,16 @@ registerAdditionalNewLanguage("id", [".fileExtension"], idDefinition(), monaco) * The id can be anything. Recommended is one of the file extensions. For example "php" or "reg". -4. Execute the steps described in the [monaco_languages.json](#monaco_languagesjson) section. +4. In case you wish to add a custom color for a token, you can do so by adding the following line to [`customTokenColors.js`](/src/common/FilePreviewCommon/Assets/Monaco/customTokenColors.js): +```javascript +{token: 'token-name', foreground: 'ff0000'} +``` +> Replace `token-name` with the name of the token and `ff0000` with the hex code of the desired color. +> Note: you can also specify a `background` and a `fontStyle` attribute for your token. + +* Keep in mind that these rules apply to all languages. Therefore, you should not change the colors of any default tokens. Instead, create new tokens specific to the language you are adding. + +5. Execute the steps described in the [monaco_languages.json](#monaco_languagesjson) section. ### Add a new file extension to an existing language diff --git a/installer/PowerToysSetup/generateFileList.ps1 b/installer/PowerToysSetup/generateFileList.ps1 index 9d9fe36c10..f674eae374 100644 --- a/installer/PowerToysSetup/generateFileList.ps1 +++ b/installer/PowerToysSetup/generateFileList.ps1 @@ -21,7 +21,7 @@ $fileWxs = Get-Content $wxsFilePath; $fileExclusionList = @("*.pdb", "*.lastcodeanalysissucceeded", "createdump.exe", "powertoys.exe") -$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "monacoSpecialLanguages.js", "*.pri") +$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "monacoSpecialLanguages.js", "customTokenColors.js", "*.pri") $dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll") diff --git a/src/common/FilePreviewCommon/Assets/Monaco/customLanguages/gitignore.js b/src/common/FilePreviewCommon/Assets/Monaco/customLanguages/gitignore.js index 365f8f69cc..e4539dd50a 100644 --- a/src/common/FilePreviewCommon/Assets/Monaco/customLanguages/gitignore.js +++ b/src/common/FilePreviewCommon/Assets/Monaco/customLanguages/gitignore.js @@ -5,8 +5,8 @@ tokenizer: { root: [ [/^#.*$/, 'comment'], - [/^\s*!.*/, 'invalid'], - [/^\s*[^#]+/, "tag"] + [/.*((?