Skip to content

Commit

Permalink
[Peek] Refactoring of file system models, removal of PngPreviewer, re…
Browse files Browse the repository at this point in the history
…trieving 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
  • Loading branch information
SamChaps authored Feb 9, 2023
1 parent 07e9780 commit e7b62a5
Show file tree
Hide file tree
Showing 42 changed files with 456 additions and 488 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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.Threading.Tasks;
using Peek.Common.Models;
using Scripting;
using Windows.Foundation;
using Windows.Storage;

namespace Peek.Common.Extensions
{
public static class IFileSystemItemExtensions
{
public static Size GetImageSize(this IFileSystemItem item)
{
var propertyStore = item.PropertyStore;
var width = propertyStore.TryGetUInt(PropertyKey.ImageHorizontalSize) ?? 0;
var height = propertyStore.TryGetUInt(PropertyKey.ImageVerticalSize) ?? 0;

var size = new Size((int)width, (int)height);

return size;
}

public static ulong GetSizeInBytes(this IFileSystemItem item)
{
ulong sizeInBytes = 0;

switch (item)
{
case FolderItem _:
FileSystemObject fileSystemObject = new FileSystemObject();
Folder folder = fileSystemObject.GetFolder(item.Path);
sizeInBytes = (ulong)folder.Size;
break;
case FileItem _:
var propertyStore = item.PropertyStore;
sizeInBytes = propertyStore.TryGetULong(PropertyKey.FileSizeBytes) ?? 0;
break;
}

return sizeInBytes;
}

public static async Task<string> GetContentTypeAsync(this IFileSystemItem item)
{
string contentType = string.Empty;

var storageItem = await item.GetStorageItemAsync();
switch (storageItem)
{
case StorageFile storageFile:
contentType = storageFile.DisplayType;
break;
case StorageFolder storageFolder:
contentType = storageFolder.DisplayType;
break;
default:
var propertyStore = item.PropertyStore;
contentType = propertyStore.TryGetString(PropertyKey.FileType) ?? string.Empty;
break;
}

return contentType;
}
}
}
119 changes: 79 additions & 40 deletions src/modules/peek/Peek.Common/Extensions/IPropertyStoreExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,32 @@ public static class IPropertyStoreExtensions
/// <param name="propertyStore">The property store</param>
/// <param name="key">The pkey</param>
/// <returns>The uint value</returns>
public static uint GetUInt(this IPropertyStore propertyStore, PropertyKey key)
public static uint? TryGetUInt(this IPropertyStore propertyStore, PropertyKey key)
{
if (propertyStore == null)
{
throw new ArgumentNullException("propertyStore");
return null;
}

PropVariant propVar;
try
{
PropVariant propVar;

propertyStore.GetValue(ref key, out propVar);
propertyStore.GetValue(ref key, out propVar);

// VT_UI4 Indicates a 4-byte unsigned integer formatted in little-endian byte order.
if ((VarEnum)propVar.Vt == VarEnum.VT_UI4)
{
return propVar.UlVal;
// VT_UI4 Indicates a 4-byte unsigned integer formatted in little-endian byte order.
if ((VarEnum)propVar.Vt == VarEnum.VT_UI4)
{
return propVar.UlVal;
}
else
{
return null;
}
}
else
catch (Exception)
{
return 0;
return null;
}
}

Expand All @@ -47,25 +54,32 @@ public static uint GetUInt(this IPropertyStore propertyStore, PropertyKey key)
/// <param name="propertyStore">The property store</param>
/// <param name="key">the pkey</param>
/// <returns>the ulong value</returns>
public static ulong GetULong(this IPropertyStore propertyStore, PropertyKey key)
public static ulong? TryGetULong(this IPropertyStore propertyStore, PropertyKey key)
{
if (propertyStore == null)
{
throw new ArgumentNullException("propertyStore");
return null;
}

PropVariant propVar;
try
{
PropVariant propVar;

propertyStore.GetValue(ref key, out propVar);
propertyStore.GetValue(ref key, out propVar);

// VT_UI8 Indicates an 8-byte unsigned integer formatted in little-endian byte order.
if ((VarEnum)propVar.Vt == VarEnum.VT_UI8)
{
return propVar.UhVal;
// VT_UI8 Indicates an 8-byte unsigned integer formatted in little-endian byte order.
if ((VarEnum)propVar.Vt == VarEnum.VT_UI8)
{
return propVar.UhVal;
}
else
{
return null;
}
}
else
catch (Exception)
{
return 0;
return null;
}
}

Expand All @@ -75,19 +89,31 @@ public static ulong GetULong(this IPropertyStore propertyStore, PropertyKey key)
/// <param name="propertyStore">The property store</param>
/// <param name="key">The pkey</param>
/// <returns>The string value</returns>
public static string GetString(this IPropertyStore propertyStore, PropertyKey key)
public static string? TryGetString(this IPropertyStore propertyStore, PropertyKey key)
{
PropVariant propVar;

propertyStore.GetValue(ref key, out propVar);
if (propertyStore == null)
{
return null;
}

if ((VarEnum)propVar.Vt == VarEnum.VT_LPWSTR)
try
{
return Marshal.PtrToStringUni(propVar.P) ?? string.Empty;
PropVariant propVar;

propertyStore.GetValue(ref key, out propVar);

if ((VarEnum)propVar.Vt == VarEnum.VT_LPWSTR)
{
return Marshal.PtrToStringUni(propVar.P) ?? string.Empty;
}
else
{
return null;
}
}
else
catch (Exception)
{
return string.Empty;
return null;
}
}

Expand All @@ -97,26 +123,39 @@ public static string GetString(this IPropertyStore propertyStore, PropertyKey ke
/// <param name="propertyStore">The property store</param>
/// <param name="key">The pkey</param>
/// <returns>The array of string values</returns>
public static string[] GetStringArray(this IPropertyStore propertyStore, PropertyKey key)
public static string[]? TryGetStringArray(this IPropertyStore propertyStore, PropertyKey key)
{
PropVariant propVar;
propertyStore.GetValue(ref key, out propVar);

List<string> values = new List<string>();
if (propertyStore == null)
{
return null;
}

if ((VarEnum)propVar.Vt == (VarEnum.VT_LPWSTR | VarEnum.VT_VECTOR))
try
{
for (int elementIndex = 0; elementIndex < propVar.Calpwstr.CElems; elementIndex++)
PropVariant propVar;
propertyStore.GetValue(ref key, out propVar);

List<string>? values = null;

if ((VarEnum)propVar.Vt == (VarEnum.VT_LPWSTR | VarEnum.VT_VECTOR))
{
var stringVal = Marshal.PtrToStringUni(Marshal.ReadIntPtr(propVar.Calpwstr.PElems, elementIndex));
if (stringVal != null)
values = new List<string>();
for (int elementIndex = 0; elementIndex < propVar.Calpwstr.CElems; elementIndex++)
{
values.Add(stringVal);
var stringVal = Marshal.PtrToStringUni(Marshal.ReadIntPtr(propVar.Calpwstr.PElems, elementIndex));
if (stringVal != null)
{
values.Add(stringVal);
}
}
}
}

return values.ToArray();
return values?.ToArray();
}
catch (Exception)
{
return null;
}
}
}
}
33 changes: 33 additions & 0 deletions src/modules/peek/Peek.Common/Helpers/ClipboardHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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 Windows.ApplicationModel.DataTransfer;
using Windows.Storage;
using Windows.Storage.Streams;

