diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 3aa3eb1008..093eb881b0 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -48,6 +48,7 @@ body:
- PowerRename
- PowerToys Run
- Shortcut Guide
+ - STL Thumbnail
- SVG Preview
- SVG Thumbnail
- Settings
diff --git a/.github/actions/spell-check/excludes.txt b/.github/actions/spell-check/excludes.txt
index e87774eab0..82952268ee 100644
--- a/.github/actions/spell-check/excludes.txt
+++ b/.github/actions/spell-check/excludes.txt
@@ -26,6 +26,7 @@ ignore$
\.pdf$
\.PNG$
\.png$
+\.stl$
\.woff$
\.zip$
^doc/devdocs/akaLinks\.md$
diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt
index 3156e45743..dd6126fd24 100644
--- a/.github/actions/spell-check/expect.txt
+++ b/.github/actions/spell-check/expect.txt
@@ -1103,6 +1103,7 @@ LPVOID
LPW
lpwndpl
LPWSTR
+LReader
LRESULT
lshift
lstrcmp
@@ -1397,6 +1398,7 @@ outsettings
OVERLAPPEDWINDOW
overlaywindow
Overridable
+Oversampling
OWNDC
PACL
PAINTSTRUCT
@@ -1861,6 +1863,7 @@ STEPIT
stgm
STGMEDIUM
sticpl
+stl
stoi
stol
stoll
diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json
index 618e437b9c..1af2b33365 100644
--- a/.pipelines/ESRPSigning_core.json
+++ b/.pipelines/ESRPSigning_core.json
@@ -57,6 +57,8 @@
"modules\\FileExplorerPreview\\PowerToys.PdfThumbnailProvider.comhost.dll",
"modules\\FileExplorerPreview\\PowerToys.powerpreview.dll",
"modules\\FileExplorerPreview\\PowerToys.PreviewHandlerCommon.dll",
+ "modules\\FileExplorerPreview\\PowerToys.StlThumbnailProvider.dll",
+ "modules\\FileExplorerPreview\\PowerToys.StlThumbnailProvider.comhost.dll",
"modules\\FileExplorerPreview\\PowerToys.SvgPreviewHandler.dll",
"modules\\FileExplorerPreview\\PowerToys.SvgPreviewHandler.comhost.dll",
"modules\\FileExplorerPreview\\PowerToys.SvgThumbnailProvider.dll",
@@ -165,6 +167,8 @@
"NLog.dll",
"HtmlAgilityPack.dll",
"Markdig.Signed.dll",
+ "HelixToolkit.dll",
+ "HelixToolkit.Core.Wpf.dll",
"Mages.Core.dll",
"JetBrains.Annotations.dll",
"ICSharpCode.SharpZipLib.dll",
diff --git a/.pipelines/ci/templates/build-powertoys-steps.yml b/.pipelines/ci/templates/build-powertoys-steps.yml
index d016b552aa..c829384bd7 100644
--- a/.pipelines/ci/templates/build-powertoys-steps.yml
+++ b/.pipelines/ci/templates/build-powertoys-steps.yml
@@ -103,6 +103,7 @@ steps:
testAssemblyVer2: |
**\UnitTests-GcodeThumbnailProvider.dll
**\UnitTests-SvgThumbnailProvider.dll
+ **\UnitTests-StlThumbnailProvider.dll
**\UnitTests-PdfThumbnailProvider.dll
**\Settings.UI.UnitTests.dll
**\UnitTests-MarkdownPreviewHandler.dll
diff --git a/PowerToys.sln b/PowerToys.sln
index 2e80669738..2b19df568a 100644
--- a/PowerToys.sln
+++ b/PowerToys.sln
@@ -390,6 +390,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Community.PowerToys.Run.Plu
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MousePointerCrosshair", "src\modules\MouseUtils\MousePointerCrosshair\MousePointerCrosshair.vcxproj", "{EAE14C0E-7A6B-45DA-9080-A7D8C077BA6E}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StlThumbnailProvider", "src\modules\previewpane\StlThumbnailProvider\StlThumbnailProvider.csproj", "{F7C8C0F1-5431-4347-89D0-8E5354F93CF2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-StlThumbnailProvider", "src\modules\previewpane\UnitTests-StlThumbnailProvider\UnitTests-StlThumbnailProvider.csproj", "{F1F6B6B6-9F18-4A17-8B5C-97DF552C53DC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -1053,6 +1057,18 @@ Global
{EAE14C0E-7A6B-45DA-9080-A7D8C077BA6E}.Release|x64.ActiveCfg = Release|x64
{EAE14C0E-7A6B-45DA-9080-A7D8C077BA6E}.Release|x64.Build.0 = Release|x64
{EAE14C0E-7A6B-45DA-9080-A7D8C077BA6E}.Release|x86.ActiveCfg = Release|x64
+ {F7C8C0F1-5431-4347-89D0-8E5354F93CF2}.Debug|x64.ActiveCfg = Debug|x64
+ {F7C8C0F1-5431-4347-89D0-8E5354F93CF2}.Debug|x64.Build.0 = Debug|x64
+ {F7C8C0F1-5431-4347-89D0-8E5354F93CF2}.Debug|x86.ActiveCfg = Debug|x64
+ {F7C8C0F1-5431-4347-89D0-8E5354F93CF2}.Release|x64.ActiveCfg = Release|x64
+ {F7C8C0F1-5431-4347-89D0-8E5354F93CF2}.Release|x64.Build.0 = Release|x64
+ {F7C8C0F1-5431-4347-89D0-8E5354F93CF2}.Release|x86.ActiveCfg = Release|x64
+ {F1F6B6B6-9F18-4A17-8B5C-97DF552C53DC}.Debug|x64.ActiveCfg = Debug|x64
+ {F1F6B6B6-9F18-4A17-8B5C-97DF552C53DC}.Debug|x64.Build.0 = Debug|x64
+ {F1F6B6B6-9F18-4A17-8B5C-97DF552C53DC}.Debug|x86.ActiveCfg = Debug|x64
+ {F1F6B6B6-9F18-4A17-8B5C-97DF552C53DC}.Release|x64.ActiveCfg = Release|x64
+ {F1F6B6B6-9F18-4A17-8B5C-97DF552C53DC}.Release|x64.Build.0 = Release|x64
+ {F1F6B6B6-9F18-4A17-8B5C-97DF552C53DC}.Release|x86.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1181,6 +1197,8 @@ Global
{48A0A19E-A0BE-4256-ACF8-CC3B80291AF9} = {60CD2D4F-C3B9-4897-9821-FCA5098B41CE}
{9F94B303-5E21-4364-9362-64426F8DB932} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{EAE14C0E-7A6B-45DA-9080-A7D8C077BA6E} = {322566EF-20DC-43A6-B9F8-616AF942579A}
+ {F7C8C0F1-5431-4347-89D0-8E5354F93CF2} = {2F305555-C296-497E-AC20-5FA1B237996A}
+ {F1F6B6B6-9F18-4A17-8B5C-97DF552C53DC} = {2F305555-C296-497E-AC20-5FA1B237996A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs
index 138fa8b3be..2fd6321248 100644
--- a/installer/PowerToysSetup/Product.wxs
+++ b/installer/PowerToysSetup/Product.wxs
@@ -877,6 +877,13 @@
+
+
+
+
+
+
+
diff --git a/src/common/utils/modulesRegistry.h b/src/common/utils/modulesRegistry.h
index 158fb0e5d7..85ec539a76 100644
--- a/src/common/utils/modulesRegistry.h
+++ b/src/common/utils/modulesRegistry.h
@@ -106,6 +106,20 @@ inline registry::ChangeSet getGcodeThumbnailHandlerChangeSet(const std::wstring
L".gcode");
}
+inline registry::ChangeSet getStlThumbnailHandlerChangeSet(const std::wstring installationDir, const bool perUser)
+{
+ using namespace registry::shellex;
+ return generatePreviewHandler(PreviewHandlerType::thumbnail,
+ perUser,
+ L"{8BC8AFC2-4E7C-4695-818E-8C1FFDCEA2AF}",
+ get_std_product_version(),
+ (fs::path{ installationDir } / LR"d(modules\FileExplorerPreview\PowerToys.StlThumbnailProvider.comhost.dll)d").wstring(),
+ registry::DOTNET_COMPONENT_CATEGORY_CLSID,
+ L"Microsoft.PowerToys.ThumbnailHandler.Stl.StlThumbnailProvider",
+ L"Stl Thumbnail Provider",
+ L".stl");
+}
+
inline std::vector getAllModulesChangeSets(const std::wstring installationDir)
{
constexpr bool PER_USER = true;
@@ -115,5 +129,6 @@ inline std::vector getAllModulesChangeSets(const std::wstri
getGcodePreviewHandlerChangeSet(installationDir, PER_USER),
getSvgThumbnailHandlerChangeSet(installationDir, PER_USER),
getPdfThumbnailHandlerChangeSet(installationDir, PER_USER),
- getGcodeThumbnailHandlerChangeSet(installationDir, PER_USER) };
+ getGcodeThumbnailHandlerChangeSet(installationDir, PER_USER),
+ getStlThumbnailHandlerChangeSet(installationDir, PER_USER) };
}
\ No newline at end of file
diff --git a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs
new file mode 100644
index 0000000000..ca3d7dcffb
--- /dev/null
+++ b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs
@@ -0,0 +1,145 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.Media3D;
+using Common.ComInterlop;
+using Common.Utilities;
+using HelixToolkit.Wpf;
+using Bitmap = System.Drawing.Bitmap;
+
+namespace Microsoft.PowerToys.ThumbnailHandler.Stl
+{
+ ///
+ /// Stl Thumbnail Provider.
+ ///
+ [Guid("8BC8AFC2-4E7C-4695-818E-8C1FFDCEA2AF")]
+ [ClassInterface(ClassInterfaceType.None)]
+ [ComVisible(true)]
+ public class StlThumbnailProvider : IInitializeWithStream, IThumbnailProvider
+ {
+ ///
+ /// Gets the stream object to access file.
+ ///
+ public IStream Stream { get; private set; }
+
+ ///
+ /// The maximum dimension (width or height) thumbnail we will generate.
+ ///
+ private const uint MaxThumbnailSize = 10000;
+
+ ///
+ /// Loads the Stl model into a Viewport3D and renders a bitmap of it.
+ ///
+ /// The Stream instance for the Stl content.
+ /// The maximum thumbnail size, in pixels.
+ /// A thumbnail rendered from the Stl model.
+ public static Bitmap GetThumbnail(Stream stream, uint cx)
+ {
+ if (cx > MaxThumbnailSize || stream == null || stream.Length == 0)
+ {
+ return null;
+ }
+
+ Bitmap thumbnail = null;
+
+ var stlReader = new StLReader
+ {
+ DefaultMaterial = new DiffuseMaterial(new SolidColorBrush(Color.FromRgb(255, 201, 36))),
+ };
+
+ var model = stlReader.Read(stream);
+
+ if (model.Bounds == Rect3D.Empty)
+ {
+ return null;
+ }
+
+ var viewport = new System.Windows.Controls.Viewport3D();
+
+ viewport.Measure(new Size(cx, cx));
+ viewport.Arrange(new Rect(0, 0, cx, cx));
+
+ var modelVisual = new ModelVisual3D()
+ {
+ Transform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 0, 1), 180)),
+ };
+ viewport.Children.Add(modelVisual);
+ viewport.Children.Add(new DefaultLights());
+
+ var perspectiveCamera = new PerspectiveCamera
+ {
+ Position = new Point3D(1, 2, 1),
+ LookDirection = new Vector3D(-1, -2, -1),
+ UpDirection = new Vector3D(0, 0, 1),
+ FieldOfView = 20,
+ NearPlaneDistance = 0.1,
+ FarPlaneDistance = double.PositiveInfinity,
+ };
+ viewport.Camera = perspectiveCamera;
+
+ modelVisual.Content = model;
+
+ perspectiveCamera.ZoomExtents(viewport);
+
+ var bitmapExporter = new BitmapExporter
+ {
+ Background = new SolidColorBrush(Colors.Transparent),
+ OversamplingMultiplier = 1,
+ };
+
+ var bitmapStream = new MemoryStream();
+
+ bitmapExporter.Export(viewport, bitmapStream);
+
+ bitmapStream.Position = 0;
+
+ thumbnail = new Bitmap(bitmapStream);
+
+ return thumbnail;
+ }
+
+ ///
+ public void Initialize(IStream pstream, uint grfMode)
+ {
+ // Ignore the grfMode always use read mode to access the file.
+ this.Stream = pstream;
+ }
+
+ ///
+ public void GetThumbnail(uint cx, out IntPtr phbmp, out WTS_ALPHATYPE pdwAlpha)
+ {
+ phbmp = IntPtr.Zero;
+ pdwAlpha = WTS_ALPHATYPE.WTSAT_UNKNOWN;
+
+ if (cx == 0 || cx > MaxThumbnailSize)
+ {
+ return;
+ }
+
+ using (var stream = new ReadonlyStream(this.Stream as IStream))
+ {
+ using (var memStream = new MemoryStream())
+ {
+ stream.CopyTo(memStream);
+
+ memStream.Position = 0;
+
+ using (Bitmap thumbnail = GetThumbnail(memStream, cx))
+ {
+ if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0)
+ {
+ phbmp = thumbnail.GetHbitmap(System.Drawing.Color.Transparent);
+ pdwAlpha = WTS_ALPHATYPE.WTSAT_ARGB;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj
new file mode 100644
index 0000000000..0f82a9d0de
--- /dev/null
+++ b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj
@@ -0,0 +1,65 @@
+
+
+ x64
+ true
+ {F7C8C0F1-5431-4347-89D0-8E5354F93CF2}
+ Microsoft.PowerToys.ThumbnailHandler.Stl
+ PowerToys.StlThumbnailProvider
+ PowerToys.StlThumbnailProvider
+ PowerToys StlPreviewHandler
+ Microsoft Corporation
+ Copyright (C) 2020 Microsoft Corporation
+ PowerToys
+ net5.0-windows
+ true
+ true
+ Microsoft Corporation
+ PowerToys
+ en-US
+ PowerToys StlPreviewHandler
+ Copyright (C) 2020 Microsoft Corporation
+ $(SolutionDir)$(Platform)\$(Configuration)\modules\FileExplorerPreview\
+ true
+ false
+ false
+ true
+ true
+
+
+
+
+
+
+
+
+ all
+
+
+ all
+
+
+ all
+
+
+ all
+
+
+ all
+
+
+ all
+
+
+
+
+
+
+
+
+
+
+
+ StyleCop.json
+
+
+
\ No newline at end of file
diff --git a/src/modules/previewpane/UnitTests-StlThumbnailProvider/HelperFiles/sample.stl b/src/modules/previewpane/UnitTests-StlThumbnailProvider/HelperFiles/sample.stl
new file mode 100644
index 0000000000..1cfed5fe59
Binary files /dev/null and b/src/modules/previewpane/UnitTests-StlThumbnailProvider/HelperFiles/sample.stl differ
diff --git a/src/modules/previewpane/UnitTests-StlThumbnailProvider/StlThumbnailProviderTests.cs b/src/modules/previewpane/UnitTests-StlThumbnailProvider/StlThumbnailProviderTests.cs
new file mode 100644
index 0000000000..1557943494
--- /dev/null
+++ b/src/modules/previewpane/UnitTests-StlThumbnailProvider/StlThumbnailProviderTests.cs
@@ -0,0 +1,111 @@
+// 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.Drawing;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+using Common.ComInterlop;
+using Microsoft.PowerToys.STATestExtension;
+using Microsoft.PowerToys.ThumbnailHandler.Stl;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+
+namespace StlThumbnailProviderUnitTests
+{
+ [STATestClass]
+ public class StlThumbnailProviderTests
+ {
+ [TestMethod]
+ public void GetThumbnailValidStreamStl()
+ {
+ // Act
+ var file = File.ReadAllBytes("HelperFiles/sample.stl");
+
+ StlThumbnailProvider provider = new StlThumbnailProvider();
+
+ provider.Initialize(GetMockStream(file), 0);
+
+ provider.GetThumbnail(256, out IntPtr bitmap, out WTS_ALPHATYPE alphaType);
+
+ Assert.IsTrue(bitmap != IntPtr.Zero);
+ Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_ARGB);
+ }
+
+ [TestMethod]
+ public void GetThumbnailInValidSizeStl()
+ {
+ // Act
+ var file = File.ReadAllBytes("HelperFiles/sample.stl");
+
+ StlThumbnailProvider provider = new StlThumbnailProvider();
+
+ provider.Initialize(GetMockStream(file), 0);
+
+ provider.GetThumbnail(0, out IntPtr bitmap, out WTS_ALPHATYPE alphaType);
+
+ Assert.IsTrue(bitmap == IntPtr.Zero);
+ Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_UNKNOWN);
+ }
+
+ [TestMethod]
+ public void GetThumbnailToBigStl()
+ {
+ // Act
+ var file = File.ReadAllBytes("HelperFiles/sample.stl");
+
+ StlThumbnailProvider provider = new StlThumbnailProvider();
+
+ provider.Initialize(GetMockStream(file), 0);
+
+ provider.GetThumbnail(10001, out IntPtr bitmap, out WTS_ALPHATYPE alphaType);
+
+ Assert.IsTrue(bitmap == IntPtr.Zero);
+ Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_UNKNOWN);
+ }
+
+ [TestMethod]
+ public void CheckNoStlEmptyStreamShouldReturnNullBitmap()
+ {
+ using (var stream = new MemoryStream())
+ {
+ Bitmap thumbnail = StlThumbnailProvider.GetThumbnail(stream, 256);
+ Assert.IsTrue(thumbnail == null);
+ }
+ }
+
+ [TestMethod]
+ public void CheckNoStlNullStreamShouldReturnNullBitmap()
+ {
+ Bitmap thumbnail = StlThumbnailProvider.GetThumbnail(null, 256);
+ Assert.IsTrue(thumbnail == null);
+ }
+
+ private static IStream GetMockStream(byte[] sourceArray)
+ {
+ var streamMock = new Mock();
+ int bytesRead = 0;
+
+ streamMock
+ .Setup(x => x.Read(It.IsAny(), It.IsAny(), It.IsAny()))
+ .Callback((buffer, countToRead, bytesReadPtr) =>
+ {
+ int actualCountToRead = Math.Min(sourceArray.Length - bytesRead, countToRead);
+ if (actualCountToRead > 0)
+ {
+ Array.Copy(sourceArray, bytesRead, buffer, 0, actualCountToRead);
+ Marshal.WriteInt32(bytesReadPtr, actualCountToRead);
+ bytesRead += actualCountToRead;
+ }
+ else
+ {
+ Marshal.WriteInt32(bytesReadPtr, 0);
+ }
+ });
+
+ return streamMock.Object;
+ }
+ }
+}
diff --git a/src/modules/previewpane/UnitTests-StlThumbnailProvider/UnitTests-StlThumbnailProvider.csproj b/src/modules/previewpane/UnitTests-StlThumbnailProvider/UnitTests-StlThumbnailProvider.csproj
new file mode 100644
index 0000000000..d4b77ddfbd
--- /dev/null
+++ b/src/modules/previewpane/UnitTests-StlThumbnailProvider/UnitTests-StlThumbnailProvider.csproj
@@ -0,0 +1,78 @@
+
+
+ x64
+ UnitTests-StlThumbnailProvider
+ PowerToys UnitTests-StlThumbnailProvider
+ Microsoft Corporation
+ Copyright (C) 2020 Microsoft Corporation
+ PowerToys
+ UnitTests-StlThumbnailProvider
+ Microsoft Corporation
+ PowerToys
+ en-US
+ PowerToys UnitTests-StlThumbnailProvider
+ Copyright (C) 2021 Microsoft Corporation
+
+
+
+ {F1F6B6B6-9F18-4A17-8B5C-97DF552C53DC}
+ StlThumbnailProviderUnitTests
+ net5.0-windows10.0.18362.0
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+
+
+
+
+
+
+
+
+
+
+
+ all
+
+
+ all
+
+
+ all
+
+
+ all
+
+
+ all
+
+
+
+
+
+ all
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ StyleCop.json
+
+
+
+
+ Always
+
+
+
\ No newline at end of file
diff --git a/src/modules/previewpane/powerpreview/CLSID.h b/src/modules/previewpane/powerpreview/CLSID.h
index d546fa4ae1..931c6469d4 100644
--- a/src/modules/previewpane/powerpreview/CLSID.h
+++ b/src/modules/previewpane/powerpreview/CLSID.h
@@ -39,6 +39,9 @@ const CLSID CLSID_GcodePreviewHandler = { 0xec52dea8, 0x7c9f, 0x4130, { 0xa7, 0x
// BFEE99B4-B74D-4348-BCA5-E757029647FF
const GUID CLSID_GcodeThumbnailProvider = { 0xbfee99b4, 0xb74d, 0x4348, { 0xbc, 0xa5, 0xe7, 0x57, 0x02, 0x96, 0x47, 0xff } };
+
+// 8BC8AFC2-4E7C-4695-818E-8C1FFDCEA2AF
+const GUID CLSID_StlThumbnailProvider = { 0x8bc8afc2, 0x4e7c, 0x4695, { 0x81, 0x8e, 0x8c, 0x1f, 0xfd, 0xce, 0xa2, 0xaf } };
// Pairs of NativeClsid vs ManagedClsid used for preview handlers.
const std::vector> NativeToManagedClsid({
diff --git a/src/modules/previewpane/powerpreview/Resources.resx b/src/modules/previewpane/powerpreview/Resources.resx
index 333f8dcd1d..1efc7aa9af 100644
--- a/src/modules/previewpane/powerpreview/Resources.resx
+++ b/src/modules/previewpane/powerpreview/Resources.resx
@@ -186,4 +186,7 @@
G-code Previewer
+
+ Stl Thumbnail Provider
+
\ No newline at end of file
diff --git a/src/modules/previewpane/powerpreview/powerpreview.cpp b/src/modules/previewpane/powerpreview/powerpreview.cpp
index a0bbd6f43c..3c1a0ea981 100644
--- a/src/modules/previewpane/powerpreview/powerpreview.cpp
+++ b/src/modules/previewpane/powerpreview/powerpreview.cpp
@@ -54,6 +54,10 @@ PowerPreviewModule::PowerPreviewModule() :
.settingDescription = GET_RESOURCE_STRING(IDS_GCODE_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION),
.registryChanges = getGcodeThumbnailHandlerChangeSet(installationDir, installPerUser) });
+ m_fileExplorerModules.push_back({ .settingName = L"stl-thumbnail-toggle-setting",
+ .settingDescription = GET_RESOURCE_STRING(IDS_STL_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION),
+ .registryChanges = getStlThumbnailHandlerChangeSet(installationDir, installPerUser) });
+
try
{
PowerToysSettings::PowerToyValues settings =
diff --git a/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs b/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs
index 0d35c229fd..0fffa808eb 100644
--- a/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs
+++ b/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs
@@ -131,6 +131,23 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
+ private bool enableStlThumbnail = true;
+
+ [JsonPropertyName("stl-thumbnail-toggle-setting")]
+ [JsonConverter(typeof(BoolPropertyJsonConverter))]
+ public bool EnableStlThumbnail
+ {
+ get => enableStlThumbnail;
+ set
+ {
+ if (value != enableStlThumbnail)
+ {
+ LogTelemetryEvent(value);
+ enableStlThumbnail = value;
+ }
+ }
+ }
+
public PowerPreviewProperties()
{
}
diff --git a/src/settings-ui/Settings.UI.Library/ViewModels/PowerPreviewViewModel.cs b/src/settings-ui/Settings.UI.Library/ViewModels/PowerPreviewViewModel.cs
index 238705368a..6b0dfd1437 100644
--- a/src/settings-ui/Settings.UI.Library/ViewModels/PowerPreviewViewModel.cs
+++ b/src/settings-ui/Settings.UI.Library/ViewModels/PowerPreviewViewModel.cs
@@ -53,6 +53,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
_gcodeRenderIsEnabled = Settings.Properties.EnableGcodePreview;
_pdfThumbnailIsEnabled = Settings.Properties.EnablePdfThumbnail;
_gcodeThumbnailIsEnabled = Settings.Properties.EnableGcodeThumbnail;
+ _stlThumbnailIsEnabled = Settings.Properties.EnableStlThumbnail;
}
private bool _svgRenderIsEnabled;
@@ -62,6 +63,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
private bool _svgThumbnailIsEnabled;
private bool _pdfThumbnailIsEnabled;
private bool _gcodeThumbnailIsEnabled;
+ private bool _stlThumbnailIsEnabled;
public bool SVGRenderIsEnabled
{
@@ -189,6 +191,24 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
}
}
+ public bool STLThumbnailIsEnabled
+ {
+ get
+ {
+ return _stlThumbnailIsEnabled;
+ }
+
+ set
+ {
+ if (value != _stlThumbnailIsEnabled)
+ {
+ _stlThumbnailIsEnabled = value;
+ Settings.Properties.EnableStlThumbnail = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
+
public string GetSettingsSubPath()
{
return _settingsConfigFileFolder + "\\" + ModuleName;
diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs
index 03d916492f..cc53a37011 100644
--- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs
+++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs
@@ -63,6 +63,7 @@ namespace ViewModelTests
Assert.AreEqual(originalSettings.Properties.EnableSvgThumbnail, viewModel.SVGThumbnailIsEnabled);
Assert.AreEqual(originalSettings.Properties.EnablePdfThumbnail, viewModel.PDFThumbnailIsEnabled);
Assert.AreEqual(originalSettings.Properties.EnableGcodeThumbnail, viewModel.GCODEThumbnailIsEnabled);
+ Assert.AreEqual(originalSettings.Properties.EnableStlThumbnail, viewModel.STLThumbnailIsEnabled);
// Verify that the stub file was used
var expectedCallCount = 2; // once via the view model, and once by the test (GetSettings)
@@ -141,6 +142,24 @@ namespace ViewModelTests
viewModel.GCODEThumbnailIsEnabled = true;
}
+ [TestMethod]
+ public void STLThumbnailIsEnabledShouldPrevHandlerWhenSuccessful()
+ {
+ // Assert
+ Func sendMockIPCConfigMSG = msg =>
+ {
+ SndModuleSettings snd = JsonSerializer.Deserialize>(msg);
+ Assert.IsTrue(snd.PowertoysSetting.FileExplorerPreviewSettings.Properties.EnableStlThumbnail);
+ return 0;
+ };
+
+ // arrange
+ PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(mockPowerPreviewSettingsUtils.Object), SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), sendMockIPCConfigMSG, PowerPreviewSettings.ModuleName);
+
+ // act
+ viewModel.STLThumbnailIsEnabled = true;
+ }
+
[TestMethod]
public void MDRenderIsEnabledShouldPrevHandlerWhenSuccessful()
{
diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw
index 8295ecc966..4208e8601a 100644
--- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw
+++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw
@@ -632,6 +632,10 @@
Enable SVG (.svg) thumbnails
Do you want this feature on / off
+
+ Enable STL (.stl) thumbnails
+ Do you want this feature on / off
+
Enable PDF (.pdf) thumbnails
Do you want this feature on / off
diff --git a/src/settings-ui/Settings.UI/Views/PowerPreviewPage.xaml b/src/settings-ui/Settings.UI/Views/PowerPreviewPage.xaml
index 488d41449b..681492a926 100644
--- a/src/settings-ui/Settings.UI/Views/PowerPreviewPage.xaml
+++ b/src/settings-ui/Settings.UI/Views/PowerPreviewPage.xaml
@@ -77,6 +77,13 @@
x:Uid="ToggleSwitch"/>
+
+
+
+
+
+
diff --git a/tools/BugReportTool/BugReportTool/RegistryUtils.cpp b/tools/BugReportTool/BugReportTool/RegistryUtils.cpp
index e725d3e152..6025336e1e 100644
--- a/tools/BugReportTool/BugReportTool/RegistryUtils.cpp
+++ b/tools/BugReportTool/BugReportTool/RegistryUtils.cpp
@@ -19,6 +19,7 @@ namespace
{ HKEY_CLASSES_ROOT, L"CLSID\\{ec52dea8-7c9f-4130-a77b-1737d0418507}" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{BCC13D15-9720-4CC4-8371-EA74A274741E}" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{BFEE99B4-B74D-4348-BCA5-E757029647FF}" },
+ { HKEY_CLASSES_ROOT, L"CLSID\\{8BC8AFC2-4E7C-4695-818E-8C1FFDCEA2AF}" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\\InprocServer32" },
{ HKEY_CLASSES_ROOT, L"CLSID\\{0440049F-D1DC-4E46-B27B-98393D79486B}" },
{ HKEY_CLASSES_ROOT, L"AllFileSystemObjects\\ShellEx\\ContextMenuHandlers\\PowerRenameExt" },
@@ -29,7 +30,8 @@ namespace
{ HKEY_CLASSES_ROOT, L".pdf\\shellex\\{8895b1c6-b41f-4c1c-a562-0d564250836f}" },
{ HKEY_CLASSES_ROOT, L".pdf\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}" },
{ HKEY_CLASSES_ROOT, L".gcode\\shellex\\{8895b1c6-b41f-4c1c-a562-0d564250836f}" },
- { HKEY_CLASSES_ROOT, L".gcode\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}" }
+ { HKEY_CLASSES_ROOT, L".gcode\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}" },
+ { HKEY_CLASSES_ROOT, L".stl\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}" }
};
vector> registryValues = {