namespace Peek.Common.Helpers
{
public static class ClipboardHelper
{
public static void SaveToClipboard(IStorageItem? storageItem)
{
if (storageItem == null)
{
return;
}

var dataPackage = new DataPackage();
dataPackage.SetStorageItems(new IStorageItem[1] { storageItem }, false);

if (storageItem is StorageFile storageFile)
{
RandomAccessStreamReference imageStreamRef = RandomAccessStreamReference.CreateFromFile(storageFile);
dataPackage.Properties.Thumbnail = imageStreamRef;
dataPackage.SetBitmap(imageStreamRef);
}

Clipboard.SetContent(dataPackage);
}
}
}
52 changes: 52 additions & 0 deletions src/modules/peek/Peek.Common/Models/FileItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// 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.Threading.Tasks;
using Peek.Common.Helpers;
using Windows.Storage;

#nullable enable

namespace Peek.Common.Models
{
public class FileItem : IFileSystemItem
{
private StorageFile? storageFile;

private Lazy<IPropertyStore> _propertyStore;

public FileItem(string path)
{
Path = path;
_propertyStore = new(() => PropertyStoreHelper.GetPropertyStoreFromPath(Path));
}

public string Path { get; init; }

public IPropertyStore PropertyStore => _propertyStore.Value;

public async Task<IStorageItem?> GetStorageItemAsync()
{
return await GetStorageFileAsync();
}

public async Task<StorageFile?> GetStorageFileAsync()
{
if (storageFile == null)
{
try
{
storageFile = await StorageFile.GetFileFromPathAsync(Path);
}
catch (Exception)
{
storageFile = null;
}
}

return storageFile;
}
}
}
52 changes: 52 additions & 0 deletions src/modules/peek/Peek.Common/Models/FolderItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// 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.Threading.Tasks;
using Peek.Common.Helpers;
using Windows.Storage;

#nullable enable

namespace Peek.Common.Models
{
public class FolderItem : IFileSystemItem
{
private StorageFolder? storageFolder;

private Lazy<IPropertyStore> _propertyStore;

public FolderItem(string path)
{
Path = path;
_propertyStore = new(() => PropertyStoreHelper.GetPropertyStoreFromPath(Path));
}

public string Path { get; init; }

public IPropertyStore PropertyStore => _propertyStore.Value;

public async Task<IStorageItem?> GetStorageItemAsync()
{
return await GetStorageFolderAsync();
}

public async Task<StorageFolder?> GetStorageFolderAsync()
{
if (storageFolder == null)
{
try
{
storageFolder = await StorageFolder.GetFolderFromPathAsync(Path);
}
catch (Exception)
{
storageFolder = null;
}
}

return storageFolder;
}
}
}
Loading

1 comment on commit e7b62a5

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@check-spelling-bot Report

🔴 Please review

See the 📜action log for details.

Unrecognized words (13)
ALLVIEW
Chromakey
GETPROPERTYSTOREFLAGS
holemenu
IPalette
ipreview
pkey
ppshv
rbhid
SHCONTF
SHGDNF
SIATTRIBFLAGS
Strm
Previously acknowledged words that are now absent Asn BESTEFFORT BHIDSF Brotli clrcompression clretwrc clrgc clrjit Cng coreclr dbgshim DCompiler DELAYCREATION Dwma EXTRINSICPROPERTIESONLY FASTPROPERTIESONLY HANDLERPROPERTIESONLY hostfxr hostpolicy IIDI Imc Intrinsics IPREVIEW JSONOf Keybd LANGID mscordaccore mscordbi mscorrc msquic neighborings netstandard OPENSLOWITEM OPLOCK Pkcs PKEY PREFERQUERYPROPERTIES Previer psfi Quic rfid RTSS Shcontf Shgno Softcoded Ssl UMsg WMSDK wpfgfx WScan WVk zopfli :arrow_right:
Some files were automatically ignored

These sample patterns would exclude them:

^\Qsrc/modules/peek/Peek.Common/NativeMethods.txt\E$

You should consider adding them to:

.github/actions/spell-check/excludes.txt

File matching is via Perl regular expressions.

To check these files, more of their words need to be in the dictionary than not. You can use patterns.txt to exclude portions, add items to the dictionary (e.g. by adding them to allow.txt), or fix typos.

To accept ✔️ these unrecognized words as correct and remove the previously acknowledged and now absent words, run the following commands

... in a clone of the git@github.com:microsoft/PowerToys.git repository
on the peek branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/v0.0.21/apply.pl' |
perl - 'https://github.com/microsoft/PowerToys/actions/runs/4129625151/attempts/1'
Available 📚 dictionaries could cover words not in the 📘 dictionary

This includes both expected items (2294) from .github/actions/spell-check/expect.txt and unrecognized words (13)

Dictionary Entries Covers
cspell:win32/src/win32.txt 53509 134
cspell:cpp/src/cpp.txt 30216 129
cspell:python/src/python/python-lib.txt 3873 32
cspell:php/php.txt 2597 17
cspell:node/node.txt 1768 13
cspell:typescript/typescript.txt 1211 12
cspell:python/src/python/python.txt 453 11
cspell:java/java.txt 7642 11
cspell:aws/aws.txt 218 8
cspell:python/src/common/extra.txt 741 7

Consider adding them using (in .github/workflows/spelling2.yml):

      with:
        extra_dictionaries:
          cspell:win32/src/win32.txt
          cspell:cpp/src/cpp.txt
          cspell:python/src/python/python-lib.txt
          cspell:php/php.txt
          cspell:node/node.txt
          cspell:typescript/typescript.txt
          cspell:python/src/python/python.txt
          cspell:java/java.txt
          cspell:aws/aws.txt
          cspell:python/src/common/extra.txt

To stop checking additional dictionaries, add:

      with:
        check_extra_dictionaries: ''
Warnings (1)

See the 📜action log for details.

ℹ️ Warnings Count
ℹ️ noisy-file 1

See ℹ️ Event descriptions for more information.

If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Please sign in to comment.