mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
merge main
This commit is contained in:
4
.github/actions/spell-check/allow/code.txt
vendored
4
.github/actions/spell-check/allow/code.txt
vendored
@@ -335,3 +335,7 @@ azp
|
|||||||
feedbackhub
|
feedbackhub
|
||||||
needinfo
|
needinfo
|
||||||
reportbug
|
reportbug
|
||||||
|
|
||||||
|
#ffmpeg
|
||||||
|
crf
|
||||||
|
nostdin
|
||||||
|
|||||||
5
.github/actions/spell-check/expect.txt
vendored
5
.github/actions/spell-check/expect.txt
vendored
@@ -144,6 +144,8 @@ BLENDFUNCTION
|
|||||||
blittable
|
blittable
|
||||||
Blockquotes
|
Blockquotes
|
||||||
blt
|
blt
|
||||||
|
bluelightreduction
|
||||||
|
bluelightreductionstate
|
||||||
BLURBEHIND
|
BLURBEHIND
|
||||||
BLURREGION
|
BLURREGION
|
||||||
bmi
|
bmi
|
||||||
@@ -1116,6 +1118,7 @@ NEWPLUSSHELLEXTENSIONWIN
|
|||||||
newrow
|
newrow
|
||||||
nicksnettravels
|
nicksnettravels
|
||||||
NIF
|
NIF
|
||||||
|
nightlight
|
||||||
NLog
|
NLog
|
||||||
NLSTEXT
|
NLSTEXT
|
||||||
NMAKE
|
NMAKE
|
||||||
@@ -1852,6 +1855,8 @@ uitests
|
|||||||
UITo
|
UITo
|
||||||
ULONGLONG
|
ULONGLONG
|
||||||
ums
|
ums
|
||||||
|
UMax
|
||||||
|
UMin
|
||||||
uncompilable
|
uncompilable
|
||||||
UNCPRIORITY
|
UNCPRIORITY
|
||||||
UNDNAME
|
UNDNAME
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ $versionExceptions = @(
|
|||||||
"WyHash.dll",
|
"WyHash.dll",
|
||||||
"Microsoft.Recognizers.Text.DataTypes.TimexExpression.dll",
|
"Microsoft.Recognizers.Text.DataTypes.TimexExpression.dll",
|
||||||
"ObjectModelCsProjection.dll",
|
"ObjectModelCsProjection.dll",
|
||||||
"RendererCsProjection.dll") -join '|';
|
"RendererCsProjection.dll",
|
||||||
|
"Microsoft.ML.OnnxRuntime.dll") -join '|';
|
||||||
$nullVersionExceptions = @(
|
$nullVersionExceptions = @(
|
||||||
"SkiaSharp.Views.WinUI.Native.dll",
|
"SkiaSharp.Views.WinUI.Native.dll",
|
||||||
"libSkiaSharp.dll",
|
"libSkiaSharp.dll",
|
||||||
|
|||||||
@@ -42,11 +42,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<!-- Make angle-bracket includes external and turn off code analysis for them -->
|
|
||||||
<TreatAngleIncludeAsExternal>true</TreatAngleIncludeAsExternal>
|
|
||||||
<ExternalWarningLevel>TurnOffAllWarnings</ExternalWarningLevel>
|
|
||||||
<DisableAnalyzeExternal>true</DisableAnalyzeExternal>
|
|
||||||
|
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Use</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Use</PrecompiledHeader>
|
||||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
@@ -116,11 +111,13 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Debug/Release props -->
|
<!-- Debug/Release props -->
|
||||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)'=='Debug'"
|
||||||
|
Label="Configuration">
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<LinkIncremental>true</LinkIncremental>
|
<LinkIncremental>true</LinkIncremental>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)'=='Release'"
|
||||||
|
Label="Configuration">
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
<LinkIncremental>false</LinkIncremental>
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
<PackageVersion Include="AdaptiveCards.ObjectModel.WinUI3" Version="2.0.0-beta" />
|
<PackageVersion Include="AdaptiveCards.ObjectModel.WinUI3" Version="2.0.0-beta" />
|
||||||
<PackageVersion Include="AdaptiveCards.Rendering.WinUI3" Version="2.1.0-beta" />
|
<PackageVersion Include="AdaptiveCards.Rendering.WinUI3" Version="2.1.0-beta" />
|
||||||
<PackageVersion Include="AdaptiveCards.Templating" Version="2.0.5" />
|
<PackageVersion Include="AdaptiveCards.Templating" Version="2.0.5" />
|
||||||
<PackageVersion Include="boost" Version="1.87.0" TargetFramework="native" />
|
|
||||||
<PackageVersion Include="boost_regex-vc143" Version="1.87.0" TargetFramework="native" />
|
|
||||||
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.OpacityMaskView" Version="0.1.251101-build.2372" />
|
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.OpacityMaskView" Version="0.1.251101-build.2372" />
|
||||||
<PackageVersion Include="Microsoft.Bot.AdaptiveExpressions.Core" Version="4.23.0" />
|
<PackageVersion Include="Microsoft.Bot.AdaptiveExpressions.Core" Version="4.23.0" />
|
||||||
<PackageVersion Include="Appium.WebDriver" Version="4.4.5" />
|
<PackageVersion Include="Appium.WebDriver" Version="4.4.5" />
|
||||||
@@ -72,12 +70,10 @@
|
|||||||
This is present due to a bug in CsWinRT where WPF projects cause the analyzer to fail.
|
This is present due to a bug in CsWinRT where WPF projects cause the analyzer to fail.
|
||||||
-->
|
-->
|
||||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
||||||
<PackageVersion Include="Microsoft.Windows.ImplementationLibrary" Version="1.0.231216.1"/>
|
|
||||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6901" />
|
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6901" />
|
||||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.250907003" />
|
||||||
<PackageVersion Include="Microsoft.WindowsAppSDK.Foundation" Version="1.8.251104000" />
|
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="1.8.37" />
|
||||||
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="1.8.39" />
|
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="1.8.250907003" />
|
||||||
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="1.8.251106002" />
|
|
||||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
||||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
|
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
|
||||||
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />
|
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />
|
||||||
@@ -117,7 +113,6 @@
|
|||||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.13" />
|
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.13" />
|
||||||
<PackageVersion Include="System.Management" Version="9.0.10" />
|
<PackageVersion Include="System.Management" Version="9.0.10" />
|
||||||
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
||||||
<PackageVersion Include="System.Numerics.Tensors" Version="9.0.11" />
|
|
||||||
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
|
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
|
||||||
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
||||||
<PackageVersion Include="System.Runtime.Caching" Version="9.0.10" />
|
<PackageVersion Include="System.Runtime.Caching" Version="9.0.10" />
|
||||||
|
|||||||
399
src/common/UITestAutomation/ScreenRecording.cs
Normal file
399
src/common/UITestAutomation/ScreenRecording.cs
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
// 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.Diagnostics;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.PowerToys.UITest
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides methods for recording the screen during UI tests.
|
||||||
|
/// Requires FFmpeg to be installed and available in PATH.
|
||||||
|
/// </summary>
|
||||||
|
internal class ScreenRecording : IDisposable
|
||||||
|
{
|
||||||
|
private readonly string outputDirectory;
|
||||||
|
private readonly string framesDirectory;
|
||||||
|
private readonly string outputFilePath;
|
||||||
|
private readonly List<string> capturedFrames;
|
||||||
|
private readonly SemaphoreSlim recordingLock = new(1, 1);
|
||||||
|
private readonly Stopwatch recordingStopwatch = new();
|
||||||
|
private readonly string? ffmpegPath;
|
||||||
|
private CancellationTokenSource? recordingCancellation;
|
||||||
|
private Task? recordingTask;
|
||||||
|
private bool isRecording;
|
||||||
|
private int frameCount;
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern IntPtr GetDC(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("gdi32.dll")]
|
||||||
|
private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool GetCursorInfo(out ScreenCapture.CURSORINFO pci);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool DrawIconEx(IntPtr hdc, int x, int y, IntPtr hIcon, int cx, int cy, int istepIfAniCur, IntPtr hbrFlickerFreeDraw, int diFlags);
|
||||||
|
|
||||||
|
private const int CURSORSHOWING = 0x00000001;
|
||||||
|
private const int DESKTOPHORZRES = 118;
|
||||||
|
private const int DESKTOPVERTRES = 117;
|
||||||
|
private const int DINORMAL = 0x0003;
|
||||||
|
private const int TargetFps = 15; // 15 FPS for good balance of quality and size
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ScreenRecording"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="outputDirectory">Directory where the recording will be saved.</param>
|
||||||
|
public ScreenRecording(string outputDirectory)
|
||||||
|
{
|
||||||
|
this.outputDirectory = outputDirectory;
|
||||||
|
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||||
|
framesDirectory = Path.Combine(outputDirectory, $"frames_{timestamp}");
|
||||||
|
outputFilePath = Path.Combine(outputDirectory, $"recording_{timestamp}.mp4");
|
||||||
|
capturedFrames = new List<string>();
|
||||||
|
frameCount = 0;
|
||||||
|
|
||||||
|
// Check if FFmpeg is available
|
||||||
|
ffmpegPath = FindFfmpeg();
|
||||||
|
if (ffmpegPath == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("FFmpeg not found. Screen recording will be disabled.");
|
||||||
|
Console.WriteLine("To enable video recording, install FFmpeg: https://ffmpeg.org/download.html");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether screen recording is available (FFmpeg found).
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAvailable => ffmpegPath != null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts recording the screen.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A task representing the asynchronous operation.</returns>
|
||||||
|
public async Task StartRecordingAsync()
|
||||||
|
{
|
||||||
|
await recordingLock.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (isRecording || !IsAvailable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create frames directory
|
||||||
|
Directory.CreateDirectory(framesDirectory);
|
||||||
|
|
||||||
|
recordingCancellation = new CancellationTokenSource();
|
||||||
|
isRecording = true;
|
||||||
|
recordingStopwatch.Start();
|
||||||
|
|
||||||
|
// Start the recording task
|
||||||
|
recordingTask = Task.Run(() => RecordFrames(recordingCancellation.Token));
|
||||||
|
|
||||||
|
Console.WriteLine($"Started screen recording at {TargetFps} FPS");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed to start recording: {ex.Message}");
|
||||||
|
isRecording = false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
recordingLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops recording and encodes video.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A task representing the asynchronous operation.</returns>
|
||||||
|
public async Task StopRecordingAsync()
|
||||||
|
{
|
||||||
|
await recordingLock.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!isRecording || recordingCancellation == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal cancellation
|
||||||
|
recordingCancellation.Cancel();
|
||||||
|
|
||||||
|
// Wait for recording task to complete
|
||||||
|
if (recordingTask != null)
|
||||||
|
{
|
||||||
|
await recordingTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
recordingStopwatch.Stop();
|
||||||
|
isRecording = false;
|
||||||
|
|
||||||
|
double duration = recordingStopwatch.Elapsed.TotalSeconds;
|
||||||
|
Console.WriteLine($"Recording stopped. Captured {capturedFrames.Count} frames in {duration:F2} seconds");
|
||||||
|
|
||||||
|
// Encode to video
|
||||||
|
await EncodeToVideoAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error stopping recording: {ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Cleanup();
|
||||||
|
recordingLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Records frames from the screen.
|
||||||
|
/// </summary>
|
||||||
|
private void RecordFrames(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int frameInterval = 1000 / TargetFps;
|
||||||
|
var frameTimer = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
var frameStart = frameTimer.ElapsedMilliseconds;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CaptureFrame();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error capturing frame: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep for remaining time to maintain target FPS
|
||||||
|
var frameTime = frameTimer.ElapsedMilliseconds - frameStart;
|
||||||
|
var sleepTime = Math.Max(0, frameInterval - (int)frameTime);
|
||||||
|
|
||||||
|
if (sleepTime > 0)
|
||||||
|
{
|
||||||
|
Thread.Sleep(sleepTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// Expected when stopping
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error during recording: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Captures a single frame.
|
||||||
|
/// </summary>
|
||||||
|
private void CaptureFrame()
|
||||||
|
{
|
||||||
|
IntPtr hdc = GetDC(IntPtr.Zero);
|
||||||
|
int screenWidth = GetDeviceCaps(hdc, DESKTOPHORZRES);
|
||||||
|
int screenHeight = GetDeviceCaps(hdc, DESKTOPVERTRES);
|
||||||
|
ReleaseDC(IntPtr.Zero, hdc);
|
||||||
|
|
||||||
|
Rectangle bounds = new Rectangle(0, 0, screenWidth, screenHeight);
|
||||||
|
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format24bppRgb))
|
||||||
|
{
|
||||||
|
using (Graphics g = Graphics.FromImage(bitmap))
|
||||||
|
{
|
||||||
|
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
|
||||||
|
|
||||||
|
ScreenCapture.CURSORINFO cursorInfo;
|
||||||
|
cursorInfo.CbSize = Marshal.SizeOf<ScreenCapture.CURSORINFO>();
|
||||||
|
if (GetCursorInfo(out cursorInfo) && cursorInfo.Flags == CURSORSHOWING)
|
||||||
|
{
|
||||||
|
IntPtr hdcDest = g.GetHdc();
|
||||||
|
DrawIconEx(hdcDest, cursorInfo.PTScreenPos.X, cursorInfo.PTScreenPos.Y, cursorInfo.HCursor, 0, 0, 0, IntPtr.Zero, DINORMAL);
|
||||||
|
g.ReleaseHdc(hdcDest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string framePath = Path.Combine(framesDirectory, $"frame_{frameCount:D6}.jpg");
|
||||||
|
bitmap.Save(framePath, ImageFormat.Jpeg);
|
||||||
|
capturedFrames.Add(framePath);
|
||||||
|
frameCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes captured frames to video using ffmpeg.
|
||||||
|
/// </summary>
|
||||||
|
private async Task EncodeToVideoAsync()
|
||||||
|
{
|
||||||
|
if (capturedFrames.Count == 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("No frames captured");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Build ffmpeg command with proper non-interactive flags
|
||||||
|
string inputPattern = Path.Combine(framesDirectory, "frame_%06d.jpg");
|
||||||
|
|
||||||
|
// -y: overwrite without asking
|
||||||
|
// -nostdin: disable interaction
|
||||||
|
// -loglevel error: only show errors
|
||||||
|
// -stats: show encoding progress
|
||||||
|
string args = $"-y -nostdin -loglevel error -stats -framerate {TargetFps} -i \"{inputPattern}\" -c:v libx264 -pix_fmt yuv420p -crf 23 \"{outputFilePath}\"";
|
||||||
|
|
||||||
|
Console.WriteLine($"Encoding {capturedFrames.Count} frames to video...");
|
||||||
|
|
||||||
|
var startInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = ffmpegPath!,
|
||||||
|
Arguments = args,
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
RedirectStandardInput = true, // Important: redirect stdin to prevent hanging
|
||||||
|
CreateNoWindow = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
using var process = Process.Start(startInfo);
|
||||||
|
if (process != null)
|
||||||
|
{
|
||||||
|
// Close stdin immediately to ensure FFmpeg doesn't wait for input
|
||||||
|
process.StandardInput.Close();
|
||||||
|
|
||||||
|
// Read output streams asynchronously to prevent deadlock
|
||||||
|
var outputTask = process.StandardOutput.ReadToEndAsync();
|
||||||
|
var errorTask = process.StandardError.ReadToEndAsync();
|
||||||
|
|
||||||
|
// Wait for process to exit
|
||||||
|
await process.WaitForExitAsync();
|
||||||
|
|
||||||
|
// Get the output
|
||||||
|
string stdout = await outputTask;
|
||||||
|
string stderr = await errorTask;
|
||||||
|
|
||||||
|
if (process.ExitCode == 0 && File.Exists(outputFilePath))
|
||||||
|
{
|
||||||
|
var fileInfo = new FileInfo(outputFilePath);
|
||||||
|
Console.WriteLine($"Video created: {outputFilePath} ({fileInfo.Length / 1024 / 1024:F1} MB)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"FFmpeg encoding failed with exit code {process.ExitCode}");
|
||||||
|
if (!string.IsNullOrWhiteSpace(stderr))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"FFmpeg error: {stderr}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error encoding video: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds ffmpeg executable.
|
||||||
|
/// </summary>
|
||||||
|
private static string? FindFfmpeg()
|
||||||
|
{
|
||||||
|
// Check if ffmpeg is in PATH
|
||||||
|
var pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(Path.PathSeparator) ?? Array.Empty<string>();
|
||||||
|
|
||||||
|
foreach (var dir in pathDirs)
|
||||||
|
{
|
||||||
|
var ffmpegPath = Path.Combine(dir, "ffmpeg.exe");
|
||||||
|
if (File.Exists(ffmpegPath))
|
||||||
|
{
|
||||||
|
return ffmpegPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check common installation locations
|
||||||
|
var commonPaths = new[]
|
||||||
|
{
|
||||||
|
@"C:\.tools\ffmpeg\bin\ffmpeg.exe",
|
||||||
|
@"C:\ffmpeg\bin\ffmpeg.exe",
|
||||||
|
@"C:\Program Files\ffmpeg\bin\ffmpeg.exe",
|
||||||
|
@"C:\Program Files (x86)\ffmpeg\bin\ffmpeg.exe",
|
||||||
|
@$"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\Microsoft\WinGet\Links\ffmpeg.exe",
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var path in commonPaths)
|
||||||
|
{
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path to the recorded video file.
|
||||||
|
/// </summary>
|
||||||
|
public string OutputFilePath => outputFilePath;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the directory containing recordings.
|
||||||
|
/// </summary>
|
||||||
|
public string OutputDirectory => outputDirectory;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cleans up resources.
|
||||||
|
/// </summary>
|
||||||
|
private void Cleanup()
|
||||||
|
{
|
||||||
|
recordingCancellation?.Dispose();
|
||||||
|
recordingCancellation = null;
|
||||||
|
recordingTask = null;
|
||||||
|
|
||||||
|
// Clean up frames directory if it exists
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Directory.Exists(framesDirectory))
|
||||||
|
{
|
||||||
|
Directory.Delete(framesDirectory, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed to cleanup frames directory: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes resources.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (isRecording)
|
||||||
|
{
|
||||||
|
StopRecordingAsync().GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
Cleanup();
|
||||||
|
recordingLock.Dispose();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -130,9 +130,13 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="appPath">The path to the application executable.</param>
|
/// <param name="appPath">The path to the application executable.</param>
|
||||||
/// <param name="args">Optional command line arguments to pass to the application.</param>
|
/// <param name="args">Optional command line arguments to pass to the application.</param>
|
||||||
public void StartExe(string appPath, string[]? args = null)
|
public void StartExe(string appPath, string[]? args = null, string? enableModules = null)
|
||||||
{
|
{
|
||||||
var opts = new AppiumOptions();
|
var opts = new AppiumOptions();
|
||||||
|
if (!string.IsNullOrEmpty(enableModules))
|
||||||
|
{
|
||||||
|
opts.AddAdditionalCapability("enableModules", enableModules);
|
||||||
|
}
|
||||||
|
|
||||||
if (scope == PowerToysModule.PowerToysSettings)
|
if (scope == PowerToysModule.PowerToysSettings)
|
||||||
{
|
{
|
||||||
@@ -168,6 +172,23 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void TryLaunchPowerToysSettings(AppiumOptions opts)
|
private void TryLaunchPowerToysSettings(AppiumOptions opts)
|
||||||
|
{
|
||||||
|
if (opts.ToCapabilities().HasCapability("enableModules"))
|
||||||
|
{
|
||||||
|
var modulesString = (string)opts.ToCapabilities().GetCapability("enableModules");
|
||||||
|
var modulesArray = modulesString.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||||
|
SettingsConfigHelper.ConfigureGlobalModuleSettings(modulesArray);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SettingsConfigHelper.ConfigureGlobalModuleSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
const int maxTries = 3;
|
||||||
|
const int delayMs = 5000;
|
||||||
|
const int maxRetries = 3;
|
||||||
|
|
||||||
|
for (int tryCount = 1; tryCount <= maxTries; tryCount++)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -179,17 +200,39 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
};
|
};
|
||||||
|
|
||||||
ExitExe(runnerProcessInfo.FileName);
|
ExitExe(runnerProcessInfo.FileName);
|
||||||
|
|
||||||
|
// Verify process was killed
|
||||||
|
string exeName = Path.GetFileNameWithoutExtension(runnerProcessInfo.FileName);
|
||||||
|
var remainingProcesses = Process.GetProcessesByName(exeName);
|
||||||
|
|
||||||
runner = Process.Start(runnerProcessInfo);
|
runner = Process.Start(runnerProcessInfo);
|
||||||
|
|
||||||
WaitForWindowAndSetCapability(opts, "PowerToys Settings", 5000, 5);
|
if (WaitForWindowAndSetCapability(opts, "PowerToys Settings", delayMs, maxRetries))
|
||||||
|
{
|
||||||
// Exit CmdPal UI before launching new process if use installer for test
|
// Exit CmdPal UI before launching new process if use installer for test
|
||||||
ExitExeByName("Microsoft.CmdPal.UI");
|
ExitExeByName("Microsoft.CmdPal.UI");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Window not found, kill all PowerToys processes and retry
|
||||||
|
if (tryCount < maxTries)
|
||||||
|
{
|
||||||
|
KillPowerToysProcesses();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Failed to launch PowerToys Settings: {ex.Message}", ex);
|
if (tryCount == maxTries)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Failed to launch PowerToys Settings after {maxTries} attempts: {ex.Message}", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kill processes and retry
|
||||||
|
KillPowerToysProcesses();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException($"Failed to launch PowerToys Settings: Window not found after {maxTries} attempts.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TryLaunchCommandPalette(AppiumOptions opts)
|
private void TryLaunchCommandPalette(AppiumOptions opts)
|
||||||
@@ -211,7 +254,10 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
var process = Process.Start(processStartInfo);
|
var process = Process.Start(processStartInfo);
|
||||||
process?.WaitForExit();
|
process?.WaitForExit();
|
||||||
|
|
||||||
WaitForWindowAndSetCapability(opts, "Command Palette", 5000, 10);
|
if (!WaitForWindowAndSetCapability(opts, "Command Palette", 5000, 10))
|
||||||
|
{
|
||||||
|
throw new TimeoutException("Failed to find Command Palette window after multiple attempts.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -219,7 +265,7 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WaitForWindowAndSetCapability(AppiumOptions opts, string windowName, int delayMs, int maxRetries)
|
private bool WaitForWindowAndSetCapability(AppiumOptions opts, string windowName, int delayMs, int maxRetries)
|
||||||
{
|
{
|
||||||
for (int attempt = 1; attempt <= maxRetries; attempt++)
|
for (int attempt = 1; attempt <= maxRetries; attempt++)
|
||||||
{
|
{
|
||||||
@@ -230,18 +276,16 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
{
|
{
|
||||||
var hexHwnd = window[0].HWnd.ToString("x");
|
var hexHwnd = window[0].HWnd.ToString("x");
|
||||||
opts.AddAdditionalCapability("appTopLevelWindow", hexHwnd);
|
opts.AddAdditionalCapability("appTopLevelWindow", hexHwnd);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attempt < maxRetries)
|
if (attempt < maxRetries)
|
||||||
{
|
{
|
||||||
Thread.Sleep(delayMs);
|
Thread.Sleep(delayMs);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new TimeoutException($"Failed to find {windowName} window after multiple attempts.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -292,17 +336,17 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Handle exceptions if needed
|
// Handle exceptions if needed
|
||||||
Debug.WriteLine($"Exception during Cleanup: {ex.Message}");
|
Console.WriteLine($"Exception during Cleanup: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Restarts now exe and takes control of it.
|
/// Restarts now exe and takes control of it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RestartScopeExe()
|
public void RestartScopeExe(string? enableModules = null)
|
||||||
{
|
{
|
||||||
ExitScopeExe();
|
ExitScopeExe();
|
||||||
StartExe(locationPath + sessionPath, this.commandLineArgs);
|
StartExe(locationPath + sessionPath, commandLineArgs, enableModules);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WindowsDriver<WindowsElement> GetRoot()
|
public WindowsDriver<WindowsElement> GetRoot()
|
||||||
@@ -327,5 +371,31 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
this.ExitExe(winAppDriverProcessInfo.FileName);
|
this.ExitExe(winAppDriverProcessInfo.FileName);
|
||||||
SessionHelper.appDriver = Process.Start(winAppDriverProcessInfo);
|
SessionHelper.appDriver = Process.Start(winAppDriverProcessInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void KillPowerToysProcesses()
|
||||||
|
{
|
||||||
|
var powerToysProcessNames = new[] { "PowerToys", "Microsoft.CmdPal.UI" };
|
||||||
|
|
||||||
|
foreach (var processName in powerToysProcessNames)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var processes = Process.GetProcessesByName(processName);
|
||||||
|
|
||||||
|
foreach (var process in processes)
|
||||||
|
{
|
||||||
|
process.Kill();
|
||||||
|
process.WaitForExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify processes are actually gone
|
||||||
|
var remainingProcesses = Process.GetProcessesByName(processName);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[KillPowerToysProcesses] Failed to kill process {processName}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,14 +26,13 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configures global PowerToys settings to enable only specified modules and disable all others.
|
/// Configures global PowerToys settings to enable only specified modules and disable all others.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="modulesToEnable">Array of module names to enable (e.g., "Peek", "FancyZones"). All other modules will be disabled.</param>
|
/// <param name="modulesToEnable">Array of module names to enable (e.g., "Peek", "FancyZones"). All other modules will be disabled. If null or empty, all modules will be disabled.</param>
|
||||||
/// <exception cref="ArgumentNullException">Thrown when modulesToEnable is null.</exception>
|
|
||||||
/// <exception cref="InvalidOperationException">Thrown when settings file operations fail.</exception>
|
/// <exception cref="InvalidOperationException">Thrown when settings file operations fail.</exception>
|
||||||
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "This is test code and will not be trimmed")]
|
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "This is test code and will not be trimmed")]
|
||||||
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "This is test code and will not be AOT compiled")]
|
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "This is test code and will not be AOT compiled")]
|
||||||
public static void ConfigureGlobalModuleSettings(params string[] modulesToEnable)
|
public static void ConfigureGlobalModuleSettings(params string[]? modulesToEnable)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(modulesToEnable);
|
modulesToEnable ??= Array.Empty<string>();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
|
|
||||||
public string? ScreenshotDirectory { get; set; }
|
public string? ScreenshotDirectory { get; set; }
|
||||||
|
|
||||||
|
public string? RecordingDirectory { get; set; }
|
||||||
|
|
||||||
public static MonitorInfoData.ParamsWrapper MonitorInfoData { get; set; } = new MonitorInfoData.ParamsWrapper() { Monitors = new List<MonitorInfoData.MonitorInfoDataWrapper>() };
|
public static MonitorInfoData.ParamsWrapper MonitorInfoData { get; set; } = new MonitorInfoData.ParamsWrapper() { Monitors = new List<MonitorInfoData.MonitorInfoDataWrapper>() };
|
||||||
|
|
||||||
private readonly PowerToysModule scope;
|
private readonly PowerToysModule scope;
|
||||||
@@ -36,6 +38,7 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
private readonly string[]? commandLineArgs;
|
private readonly string[]? commandLineArgs;
|
||||||
private SessionHelper? sessionHelper;
|
private SessionHelper? sessionHelper;
|
||||||
private System.Threading.Timer? screenshotTimer;
|
private System.Threading.Timer? screenshotTimer;
|
||||||
|
private ScreenRecording? screenRecording;
|
||||||
|
|
||||||
public UITestBase(PowerToysModule scope = PowerToysModule.PowerToysSettings, WindowSize size = WindowSize.UnSpecified, string[]? commandLineArgs = null)
|
public UITestBase(PowerToysModule scope = PowerToysModule.PowerToysSettings, WindowSize size = WindowSize.UnSpecified, string[]? commandLineArgs = null)
|
||||||
{
|
{
|
||||||
@@ -65,12 +68,35 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
CloseOtherApplications();
|
CloseOtherApplications();
|
||||||
if (IsInPipeline)
|
if (IsInPipeline)
|
||||||
{
|
{
|
||||||
ScreenshotDirectory = Path.Combine(this.TestContext.TestResultsDirectory ?? string.Empty, "UITestScreenshots_" + Guid.NewGuid().ToString());
|
string baseDirectory = this.TestContext.TestResultsDirectory ?? string.Empty;
|
||||||
|
ScreenshotDirectory = Path.Combine(baseDirectory, "UITestScreenshots_" + Guid.NewGuid().ToString());
|
||||||
Directory.CreateDirectory(ScreenshotDirectory);
|
Directory.CreateDirectory(ScreenshotDirectory);
|
||||||
|
|
||||||
|
RecordingDirectory = Path.Combine(baseDirectory, "UITestRecordings_" + Guid.NewGuid().ToString());
|
||||||
|
Directory.CreateDirectory(RecordingDirectory);
|
||||||
|
|
||||||
// Take screenshot every 1 second
|
// Take screenshot every 1 second
|
||||||
screenshotTimer = new System.Threading.Timer(ScreenCapture.TimerCallback, ScreenshotDirectory, TimeSpan.Zero, TimeSpan.FromMilliseconds(1000));
|
screenshotTimer = new System.Threading.Timer(ScreenCapture.TimerCallback, ScreenshotDirectory, TimeSpan.Zero, TimeSpan.FromMilliseconds(1000));
|
||||||
|
|
||||||
|
// Start screen recording (requires FFmpeg)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
screenRecording = new ScreenRecording(RecordingDirectory);
|
||||||
|
if (screenRecording.IsAvailable)
|
||||||
|
{
|
||||||
|
_ = screenRecording.StartRecordingAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
screenRecording = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed to start screen recording: {ex.Message}");
|
||||||
|
screenRecording = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Escape Popups before starting
|
// Escape Popups before starting
|
||||||
System.Windows.Forms.SendKeys.SendWait("{ESC}");
|
System.Windows.Forms.SendKeys.SendWait("{ESC}");
|
||||||
}
|
}
|
||||||
@@ -88,15 +114,36 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
if (IsInPipeline)
|
if (IsInPipeline)
|
||||||
{
|
{
|
||||||
screenshotTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
screenshotTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
||||||
Dispose();
|
|
||||||
|
// Stop screen recording
|
||||||
|
if (screenRecording != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
screenRecording.StopRecordingAsync().GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed to stop screen recording: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (TestContext.CurrentTestOutcome is UnitTestOutcome.Failed
|
if (TestContext.CurrentTestOutcome is UnitTestOutcome.Failed
|
||||||
or UnitTestOutcome.Error
|
or UnitTestOutcome.Error
|
||||||
or UnitTestOutcome.Unknown)
|
or UnitTestOutcome.Unknown)
|
||||||
{
|
{
|
||||||
Task.Delay(1000).Wait();
|
Task.Delay(1000).Wait();
|
||||||
AddScreenShotsToTestResultsDirectory();
|
AddScreenShotsToTestResultsDirectory();
|
||||||
|
AddRecordingsToTestResultsDirectory();
|
||||||
AddLogFilesToTestResultsDirectory();
|
AddLogFilesToTestResultsDirectory();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Clean up recording if test passed
|
||||||
|
CleanupRecordingDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Session.Cleanup();
|
this.Session.Cleanup();
|
||||||
@@ -106,6 +153,7 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
screenshotTimer?.Dispose();
|
screenshotTimer?.Dispose();
|
||||||
|
screenRecording?.Dispose();
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,6 +648,47 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds screen recordings to test results directory when test fails.
|
||||||
|
/// </summary>
|
||||||
|
protected void AddRecordingsToTestResultsDirectory()
|
||||||
|
{
|
||||||
|
if (RecordingDirectory != null && Directory.Exists(RecordingDirectory))
|
||||||
|
{
|
||||||
|
// Add video files (MP4)
|
||||||
|
var videoFiles = Directory.GetFiles(RecordingDirectory, "*.mp4");
|
||||||
|
foreach (string file in videoFiles)
|
||||||
|
{
|
||||||
|
this.TestContext.AddResultFile(file);
|
||||||
|
var fileInfo = new FileInfo(file);
|
||||||
|
Console.WriteLine($"Added video recording: {Path.GetFileName(file)} ({fileInfo.Length / 1024 / 1024:F1} MB)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFiles.Length == 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("No video recording available (FFmpeg not found). Screenshots are still captured.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cleans up recording directory when test passes.
|
||||||
|
/// </summary>
|
||||||
|
private void CleanupRecordingDirectory()
|
||||||
|
{
|
||||||
|
if (RecordingDirectory != null && Directory.Exists(RecordingDirectory))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.Delete(RecordingDirectory, true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed to cleanup recording directory: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copies PowerToys log files to test results directory when test fails.
|
/// Copies PowerToys log files to test results directory when test fails.
|
||||||
/// Renames files to include the directory structure after \PowerToys.
|
/// Renames files to include the directory structure after \PowerToys.
|
||||||
@@ -689,11 +778,11 @@ namespace Microsoft.PowerToys.UITest
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Restart scope exe.
|
/// Restart scope exe.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RestartScopeExe()
|
public Session RestartScopeExe(string? enableModules = null)
|
||||||
{
|
{
|
||||||
this.sessionHelper!.RestartScopeExe();
|
this.sessionHelper!.RestartScopeExe(enableModules);
|
||||||
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver(), this.scope, this.size);
|
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver(), this.scope, this.size);
|
||||||
return;
|
return Session;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ enum class ScheduleMode
|
|||||||
Off,
|
Off,
|
||||||
FixedHours,
|
FixedHours,
|
||||||
SunsetToSunrise,
|
SunsetToSunrise,
|
||||||
|
FollowNightLight,
|
||||||
// add more later
|
// add more later
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,6 +64,8 @@ inline std::wstring ToString(ScheduleMode mode)
|
|||||||
return L"SunsetToSunrise";
|
return L"SunsetToSunrise";
|
||||||
case ScheduleMode::FixedHours:
|
case ScheduleMode::FixedHours:
|
||||||
return L"FixedHours";
|
return L"FixedHours";
|
||||||
|
case ScheduleMode::FollowNightLight:
|
||||||
|
return L"FollowNightLight";
|
||||||
default:
|
default:
|
||||||
return L"Off";
|
return L"Off";
|
||||||
}
|
}
|
||||||
@@ -74,6 +77,8 @@ inline ScheduleMode FromString(const std::wstring& str)
|
|||||||
return ScheduleMode::SunsetToSunrise;
|
return ScheduleMode::SunsetToSunrise;
|
||||||
if (str == L"FixedHours")
|
if (str == L"FixedHours")
|
||||||
return ScheduleMode::FixedHours;
|
return ScheduleMode::FixedHours;
|
||||||
|
if (str == L"FollowNightLight")
|
||||||
|
return ScheduleMode::FollowNightLight;
|
||||||
return ScheduleMode::Off;
|
return ScheduleMode::Off;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +183,9 @@ public:
|
|||||||
ToString(g_settings.m_scheduleMode),
|
ToString(g_settings.m_scheduleMode),
|
||||||
{ { L"Off", L"Disable the schedule" },
|
{ { L"Off", L"Disable the schedule" },
|
||||||
{ L"FixedHours", L"Set hours manually" },
|
{ L"FixedHours", L"Set hours manually" },
|
||||||
{ L"SunsetToSunrise", L"Use sunrise/sunset times" } });
|
{ L"SunsetToSunrise", L"Use sunrise/sunset times" },
|
||||||
|
{ L"FollowNightLight", L"Follow Windows Night Light state" }
|
||||||
|
});
|
||||||
|
|
||||||
// Integer spinners
|
// Integer spinners
|
||||||
settings.add_int_spinner(
|
settings.add_int_spinner(
|
||||||
|
|||||||
@@ -13,10 +13,12 @@
|
|||||||
#include <utils/logger_helper.h>
|
#include <utils/logger_helper.h>
|
||||||
#include "LightSwitchStateManager.h"
|
#include "LightSwitchStateManager.h"
|
||||||
#include <LightSwitchUtils.h>
|
#include <LightSwitchUtils.h>
|
||||||
|
#include <NightLightRegistryObserver.h>
|
||||||
|
|
||||||
SERVICE_STATUS g_ServiceStatus = {};
|
SERVICE_STATUS g_ServiceStatus = {};
|
||||||
SERVICE_STATUS_HANDLE g_StatusHandle = nullptr;
|
SERVICE_STATUS_HANDLE g_StatusHandle = nullptr;
|
||||||
HANDLE g_ServiceStopEvent = nullptr;
|
HANDLE g_ServiceStopEvent = nullptr;
|
||||||
|
static LightSwitchStateManager* g_stateManagerPtr = nullptr;
|
||||||
|
|
||||||
VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv);
|
VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv);
|
||||||
VOID WINAPI ServiceCtrlHandler(DWORD dwCtrl);
|
VOID WINAPI ServiceCtrlHandler(DWORD dwCtrl);
|
||||||
@@ -168,7 +170,15 @@ static void DetectAndHandleExternalThemeChange(LightSwitchStateManager& stateMan
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use shared helper (handles wraparound logic)
|
// Use shared helper (handles wraparound logic)
|
||||||
bool shouldBeLight = ShouldBeLight(nowMinutes, effectiveLight, effectiveDark);
|
bool shouldBeLight = false;
|
||||||
|
if (s.scheduleMode == ScheduleMode::FollowNightLight)
|
||||||
|
{
|
||||||
|
shouldBeLight = !IsNightLightEnabled();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shouldBeLight = ShouldBeLight(nowMinutes, effectiveLight, effectiveDark);
|
||||||
|
}
|
||||||
|
|
||||||
// Compare current system/apps theme
|
// Compare current system/apps theme
|
||||||
bool currentSystemLight = GetCurrentSystemTheme();
|
bool currentSystemLight = GetCurrentSystemTheme();
|
||||||
@@ -199,15 +209,40 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
|
|||||||
// Initialization
|
// Initialization
|
||||||
// ────────────────────────────────────────────────────────────────
|
// ────────────────────────────────────────────────────────────────
|
||||||
static LightSwitchStateManager stateManager;
|
static LightSwitchStateManager stateManager;
|
||||||
|
g_stateManagerPtr = &stateManager;
|
||||||
|
|
||||||
LightSwitchSettings::instance().InitFileWatcher();
|
LightSwitchSettings::instance().InitFileWatcher();
|
||||||
|
|
||||||
HANDLE hManualOverride = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
|
HANDLE hManualOverride = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
|
||||||
HANDLE hSettingsChanged = LightSwitchSettings::instance().GetSettingsChangedEvent();
|
HANDLE hSettingsChanged = LightSwitchSettings::instance().GetSettingsChangedEvent();
|
||||||
|
|
||||||
|
static std::unique_ptr<NightLightRegistryObserver> g_nightLightWatcher;
|
||||||
|
|
||||||
LightSwitchSettings::instance().LoadSettings();
|
LightSwitchSettings::instance().LoadSettings();
|
||||||
const auto& settings = LightSwitchSettings::instance().settings();
|
const auto& settings = LightSwitchSettings::instance().settings();
|
||||||
|
|
||||||
|
// after loading settings:
|
||||||
|
bool nightLightNeeded = (settings.scheduleMode == ScheduleMode::FollowNightLight);
|
||||||
|
|
||||||
|
if (nightLightNeeded && !g_nightLightWatcher)
|
||||||
|
{
|
||||||
|
Logger::info(L"[LightSwitchService] Starting Night Light registry watcher...");
|
||||||
|
|
||||||
|
g_nightLightWatcher = std::make_unique<NightLightRegistryObserver>(
|
||||||
|
HKEY_CURRENT_USER,
|
||||||
|
NIGHT_LIGHT_REGISTRY_PATH,
|
||||||
|
[]() {
|
||||||
|
if (g_stateManagerPtr)
|
||||||
|
g_stateManagerPtr->OnNightLightChange();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (!nightLightNeeded && g_nightLightWatcher)
|
||||||
|
{
|
||||||
|
Logger::info(L"[LightSwitchService] Stopping Night Light registry watcher...");
|
||||||
|
g_nightLightWatcher->Stop();
|
||||||
|
g_nightLightWatcher.reset();
|
||||||
|
}
|
||||||
|
|
||||||
SYSTEMTIME st;
|
SYSTEMTIME st;
|
||||||
GetLocalTime(&st);
|
GetLocalTime(&st);
|
||||||
int nowMinutes = st.wHour * 60 + st.wMinute;
|
int nowMinutes = st.wHour * 60 + st.wMinute;
|
||||||
@@ -274,6 +309,31 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
|
|||||||
ResetEvent(hSettingsChanged);
|
ResetEvent(hSettingsChanged);
|
||||||
LightSwitchSettings::instance().LoadSettings();
|
LightSwitchSettings::instance().LoadSettings();
|
||||||
stateManager.OnSettingsChanged();
|
stateManager.OnSettingsChanged();
|
||||||
|
|
||||||
|
const auto& settings = LightSwitchSettings::instance().settings();
|
||||||
|
bool nightLightNeeded = (settings.scheduleMode == ScheduleMode::FollowNightLight);
|
||||||
|
|
||||||
|
if (nightLightNeeded && !g_nightLightWatcher)
|
||||||
|
{
|
||||||
|
Logger::info(L"[LightSwitchService] Starting Night Light registry watcher...");
|
||||||
|
|
||||||
|
g_nightLightWatcher = std::make_unique<NightLightRegistryObserver>(
|
||||||
|
HKEY_CURRENT_USER,
|
||||||
|
NIGHT_LIGHT_REGISTRY_PATH,
|
||||||
|
[]() {
|
||||||
|
if (g_stateManagerPtr)
|
||||||
|
g_stateManagerPtr->OnNightLightChange();
|
||||||
|
});
|
||||||
|
|
||||||
|
stateManager.OnNightLightChange();
|
||||||
|
}
|
||||||
|
else if (!nightLightNeeded && g_nightLightWatcher)
|
||||||
|
{
|
||||||
|
Logger::info(L"[LightSwitchService] Stopping Night Light registry watcher...");
|
||||||
|
g_nightLightWatcher->Stop();
|
||||||
|
g_nightLightWatcher.reset();
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,6 +345,11 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
|
|||||||
CloseHandle(hManualOverride);
|
CloseHandle(hManualOverride);
|
||||||
if (hParent)
|
if (hParent)
|
||||||
CloseHandle(hParent);
|
CloseHandle(hParent);
|
||||||
|
if (g_nightLightWatcher)
|
||||||
|
{
|
||||||
|
g_nightLightWatcher->Stop();
|
||||||
|
g_nightLightWatcher.reset();
|
||||||
|
}
|
||||||
|
|
||||||
Logger::info(L"[LightSwitchService] Worker thread exiting cleanly.");
|
Logger::info(L"[LightSwitchService] Worker thread exiting cleanly.");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -76,6 +76,7 @@
|
|||||||
<ClCompile Include="LightSwitchService.cpp" />
|
<ClCompile Include="LightSwitchService.cpp" />
|
||||||
<ClCompile Include="LightSwitchSettings.cpp" />
|
<ClCompile Include="LightSwitchSettings.cpp" />
|
||||||
<ClCompile Include="LightSwitchStateManager.cpp" />
|
<ClCompile Include="LightSwitchStateManager.cpp" />
|
||||||
|
<ClCompile Include="NightLightRegistryObserver.cpp" />
|
||||||
<ClCompile Include="SettingsConstants.cpp" />
|
<ClCompile Include="SettingsConstants.cpp" />
|
||||||
<ClCompile Include="ThemeHelper.cpp" />
|
<ClCompile Include="ThemeHelper.cpp" />
|
||||||
<ClCompile Include="ThemeScheduler.cpp" />
|
<ClCompile Include="ThemeScheduler.cpp" />
|
||||||
@@ -88,6 +89,7 @@
|
|||||||
<ClInclude Include="LightSwitchSettings.h" />
|
<ClInclude Include="LightSwitchSettings.h" />
|
||||||
<ClInclude Include="LightSwitchStateManager.h" />
|
<ClInclude Include="LightSwitchStateManager.h" />
|
||||||
<ClInclude Include="LightSwitchUtils.h" />
|
<ClInclude Include="LightSwitchUtils.h" />
|
||||||
|
<ClInclude Include="NightLightRegistryObserver.h" />
|
||||||
<ClInclude Include="SettingsConstants.h" />
|
<ClInclude Include="SettingsConstants.h" />
|
||||||
<ClInclude Include="SettingsObserver.h" />
|
<ClInclude Include="SettingsObserver.h" />
|
||||||
<ClInclude Include="ThemeHelper.h" />
|
<ClInclude Include="ThemeHelper.h" />
|
||||||
|
|||||||
@@ -36,6 +36,9 @@
|
|||||||
<ClCompile Include="LightSwitchStateManager.cpp">
|
<ClCompile Include="LightSwitchStateManager.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="NightLightRegistryObserver.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="ThemeScheduler.h">
|
<ClInclude Include="ThemeScheduler.h">
|
||||||
@@ -62,6 +65,9 @@
|
|||||||
<ClInclude Include="LightSwitchUtils.h">
|
<ClInclude Include="LightSwitchUtils.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="NightLightRegistryObserver.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
|
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ enum class ScheduleMode
|
|||||||
{
|
{
|
||||||
Off,
|
Off,
|
||||||
FixedHours,
|
FixedHours,
|
||||||
SunsetToSunrise
|
SunsetToSunrise,
|
||||||
|
FollowNightLight,
|
||||||
// Add more in the future
|
// Add more in the future
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -31,6 +32,8 @@ inline std::wstring ToString(ScheduleMode mode)
|
|||||||
return L"FixedHours";
|
return L"FixedHours";
|
||||||
case ScheduleMode::SunsetToSunrise:
|
case ScheduleMode::SunsetToSunrise:
|
||||||
return L"SunsetToSunrise";
|
return L"SunsetToSunrise";
|
||||||
|
case ScheduleMode::FollowNightLight:
|
||||||
|
return L"FollowNightLight";
|
||||||
default:
|
default:
|
||||||
return L"Off";
|
return L"Off";
|
||||||
}
|
}
|
||||||
@@ -42,6 +45,8 @@ inline ScheduleMode FromString(const std::wstring& str)
|
|||||||
return ScheduleMode::SunsetToSunrise;
|
return ScheduleMode::SunsetToSunrise;
|
||||||
if (str == L"FixedHours")
|
if (str == L"FixedHours")
|
||||||
return ScheduleMode::FixedHours;
|
return ScheduleMode::FixedHours;
|
||||||
|
if (str == L"FollowNightLight")
|
||||||
|
return ScheduleMode::FollowNightLight;
|
||||||
else
|
else
|
||||||
return ScheduleMode::Off;
|
return ScheduleMode::Off;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,10 @@ void LightSwitchStateManager::OnSettingsChanged()
|
|||||||
void LightSwitchStateManager::OnTick(int currentMinutes)
|
void LightSwitchStateManager::OnTick(int currentMinutes)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_stateMutex);
|
std::lock_guard<std::mutex> lock(_stateMutex);
|
||||||
|
if (_state.lastAppliedMode != ScheduleMode::FollowNightLight)
|
||||||
|
{
|
||||||
EvaluateAndApplyIfNeeded();
|
EvaluateAndApplyIfNeeded();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when manual override is triggered
|
// Called when manual override is triggered
|
||||||
@@ -56,6 +59,36 @@ void LightSwitchStateManager::OnManualOverride()
|
|||||||
EvaluateAndApplyIfNeeded();
|
EvaluateAndApplyIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Runs with the registry observer detects a change in Night Light settings.
|
||||||
|
void LightSwitchStateManager::OnNightLightChange()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_stateMutex);
|
||||||
|
|
||||||
|
bool newNightLightState = IsNightLightEnabled();
|
||||||
|
|
||||||
|
// In Follow Night Light mode, treat a Night Light toggle as a boundary
|
||||||
|
if (_state.lastAppliedMode == ScheduleMode::FollowNightLight && _state.isManualOverride)
|
||||||
|
{
|
||||||
|
Logger::info(L"[LightSwitchStateManager] Night Light changed while manual override active; "
|
||||||
|
L"treating as a boundary and clearing manual override.");
|
||||||
|
_state.isManualOverride = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newNightLightState != _state.isNightLightActive)
|
||||||
|
{
|
||||||
|
Logger::info(L"[LightSwitchStateManager] Night Light toggled to {}",
|
||||||
|
newNightLightState ? L"ON" : L"OFF");
|
||||||
|
|
||||||
|
_state.isNightLightActive = newNightLightState;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::debug(L"[LightSwitchStateManager] Night Light change event fired, but no actual change.");
|
||||||
|
}
|
||||||
|
|
||||||
|
EvaluateAndApplyIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
bool LightSwitchStateManager::CoordinatesAreValid(const std::wstring& lat, const std::wstring& lon)
|
bool LightSwitchStateManager::CoordinatesAreValid(const std::wstring& lat, const std::wstring& lon)
|
||||||
{
|
{
|
||||||
@@ -194,7 +227,15 @@ void LightSwitchStateManager::EvaluateAndApplyIfNeeded()
|
|||||||
|
|
||||||
_state.lastAppliedMode = _currentSettings.scheduleMode;
|
_state.lastAppliedMode = _currentSettings.scheduleMode;
|
||||||
|
|
||||||
bool shouldBeLight = ShouldBeLight(now, _state.effectiveLightMinutes, _state.effectiveDarkMinutes);
|
bool shouldBeLight = false;
|
||||||
|
if (_currentSettings.scheduleMode == ScheduleMode::FollowNightLight)
|
||||||
|
{
|
||||||
|
shouldBeLight = !_state.isNightLightActive;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shouldBeLight = ShouldBeLight(now, _state.effectiveLightMinutes, _state.effectiveDarkMinutes);
|
||||||
|
}
|
||||||
|
|
||||||
bool appsNeedsToChange = _currentSettings.changeApps && (_state.isAppsLightActive != shouldBeLight);
|
bool appsNeedsToChange = _currentSettings.changeApps && (_state.isAppsLightActive != shouldBeLight);
|
||||||
bool systemNeedsToChange = _currentSettings.changeSystem && (_state.isSystemLightActive != shouldBeLight);
|
bool systemNeedsToChange = _currentSettings.changeSystem && (_state.isSystemLightActive != shouldBeLight);
|
||||||
@@ -227,6 +268,3 @@ void LightSwitchStateManager::EvaluateAndApplyIfNeeded()
|
|||||||
|
|
||||||
_state.lastTickMinutes = now;
|
_state.lastTickMinutes = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ struct LightSwitchState
|
|||||||
bool isManualOverride = false;
|
bool isManualOverride = false;
|
||||||
bool isSystemLightActive = false;
|
bool isSystemLightActive = false;
|
||||||
bool isAppsLightActive = false;
|
bool isAppsLightActive = false;
|
||||||
|
bool isNightLightActive = false;
|
||||||
int lastEvaluatedDay = -1;
|
int lastEvaluatedDay = -1;
|
||||||
int lastTickMinutes = -1;
|
int lastTickMinutes = -1;
|
||||||
|
|
||||||
@@ -32,6 +33,9 @@ public:
|
|||||||
// Called when manual override is toggled (via shortcut or system change).
|
// Called when manual override is toggled (via shortcut or system change).
|
||||||
void OnManualOverride();
|
void OnManualOverride();
|
||||||
|
|
||||||
|
// Called when night light changes in windows settings
|
||||||
|
void OnNightLightChange();
|
||||||
|
|
||||||
// Initial sync at startup to align internal state with system theme
|
// Initial sync at startup to align internal state with system theme
|
||||||
void SyncInitialThemeState();
|
void SyncInitialThemeState();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
#include "NightLightRegistryObserver.h"
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <wtypes.h>
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
class NightLightRegistryObserver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NightLightRegistryObserver(HKEY root, const std::wstring& subkey, std::function<void()> callback) :
|
||||||
|
_root(root), _subkey(subkey), _callback(std::move(callback)), _stop(false)
|
||||||
|
{
|
||||||
|
_thread = std::thread([this]() { this->Run(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
~NightLightRegistryObserver()
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stop()
|
||||||
|
{
|
||||||
|
_stop = true;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if (_event)
|
||||||
|
SetEvent(_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_thread.joinable())
|
||||||
|
_thread.join();
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if (_hKey)
|
||||||
|
{
|
||||||
|
RegCloseKey(_hKey);
|
||||||
|
_hKey = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_event)
|
||||||
|
{
|
||||||
|
CloseHandle(_event);
|
||||||
|
_event = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Run()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if (RegOpenKeyExW(_root, _subkey.c_str(), 0, KEY_NOTIFY, &_hKey) != ERROR_SUCCESS)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_event = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||||
|
if (!_event)
|
||||||
|
{
|
||||||
|
RegCloseKey(_hKey);
|
||||||
|
_hKey = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!_stop)
|
||||||
|
{
|
||||||
|
HKEY hKeyLocal = nullptr;
|
||||||
|
HANDLE eventLocal = nullptr;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if (_stop)
|
||||||
|
break;
|
||||||
|
|
||||||
|
hKeyLocal = _hKey;
|
||||||
|
eventLocal = _event;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hKeyLocal || !eventLocal)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (_stop)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (RegNotifyChangeKeyValue(hKeyLocal, FALSE, REG_NOTIFY_CHANGE_LAST_SET, eventLocal, TRUE) != ERROR_SUCCESS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
DWORD wait = WaitForSingleObject(eventLocal, INFINITE);
|
||||||
|
if (_stop || wait == WAIT_FAILED)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ResetEvent(eventLocal);
|
||||||
|
|
||||||
|
if (!_stop && _callback)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_callback();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if (_hKey)
|
||||||
|
{
|
||||||
|
RegCloseKey(_hKey);
|
||||||
|
_hKey = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_event)
|
||||||
|
{
|
||||||
|
CloseHandle(_event);
|
||||||
|
_event = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HKEY _root;
|
||||||
|
std::wstring _subkey;
|
||||||
|
std::function<void()> _callback;
|
||||||
|
HANDLE _event = nullptr;
|
||||||
|
HKEY _hKey = nullptr;
|
||||||
|
std::thread _thread;
|
||||||
|
std::atomic<bool> _stop;
|
||||||
|
std::mutex _mutex;
|
||||||
|
};
|
||||||
@@ -12,3 +12,6 @@ enum class SettingId
|
|||||||
ChangeSystem,
|
ChangeSystem,
|
||||||
ChangeApps
|
ChangeApps
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr wchar_t PERSONALIZATION_REGISTRY_PATH[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
|
||||||
|
constexpr wchar_t NIGHT_LIGHT_REGISTRY_PATH[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\CloudStore\\Store\\DefaultAccount\\Current\\default$windows.data.bluelightreduction.bluelightreductionstate\\windows.data.bluelightreduction.bluelightreductionstate";
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <logger/logger.h>
|
#include <logger/logger.h>
|
||||||
#include <utils/logger_helper.h>
|
#include <utils/logger_helper.h>
|
||||||
#include "ThemeHelper.h"
|
#include "ThemeHelper.h"
|
||||||
|
#include <SettingsConstants.h>
|
||||||
|
|
||||||
// Controls changing the themes.
|
// Controls changing the themes.
|
||||||
|
|
||||||
@@ -10,7 +11,7 @@ static void ResetColorPrevalence()
|
|||||||
{
|
{
|
||||||
HKEY hKey;
|
HKEY hKey;
|
||||||
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
||||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
|
PERSONALIZATION_REGISTRY_PATH,
|
||||||
0,
|
0,
|
||||||
KEY_SET_VALUE,
|
KEY_SET_VALUE,
|
||||||
&hKey) == ERROR_SUCCESS)
|
&hKey) == ERROR_SUCCESS)
|
||||||
@@ -31,7 +32,7 @@ void SetAppsTheme(bool mode)
|
|||||||
{
|
{
|
||||||
HKEY hKey;
|
HKEY hKey;
|
||||||
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
||||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
|
PERSONALIZATION_REGISTRY_PATH,
|
||||||
0,
|
0,
|
||||||
KEY_SET_VALUE,
|
KEY_SET_VALUE,
|
||||||
&hKey) == ERROR_SUCCESS)
|
&hKey) == ERROR_SUCCESS)
|
||||||
@@ -50,7 +51,7 @@ void SetSystemTheme(bool mode)
|
|||||||
{
|
{
|
||||||
HKEY hKey;
|
HKEY hKey;
|
||||||
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
||||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
|
PERSONALIZATION_REGISTRY_PATH,
|
||||||
0,
|
0,
|
||||||
KEY_SET_VALUE,
|
KEY_SET_VALUE,
|
||||||
&hKey) == ERROR_SUCCESS)
|
&hKey) == ERROR_SUCCESS)
|
||||||
@@ -79,7 +80,7 @@ bool GetCurrentSystemTheme()
|
|||||||
DWORD size = sizeof(value);
|
DWORD size = sizeof(value);
|
||||||
|
|
||||||
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
||||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
|
PERSONALIZATION_REGISTRY_PATH,
|
||||||
0,
|
0,
|
||||||
KEY_READ,
|
KEY_READ,
|
||||||
&hKey) == ERROR_SUCCESS)
|
&hKey) == ERROR_SUCCESS)
|
||||||
@@ -98,7 +99,7 @@ bool GetCurrentAppsTheme()
|
|||||||
DWORD size = sizeof(value);
|
DWORD size = sizeof(value);
|
||||||
|
|
||||||
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
||||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
|
PERSONALIZATION_REGISTRY_PATH,
|
||||||
0,
|
0,
|
||||||
KEY_READ,
|
KEY_READ,
|
||||||
&hKey) == ERROR_SUCCESS)
|
&hKey) == ERROR_SUCCESS)
|
||||||
@@ -109,3 +110,30 @@ bool GetCurrentAppsTheme()
|
|||||||
|
|
||||||
return value == 1; // true = light, false = dark
|
return value == 1; // true = light, false = dark
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsNightLightEnabled()
|
||||||
|
{
|
||||||
|
HKEY hKey;
|
||||||
|
const wchar_t* path = NIGHT_LIGHT_REGISTRY_PATH;
|
||||||
|
|
||||||
|
if (RegOpenKeyExW(HKEY_CURRENT_USER, path, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// RegGetValueW will set size to the size of the data and we expect that to be at least 25 bytes (we need to access bytes 23 and 24)
|
||||||
|
DWORD size = 0;
|
||||||
|
if (RegGetValueW(hKey, nullptr, L"Data", RRF_RT_REG_BINARY, nullptr, nullptr, &size) != ERROR_SUCCESS || size < 25)
|
||||||
|
{
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<BYTE> data(size);
|
||||||
|
if (RegGetValueW(hKey, nullptr, L"Data", RRF_RT_REG_BINARY, nullptr, data.data(), &size) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
return data[23] == 0x10 && data[24] == 0x00;
|
||||||
|
}
|
||||||
@@ -3,3 +3,4 @@ void SetSystemTheme(bool dark);
|
|||||||
void SetAppsTheme(bool dark);
|
void SetAppsTheme(bool dark);
|
||||||
bool GetCurrentSystemTheme();
|
bool GetCurrentSystemTheme();
|
||||||
bool GetCurrentAppsTheme();
|
bool GetCurrentAppsTheme();
|
||||||
|
bool IsNightLightEnabled();
|
||||||
@@ -1,16 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup Label="NuGet">
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||||
<!-- Tell NuGet this is PackageReference style -->
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
|
||||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
|
||||||
<!-- Tell NuGet we're a native project -->
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
|
||||||
<NuGetTargetMoniker>native,Version=v0.0</NuGetTargetMoniker>
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
|
||||||
<!-- Tell NuGet we target Windows (use your existing WindowsTargetPlatformVersion) -->
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||||
<NuGetTargetPlatformIdentifier>Windows</NuGetTargetPlatformIdentifier>
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
<NuGetTargetPlatformVersion>$(WindowsTargetPlatformVersion)</NuGetTargetPlatformVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||||
@@ -33,11 +31,6 @@
|
|||||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" GeneratePathProperty="true" />
|
|
||||||
<PackageReference Include="Microsoft.Windows.CppWinRT" GeneratePathProperty="true" />
|
|
||||||
<PackageReference Include="Microsoft.Windows.ImplementationLibrary" GeneratePathProperty="true" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Label="Configuration">
|
<PropertyGroup Label="Configuration">
|
||||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
@@ -45,6 +38,7 @@
|
|||||||
<DesktopCompatible>true</DesktopCompatible>
|
<DesktopCompatible>true</DesktopCompatible>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||||
<ImportGroup Label="ExtensionSettings">
|
<ImportGroup Label="ExtensionSettings">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
<ImportGroup Label="PropertySheets">
|
<ImportGroup Label="PropertySheets">
|
||||||
@@ -124,6 +118,9 @@
|
|||||||
<WarnAsError>true</WarnAsError>
|
<WarnAsError>true</WarnAsError>
|
||||||
</Midl>
|
</Midl>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\common\Display\Display.vcxproj">
|
<ProjectReference Include="..\..\..\common\Display\Display.vcxproj">
|
||||||
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
|
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
|
||||||
@@ -145,5 +142,42 @@
|
|||||||
<ResourceCompile Include="PowerToys.MeasureToolCore.rc" />
|
<ResourceCompile Include="PowerToys.MeasureToolCore.rc" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
17
src/modules/MeasureTool/MeasureToolCore/packages.config
Normal file
17
src/modules/MeasureTool/MeasureToolCore/packages.config
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||||
|
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||||
|
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||||
|
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.26100.4188" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
|
||||||
|
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
|
||||||
|
</packages>
|
||||||
@@ -73,13 +73,6 @@
|
|||||||
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||||
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||||
<ProjectReference Include="..\MeasureToolCore\PowerToys.MeasureToolCore.vcxproj">
|
<ProjectReference Include="..\MeasureToolCore\PowerToys.MeasureToolCore.vcxproj" />
|
||||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
|
||||||
<BuildProject>true</BuildProject>
|
|
||||||
</ProjectReference>
|
|
||||||
<CsWinRTInputs Include="$(OutputPath)\PowerToys.MeasureToolCore.winmd" />
|
|
||||||
<None Include="$(OutputPath)\PowerToys.MeasureToolCore.dll">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup Label="NuGet">
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||||
<!-- Tell NuGet this is PackageReference style -->
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
|
||||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
|
||||||
<!-- Tell NuGet we're a native project -->
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
|
||||||
<NuGetTargetMoniker>native,Version=v0.0</NuGetTargetMoniker>
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
|
||||||
<!-- Tell NuGet we target Windows (use your existing WindowsTargetPlatformVersion) -->
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
<NuGetTargetPlatformIdentifier>Windows</NuGetTargetPlatformIdentifier>
|
|
||||||
<NuGetTargetPlatformVersion>$(WindowsTargetPlatformVersion)</NuGetTargetPlatformVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<VCProjectVersion>15.0</VCProjectVersion>
|
<VCProjectVersion>15.0</VCProjectVersion>
|
||||||
<ProjectGuid>{e94fd11c-0591-456f-899f-efc0ca548336}</ProjectGuid>
|
<ProjectGuid>{e94fd11c-0591-456f-899f-efc0ca548336}</ProjectGuid>
|
||||||
@@ -23,12 +20,9 @@
|
|||||||
<WindowsAppSdkBootstrapInitialize>false</WindowsAppSdkBootstrapInitialize>
|
<WindowsAppSdkBootstrapInitialize>false</WindowsAppSdkBootstrapInitialize>
|
||||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||||
<WindowsAppSDKVerifyTransitiveDependencies>false</WindowsAppSDKVerifyTransitiveDependencies>
|
<WindowsAppSDKVerifyTransitiveDependencies>false</WindowsAppSDKVerifyTransitiveDependencies>
|
||||||
|
<!-- Force NuGet to treat this project strictly as packages.config style -->
|
||||||
|
<RestoreProjectStyle>packages.config</RestoreProjectStyle>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" GeneratePathProperty="true"/>
|
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK.Foundation" GeneratePathProperty="true"/>
|
|
||||||
<PackageReference Include="Microsoft.Windows.CppWinRT" GeneratePathProperty="true"/>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
@@ -133,18 +127,18 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="FindMyMouse.rc" />
|
<ResourceCompile Include="FindMyMouse.rc" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- Deduplicate WindowsAppRuntimeAutoInitializer.cpp (added twice via transitive imports causing LNK4042). Remove all then add exactly once. -->
|
|
||||||
<Target Name="FixWinAppSDKAutoInitializer" BeforeTargets="ClCompile" AfterTargets="WindowsAppRuntimeAutoInitializer">
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- Remove ALL injected versions of the file -->
|
<None Include="packages.config" />
|
||||||
<ClCompile Remove="@(ClCompile)" Condition="'%(Filename)' == 'WindowsAppRuntimeAutoInitializer'" />
|
</ItemGroup>
|
||||||
|
<!-- Deduplicate WindowsAppRuntimeAutoInitializer.cpp (added twice via transitive imports causing LNK4042). Remove all then add exactly once. -->
|
||||||
<!-- Add ONE copy back manually -->
|
<ItemGroup Condition="'$(PkgMicrosoft_WindowsAppSDK)'!=''">
|
||||||
<ClCompile Include="$(PkgMicrosoft_WindowsAppSDK_Foundation)\include\WindowsAppRuntimeAutoInitializer.cpp">
|
<!-- Remove any transitive inclusion first -->
|
||||||
|
<ClCompile Remove="$(PkgMicrosoft_WindowsAppSDK)\include\WindowsAppRuntimeAutoInitializer.cpp" />
|
||||||
|
<!-- Re-add once, but disable PCH because the SDK file doesn't include our pch.h -->
|
||||||
|
<ClCompile Include="$(PkgMicrosoft_WindowsAppSDK)\include\WindowsAppRuntimeAutoInitializer.cpp">
|
||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Target>
|
|
||||||
<Target Name="RemoveManagedWebView2CoreFromNativeOutDir" AfterTargets="Build">
|
<Target Name="RemoveManagedWebView2CoreFromNativeOutDir" AfterTargets="Build">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<_ToDelete Include="$(OutDir)Microsoft.Web.WebView2.Core.dll" />
|
<_ToDelete Include="$(OutDir)Microsoft.Web.WebView2.Core.dll" />
|
||||||
@@ -154,4 +148,38 @@
|
|||||||
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
|
||||||
|
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.Web.WebView2.1.0.2903.40\\build\\native\\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.Web.WebView2.1.0.2903.40\\build\\native\\Microsoft.Web.WebView2.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.props'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.props'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.props'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.props'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.props'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.props'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
|
||||||
|
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
12
src/modules/MouseUtils/FindMyMouse/packages.config
Normal file
12
src/modules/MouseUtils/FindMyMouse/packages.config
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
|
||||||
|
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||||
|
</packages>
|
||||||
@@ -617,6 +617,8 @@ namespace MouseUtils.UITests
|
|||||||
|
|
||||||
private void LaunchFromSetting(bool reload = false, bool launchAsAdmin = false)
|
private void LaunchFromSetting(bool reload = false, bool launchAsAdmin = false)
|
||||||
{
|
{
|
||||||
|
Session = RestartScopeExe("FindMyMouse,MouseHighlighter,MouseJump,MousePointerCrosshairs,CursorWrap");
|
||||||
|
|
||||||
// this.Session.Attach(PowerToysModule.PowerToysSettings);
|
// this.Session.Attach(PowerToysModule.PowerToysSettings);
|
||||||
this.Session.SetMainWindowSize(WindowSize.Large);
|
this.Session.SetMainWindowSize(WindowSize.Large);
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ public partial class DetailsViewModel(IDetails _details, WeakReference<IPageCont
|
|||||||
|
|
||||||
public string Body { get; private set; } = string.Empty;
|
public string Body { get; private set; } = string.Empty;
|
||||||
|
|
||||||
|
public ContentSize? Size { get; private set; } = ContentSize.Small;
|
||||||
|
|
||||||
// Metadata is an array of IDetailsElement,
|
// Metadata is an array of IDetailsElement,
|
||||||
// where IDetailsElement = {IDetailsTags, IDetailsLink, IDetailsSeparator}
|
// where IDetailsElement = {IDetailsTags, IDetailsLink, IDetailsSeparator}
|
||||||
public List<DetailsElementViewModel> Metadata { get; private set; } = [];
|
public List<DetailsElementViewModel> Metadata { get; private set; } = [];
|
||||||
@@ -40,6 +42,21 @@ public partial class DetailsViewModel(IDetails _details, WeakReference<IPageCont
|
|||||||
UpdateProperty(nameof(Body));
|
UpdateProperty(nameof(Body));
|
||||||
UpdateProperty(nameof(HeroImage));
|
UpdateProperty(nameof(HeroImage));
|
||||||
|
|
||||||
|
if (model is IExtendedAttributesProvider provider)
|
||||||
|
{
|
||||||
|
if (provider.GetProperties()?.TryGetValue("Size", out var rawValue) == true)
|
||||||
|
{
|
||||||
|
if (rawValue is int sizeAsInt)
|
||||||
|
{
|
||||||
|
Size = (ContentSize)sizeAsInt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Size ??= ContentSize.Small;
|
||||||
|
|
||||||
|
UpdateProperty(nameof(Size));
|
||||||
|
|
||||||
var meta = model.Metadata;
|
var meta = model.Metadata;
|
||||||
if (meta is not null)
|
if (meta is not null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ public partial class ListItemViewModel : CommandItemViewModel
|
|||||||
|
|
||||||
public string Section { get; private set; } = string.Empty;
|
public string Section { get; private set; } = string.Empty;
|
||||||
|
|
||||||
|
public bool IsSectionOrSeparator { get; private set; }
|
||||||
|
|
||||||
public DetailsViewModel? Details { get; private set; }
|
public DetailsViewModel? Details { get; private set; }
|
||||||
|
|
||||||
[MemberNotNullWhen(true, nameof(Details))]
|
[MemberNotNullWhen(true, nameof(Details))]
|
||||||
@@ -82,14 +84,18 @@ public partial class ListItemViewModel : CommandItemViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
UpdateTags(li.Tags);
|
UpdateTags(li.Tags);
|
||||||
|
|
||||||
Section = li.Section ?? string.Empty;
|
Section = li.Section ?? string.Empty;
|
||||||
|
IsSectionOrSeparator = IsSeparator(li);
|
||||||
UpdateProperty(nameof(Section));
|
UpdateProperty(nameof(Section), nameof(IsSectionOrSeparator));
|
||||||
|
|
||||||
UpdateAccessibleName();
|
UpdateAccessibleName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsSeparator(IListItem item)
|
||||||
|
{
|
||||||
|
return item.Command is null;
|
||||||
|
}
|
||||||
|
|
||||||
public override void SlowInitializeProperties()
|
public override void SlowInitializeProperties()
|
||||||
{
|
{
|
||||||
base.SlowInitializeProperties();
|
base.SlowInitializeProperties();
|
||||||
@@ -104,8 +110,7 @@ public partial class ListItemViewModel : CommandItemViewModel
|
|||||||
{
|
{
|
||||||
Details = new(extensionDetails, PageContext);
|
Details = new(extensionDetails, PageContext);
|
||||||
Details.InitializeProperties();
|
Details.InitializeProperties();
|
||||||
UpdateProperty(nameof(Details));
|
UpdateProperty(nameof(Details), nameof(HasDetails));
|
||||||
UpdateProperty(nameof(HasDetails));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AddShowDetailsCommands();
|
AddShowDetailsCommands();
|
||||||
@@ -135,14 +140,18 @@ public partial class ListItemViewModel : CommandItemViewModel
|
|||||||
break;
|
break;
|
||||||
case nameof(model.Section):
|
case nameof(model.Section):
|
||||||
Section = model.Section ?? string.Empty;
|
Section = model.Section ?? string.Empty;
|
||||||
UpdateProperty(nameof(Section));
|
IsSectionOrSeparator = IsSeparator(model);
|
||||||
|
UpdateProperty(nameof(Section), nameof(IsSectionOrSeparator));
|
||||||
break;
|
break;
|
||||||
case nameof(model.Details):
|
case nameof(model.Command):
|
||||||
|
IsSectionOrSeparator = IsSeparator(model);
|
||||||
|
UpdateProperty(nameof(IsSectionOrSeparator));
|
||||||
|
break;
|
||||||
|
case nameof(Details):
|
||||||
var extensionDetails = model.Details;
|
var extensionDetails = model.Details;
|
||||||
Details = extensionDetails is not null ? new(extensionDetails, PageContext) : null;
|
Details = extensionDetails is not null ? new(extensionDetails, PageContext) : null;
|
||||||
Details?.InitializeProperties();
|
Details?.InitializeProperties();
|
||||||
UpdateProperty(nameof(Details));
|
UpdateProperty(nameof(Details), nameof(HasDetails));
|
||||||
UpdateProperty(nameof(HasDetails));
|
|
||||||
UpdateShowDetailsCommand();
|
UpdateShowDetailsCommand();
|
||||||
break;
|
break;
|
||||||
case nameof(model.MoreCommands):
|
case nameof(model.MoreCommands):
|
||||||
@@ -194,8 +203,7 @@ public partial class ListItemViewModel : CommandItemViewModel
|
|||||||
MoreCommands.Add(showDetailsContextItemViewModel);
|
MoreCommands.Add(showDetailsContextItemViewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateProperty(nameof(MoreCommands));
|
UpdateProperty(nameof(MoreCommands), nameof(AllCommands));
|
||||||
UpdateProperty(nameof(AllCommands));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,8 +235,7 @@ public partial class ListItemViewModel : CommandItemViewModel
|
|||||||
showDetailsContextItemViewModel.SlowInitializeProperties();
|
showDetailsContextItemViewModel.SlowInitializeProperties();
|
||||||
MoreCommands.Add(showDetailsContextItemViewModel);
|
MoreCommands.Add(showDetailsContextItemViewModel);
|
||||||
|
|
||||||
UpdateProperty(nameof(MoreCommands));
|
UpdateProperty(nameof(MoreCommands), nameof(AllCommands));
|
||||||
UpdateProperty(nameof(AllCommands));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Microsoft.CmdPal.Core.ViewModels;
|
|
||||||
using Microsoft.CommandPalette.Extensions;
|
using Microsoft.CommandPalette.Extensions;
|
||||||
|
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.Core.ViewModels;
|
namespace Microsoft.CmdPal.Core.ViewModels;
|
||||||
|
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
||||||
public partial class SeparatorViewModel() :
|
public partial class SeparatorViewModel() :
|
||||||
|
CommandItem,
|
||||||
IContextItemViewModel,
|
IContextItemViewModel,
|
||||||
IFilterItemViewModel,
|
IFilterItemViewModel,
|
||||||
ISeparatorContextItem,
|
ISeparatorContextItem,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<!-- For MVVM Toolkit Partial Properties/AOT support -->
|
<!-- For MVVM Toolkit Partial Properties/AOT support -->
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
|
|
||||||
<OutputPath>..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\CmdPal\</OutputPath>
|
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal\</OutputPath>
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||||
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
|
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- Images -->
|
<!-- Images -->
|
||||||
<Content Include=".\Assets\$(CmdPalAssetSuffix)\**\*">
|
<Content Include="$(SolutionDir)\src\modules\cmdpal\Microsoft.CmdPal.UI\Assets\$(CmdPalAssetSuffix)\**\*">
|
||||||
<DeploymentContent>true</DeploymentContent>
|
<DeploymentContent>true</DeploymentContent>
|
||||||
<Link>Assets\%(RecursiveDir)%(FileName)%(Extension)</Link>
|
<Link>Assets\%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||||
</Content>
|
</Content>
|
||||||
@@ -35,10 +35,14 @@
|
|||||||
|
|
||||||
<!-- In the future, when we actually want to support "preview" and "canary",
|
<!-- In the future, when we actually want to support "preview" and "canary",
|
||||||
add a Package-Pre.appxmanifest, etc. -->
|
add a Package-Pre.appxmanifest, etc. -->
|
||||||
<AppxManifest Include="Package.appxmanifest" Condition="'$(CommandPaletteBranding)'=='Release'" />
|
<AppxManifest Include="Package.appxmanifest"
|
||||||
<AppxManifest Include="Package.appxmanifest" Condition="'$(CommandPaletteBranding)'=='Preview'" />
|
Condition="'$(CommandPaletteBranding)'=='Release'" />
|
||||||
<AppxManifest Include="Package.appxmanifest" Condition="'$(CommandPaletteBranding)'=='Canary'" />
|
<AppxManifest Include="Package.appxmanifest"
|
||||||
<AppxManifest Include="Package-Dev.appxmanifest" Condition="'$(CommandPaletteBranding)'=='' or '$(CommandPaletteBranding)'=='Dev'" />
|
Condition="'$(CommandPaletteBranding)'=='Preview'" />
|
||||||
|
<AppxManifest Include="Package.appxmanifest"
|
||||||
|
Condition="'$(CommandPaletteBranding)'=='Canary'" />
|
||||||
|
<AppxManifest Include="Package-Dev.appxmanifest"
|
||||||
|
Condition="'$(CommandPaletteBranding)'=='' or '$(CommandPaletteBranding)'=='Dev'" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
|
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
|
||||||
<!-- Reset this because the Versioning task might have overwritten it before it knew about OutDir -->
|
<!-- Reset this because the Versioning task might have overwritten it before it knew about OutDir -->
|
||||||
<AppxPackageDir>$(OutputPath)\AppPackages\</AppxPackageDir>
|
<AppxPackageDir>$(OutputPath)\AppPackages\</AppxPackageDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// 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;
|
||||||
|
using Windows.Foundation;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.UI.Controls;
|
||||||
|
|
||||||
|
internal sealed class UVBounds
|
||||||
|
{
|
||||||
|
public double UMin { get; }
|
||||||
|
|
||||||
|
public double UMax { get; }
|
||||||
|
|
||||||
|
public double VMin { get; }
|
||||||
|
|
||||||
|
public double VMax { get; }
|
||||||
|
|
||||||
|
public UVBounds(Orientation orientation, Rect rect)
|
||||||
|
{
|
||||||
|
if (orientation == Orientation.Horizontal)
|
||||||
|
{
|
||||||
|
UMin = rect.Left;
|
||||||
|
UMax = rect.Right;
|
||||||
|
VMin = rect.Top;
|
||||||
|
VMax = rect.Bottom;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UMin = rect.Top;
|
||||||
|
UMax = rect.Bottom;
|
||||||
|
VMin = rect.Left;
|
||||||
|
VMax = rect.Right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
// 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.Diagnostics;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Windows.Foundation;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.UI.Controls;
|
||||||
|
|
||||||
|
[DebuggerDisplay("U = {U} V = {V}")]
|
||||||
|
internal struct UvMeasure
|
||||||
|
{
|
||||||
|
internal double U { get; set; }
|
||||||
|
|
||||||
|
internal double V { get; set; }
|
||||||
|
|
||||||
|
internal static UvMeasure Zero => default(UvMeasure);
|
||||||
|
|
||||||
|
public UvMeasure(Orientation orientation, Size size)
|
||||||
|
: this(orientation, size.Width, size.Height)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public UvMeasure(Orientation orientation, double width, double height)
|
||||||
|
{
|
||||||
|
if (orientation == Orientation.Horizontal)
|
||||||
|
{
|
||||||
|
U = width;
|
||||||
|
V = height;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
U = height;
|
||||||
|
V = width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UvMeasure Add(double u, double v)
|
||||||
|
{
|
||||||
|
UvMeasure result = default(UvMeasure);
|
||||||
|
result.U = U + u;
|
||||||
|
result.V = V + v;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UvMeasure Add(UvMeasure measure)
|
||||||
|
{
|
||||||
|
return Add(measure.U, measure.V);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size ToSize(Orientation orientation)
|
||||||
|
{
|
||||||
|
if (orientation != Orientation.Horizontal)
|
||||||
|
{
|
||||||
|
return new Size(V, U);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Size(U, V);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Point GetPoint(Orientation orientation)
|
||||||
|
{
|
||||||
|
return orientation is Orientation.Horizontal ? new Point(U, V) : new Point(V, U);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size GetSize(Orientation orientation)
|
||||||
|
{
|
||||||
|
return orientation is Orientation.Horizontal ? new Size(U, V) : new Size(V, U);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(UvMeasure measure1, UvMeasure measure2)
|
||||||
|
{
|
||||||
|
return measure1.U == measure2.U && measure1.V == measure2.V;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(UvMeasure measure1, UvMeasure measure2)
|
||||||
|
{
|
||||||
|
return !(measure1 == measure2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return obj is UvMeasure measure && this == measure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(UvMeasure value)
|
||||||
|
{
|
||||||
|
return this == value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return base.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,416 @@
|
|||||||
|
// 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.WinUI.Controls;
|
||||||
|
using Microsoft.CmdPal.Core.ViewModels;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Windows.Foundation;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.UI.Controls;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arranges elements by wrapping them to fit the available space.
|
||||||
|
/// When <see cref="Orientation"/> is set to Orientation.Horizontal, element are arranged in rows until the available width is reached and then to a new row.
|
||||||
|
/// When <see cref="Orientation"/> is set to Orientation.Vertical, element are arranged in columns until the available height is reached.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class WrapPanel : Panel
|
||||||
|
{
|
||||||
|
private struct UvRect
|
||||||
|
{
|
||||||
|
public UvMeasure Position { get; set; }
|
||||||
|
|
||||||
|
public UvMeasure Size { get; set; }
|
||||||
|
|
||||||
|
public Rect ToRect(Orientation orientation)
|
||||||
|
{
|
||||||
|
return orientation switch
|
||||||
|
{
|
||||||
|
Orientation.Vertical => new Rect(Position.V, Position.U, Size.V, Size.U),
|
||||||
|
Orientation.Horizontal => new Rect(Position.U, Position.V, Size.U, Size.V),
|
||||||
|
_ => ThrowArgumentException(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Rect ThrowArgumentException()
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The input orientation is not valid.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct Row
|
||||||
|
{
|
||||||
|
public List<UvRect> ChildrenRects { get; }
|
||||||
|
|
||||||
|
public UvMeasure Size { get; set; }
|
||||||
|
|
||||||
|
public UvRect Rect
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
UvRect result;
|
||||||
|
if (ChildrenRects.Count <= 0)
|
||||||
|
{
|
||||||
|
result = default(UvRect);
|
||||||
|
result.Position = UvMeasure.Zero;
|
||||||
|
result.Size = Size;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = default(UvRect);
|
||||||
|
result.Position = ChildrenRects.First().Position;
|
||||||
|
result.Size = Size;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Row(List<UvRect> childrenRects, UvMeasure size)
|
||||||
|
{
|
||||||
|
ChildrenRects = childrenRects;
|
||||||
|
Size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(UvMeasure position, UvMeasure size)
|
||||||
|
{
|
||||||
|
ChildrenRects.Add(new UvRect
|
||||||
|
{
|
||||||
|
Position = position,
|
||||||
|
Size = size,
|
||||||
|
});
|
||||||
|
|
||||||
|
Size = new UvMeasure
|
||||||
|
{
|
||||||
|
U = position.U + size.U,
|
||||||
|
V = Math.Max(Size.V, size.V),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a uniform Horizontal distance (in pixels) between items when <see cref="Orientation"/> is set to Horizontal,
|
||||||
|
/// or between columns of items when <see cref="Orientation"/> is set to Vertical.
|
||||||
|
/// </summary>
|
||||||
|
public double HorizontalSpacing
|
||||||
|
{
|
||||||
|
get { return (double)GetValue(HorizontalSpacingProperty); }
|
||||||
|
set { SetValue(HorizontalSpacingProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsSectionItem(UIElement element) => element is FrameworkElement fe && fe.DataContext is ListItemViewModel item && item.IsSectionOrSeparator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies the <see cref="HorizontalSpacing"/> dependency property.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DependencyProperty HorizontalSpacingProperty =
|
||||||
|
DependencyProperty.Register(
|
||||||
|
nameof(HorizontalSpacing),
|
||||||
|
typeof(double),
|
||||||
|
typeof(WrapPanel),
|
||||||
|
new PropertyMetadata(0d, LayoutPropertyChanged));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a uniform Vertical distance (in pixels) between items when <see cref="Orientation"/> is set to Vertical,
|
||||||
|
/// or between rows of items when <see cref="Orientation"/> is set to Horizontal.
|
||||||
|
/// </summary>
|
||||||
|
public double VerticalSpacing
|
||||||
|
{
|
||||||
|
get { return (double)GetValue(VerticalSpacingProperty); }
|
||||||
|
set { SetValue(VerticalSpacingProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies the <see cref="VerticalSpacing"/> dependency property.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DependencyProperty VerticalSpacingProperty =
|
||||||
|
DependencyProperty.Register(
|
||||||
|
nameof(VerticalSpacing),
|
||||||
|
typeof(double),
|
||||||
|
typeof(WrapPanel),
|
||||||
|
new PropertyMetadata(0d, LayoutPropertyChanged));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the orientation of the WrapPanel.
|
||||||
|
/// Horizontal means that child controls will be added horizontally until the width of the panel is reached, then a new row is added to add new child controls.
|
||||||
|
/// Vertical means that children will be added vertically until the height of the panel is reached, then a new column is added.
|
||||||
|
/// </summary>
|
||||||
|
public Orientation Orientation
|
||||||
|
{
|
||||||
|
get { return (Orientation)GetValue(OrientationProperty); }
|
||||||
|
set { SetValue(OrientationProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies the <see cref="Orientation"/> dependency property.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DependencyProperty OrientationProperty =
|
||||||
|
DependencyProperty.Register(
|
||||||
|
nameof(Orientation),
|
||||||
|
typeof(Orientation),
|
||||||
|
typeof(WrapPanel),
|
||||||
|
new PropertyMetadata(Orientation.Horizontal, LayoutPropertyChanged));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the distance between the border and its child object.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// The dimensions of the space between the border and its child as a Thickness value.
|
||||||
|
/// Thickness is a structure that stores dimension values using pixel measures.
|
||||||
|
/// </returns>
|
||||||
|
public Thickness Padding
|
||||||
|
{
|
||||||
|
get { return (Thickness)GetValue(PaddingProperty); }
|
||||||
|
set { SetValue(PaddingProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies the Padding dependency property.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The identifier for the <see cref="Padding"/> dependency property.</returns>
|
||||||
|
public static readonly DependencyProperty PaddingProperty =
|
||||||
|
DependencyProperty.Register(
|
||||||
|
nameof(Padding),
|
||||||
|
typeof(Thickness),
|
||||||
|
typeof(WrapPanel),
|
||||||
|
new PropertyMetadata(default(Thickness), LayoutPropertyChanged));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating how to arrange child items
|
||||||
|
/// </summary>
|
||||||
|
public StretchChild StretchChild
|
||||||
|
{
|
||||||
|
get { return (StretchChild)GetValue(StretchChildProperty); }
|
||||||
|
set { SetValue(StretchChildProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies the <see cref="StretchChild"/> dependency property.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The identifier for the <see cref="StretchChild"/> dependency property.</returns>
|
||||||
|
public static readonly DependencyProperty StretchChildProperty =
|
||||||
|
DependencyProperty.Register(
|
||||||
|
nameof(StretchChild),
|
||||||
|
typeof(StretchChild),
|
||||||
|
typeof(WrapPanel),
|
||||||
|
new PropertyMetadata(StretchChild.None, LayoutPropertyChanged));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies the IsFullLine attached dependency property.
|
||||||
|
/// If true, the child element will occupy the entire width of the panel and force a line break before and after itself.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DependencyProperty IsFullLineProperty =
|
||||||
|
DependencyProperty.RegisterAttached(
|
||||||
|
"IsFullLine",
|
||||||
|
typeof(bool),
|
||||||
|
typeof(WrapPanel),
|
||||||
|
new PropertyMetadata(false, OnIsFullLineChanged));
|
||||||
|
|
||||||
|
public static bool GetIsFullLine(DependencyObject obj)
|
||||||
|
{
|
||||||
|
return (bool)obj.GetValue(IsFullLineProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetIsFullLine(DependencyObject obj, bool value)
|
||||||
|
{
|
||||||
|
obj.SetValue(IsFullLineProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnIsFullLineChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (FindVisualParentWrapPanel(d) is WrapPanel wp)
|
||||||
|
{
|
||||||
|
wp.InvalidateMeasure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static WrapPanel? FindVisualParentWrapPanel(DependencyObject child)
|
||||||
|
{
|
||||||
|
var parent = Microsoft.UI.Xaml.Media.VisualTreeHelper.GetParent(child);
|
||||||
|
|
||||||
|
while (parent != null)
|
||||||
|
{
|
||||||
|
if (parent is WrapPanel wrapPanel)
|
||||||
|
{
|
||||||
|
return wrapPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = Microsoft.UI.Xaml.Media.VisualTreeHelper.GetParent(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LayoutPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (d is WrapPanel wp)
|
||||||
|
{
|
||||||
|
wp.InvalidateMeasure();
|
||||||
|
wp.InvalidateArrange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly List<Row> _rows = new List<Row>();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override Size MeasureOverride(Size availableSize)
|
||||||
|
{
|
||||||
|
var childAvailableSize = new Size(
|
||||||
|
availableSize.Width - Padding.Left - Padding.Right,
|
||||||
|
availableSize.Height - Padding.Top - Padding.Bottom);
|
||||||
|
foreach (var child in Children)
|
||||||
|
{
|
||||||
|
child.Measure(childAvailableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
var requiredSize = UpdateRows(availableSize);
|
||||||
|
return requiredSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override Size ArrangeOverride(Size finalSize)
|
||||||
|
{
|
||||||
|
if ((Orientation == Orientation.Horizontal && finalSize.Width < DesiredSize.Width) ||
|
||||||
|
(Orientation == Orientation.Vertical && finalSize.Height < DesiredSize.Height))
|
||||||
|
{
|
||||||
|
// We haven't received our desired size. We need to refresh the rows.
|
||||||
|
UpdateRows(finalSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_rows.Count > 0)
|
||||||
|
{
|
||||||
|
// Now that we have all the data, we do the actual arrange pass
|
||||||
|
var childIndex = 0;
|
||||||
|
foreach (var row in _rows)
|
||||||
|
{
|
||||||
|
foreach (var rect in row.ChildrenRects)
|
||||||
|
{
|
||||||
|
var child = Children[childIndex++];
|
||||||
|
while (child.Visibility == Visibility.Collapsed)
|
||||||
|
{
|
||||||
|
// Collapsed children are not added into the rows,
|
||||||
|
// we skip them.
|
||||||
|
child = Children[childIndex++];
|
||||||
|
}
|
||||||
|
|
||||||
|
var arrangeRect = new UvRect
|
||||||
|
{
|
||||||
|
Position = rect.Position,
|
||||||
|
Size = new UvMeasure { U = rect.Size.U, V = row.Size.V },
|
||||||
|
};
|
||||||
|
|
||||||
|
var finalRect = arrangeRect.ToRect(Orientation);
|
||||||
|
child.Arrange(finalRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Size UpdateRows(Size availableSize)
|
||||||
|
{
|
||||||
|
_rows.Clear();
|
||||||
|
|
||||||
|
var paddingStart = new UvMeasure(Orientation, Padding.Left, Padding.Top);
|
||||||
|
var paddingEnd = new UvMeasure(Orientation, Padding.Right, Padding.Bottom);
|
||||||
|
|
||||||
|
if (Children.Count == 0)
|
||||||
|
{
|
||||||
|
return paddingStart.Add(paddingEnd).ToSize(Orientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
var parentMeasure = new UvMeasure(Orientation, availableSize.Width, availableSize.Height);
|
||||||
|
var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing);
|
||||||
|
var position = new UvMeasure(Orientation, Padding.Left, Padding.Top);
|
||||||
|
|
||||||
|
var currentRow = new Row(new List<UvRect>(), default);
|
||||||
|
var finalMeasure = new UvMeasure(Orientation, width: 0.0, height: 0.0);
|
||||||
|
|
||||||
|
void CommitRow()
|
||||||
|
{
|
||||||
|
// Only adds if the row has a content
|
||||||
|
if (currentRow.ChildrenRects.Count > 0)
|
||||||
|
{
|
||||||
|
_rows.Add(currentRow);
|
||||||
|
|
||||||
|
position.V += currentRow.Size.V + spacingMeasure.V;
|
||||||
|
}
|
||||||
|
|
||||||
|
position.U = paddingStart.U;
|
||||||
|
|
||||||
|
currentRow = new Row(new List<UvRect>(), default);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arrange(UIElement child, bool isLast = false)
|
||||||
|
{
|
||||||
|
if (child.Visibility == Visibility.Collapsed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isFullLine = IsSectionItem(child);
|
||||||
|
var desiredMeasure = new UvMeasure(Orientation, child.DesiredSize);
|
||||||
|
|
||||||
|
if (isFullLine)
|
||||||
|
{
|
||||||
|
if (currentRow.ChildrenRects.Count > 0)
|
||||||
|
{
|
||||||
|
CommitRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forces the width to fill all the available space
|
||||||
|
// (Total width - Padding Left - Padding Right)
|
||||||
|
desiredMeasure.U = parentMeasure.U - paddingStart.U - paddingEnd.U;
|
||||||
|
|
||||||
|
// Adds the Section Header to the row
|
||||||
|
currentRow.Add(position, desiredMeasure);
|
||||||
|
|
||||||
|
// Updates the global measures
|
||||||
|
position.U += desiredMeasure.U + spacingMeasure.U;
|
||||||
|
finalMeasure.U = Math.Max(finalMeasure.U, position.U);
|
||||||
|
|
||||||
|
CommitRow();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Checks if the item can fit in the row
|
||||||
|
if ((desiredMeasure.U + position.U + paddingEnd.U) > parentMeasure.U)
|
||||||
|
{
|
||||||
|
CommitRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLast)
|
||||||
|
{
|
||||||
|
desiredMeasure.U = parentMeasure.U - position.U;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRow.Add(position, desiredMeasure);
|
||||||
|
|
||||||
|
position.U += desiredMeasure.U + spacingMeasure.U;
|
||||||
|
finalMeasure.U = Math.Max(finalMeasure.U, position.U);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastIndex = Children.Count - 1;
|
||||||
|
for (var i = 0; i < lastIndex; i++)
|
||||||
|
{
|
||||||
|
Arrange(Children[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Arrange(Children[lastIndex], StretchChild == StretchChild.Last);
|
||||||
|
|
||||||
|
if (currentRow.ChildrenRects.Count > 0)
|
||||||
|
{
|
||||||
|
_rows.Add(currentRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_rows.Count == 0)
|
||||||
|
{
|
||||||
|
return paddingStart.Add(paddingEnd).ToSize(Orientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastRowRect = _rows.Last().Rect;
|
||||||
|
finalMeasure.V = lastRowRect.Position.V + lastRowRect.Size.V;
|
||||||
|
return finalMeasure.Add(paddingEnd).ToSize(Orientation);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// 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.CommandPalette.Extensions;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.UI;
|
||||||
|
|
||||||
|
public partial class DetailsSizeToGridLengthConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
if (value is ContentSize size)
|
||||||
|
{
|
||||||
|
// This converter calculates the Star width for the LIST.
|
||||||
|
//
|
||||||
|
// The input 'size' (ContentSize) represents the TARGET WIDTH desired for the DETAILS PANEL.
|
||||||
|
//
|
||||||
|
// To ensure the Details Panel achieves its target size (e.g. ContentSize.Large),
|
||||||
|
// we must shrink the List and let the Details fill the available space.
|
||||||
|
// (e.g., A larger target size for Details results in a smaller Star value for the List).
|
||||||
|
var starValue = size switch
|
||||||
|
{
|
||||||
|
ContentSize.Small => 3.0,
|
||||||
|
ContentSize.Medium => 2.0,
|
||||||
|
ContentSize.Large => 1.0,
|
||||||
|
_ => 3.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new GridLength(starValue, GridUnitType.Star);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GridLength(3.0, GridUnitType.Star);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException();
|
||||||
|
}
|
||||||
@@ -18,8 +18,23 @@ internal sealed partial class GridItemTemplateSelector : DataTemplateSelector
|
|||||||
|
|
||||||
public DataTemplate? Gallery { get; set; }
|
public DataTemplate? Gallery { get; set; }
|
||||||
|
|
||||||
|
public DataTemplate? Section { get; set; }
|
||||||
|
|
||||||
|
public DataTemplate? Separator { get; set; }
|
||||||
|
|
||||||
protected override DataTemplate? SelectTemplateCore(object item, DependencyObject dependencyObject)
|
protected override DataTemplate? SelectTemplateCore(object item, DependencyObject dependencyObject)
|
||||||
{
|
{
|
||||||
|
if (item is ListItemViewModel element && element.IsSectionOrSeparator)
|
||||||
|
{
|
||||||
|
if (dependencyObject is UIElement li)
|
||||||
|
{
|
||||||
|
li.IsTabStop = false;
|
||||||
|
li.IsHitTestVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.IsNullOrWhiteSpace(element.Section) ? Separator : Section;
|
||||||
|
}
|
||||||
|
|
||||||
return GridProperties switch
|
return GridProperties switch
|
||||||
{
|
{
|
||||||
SmallGridPropertiesViewModel => Small,
|
SmallGridPropertiesViewModel => Small,
|
||||||
|
|||||||
@@ -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 Microsoft.CmdPal.Core.ViewModels;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace Microsoft.CmdPal.UI;
|
||||||
|
|
||||||
|
public sealed partial class ListItemTemplateSelector : DataTemplateSelector
|
||||||
|
{
|
||||||
|
public DataTemplate? ListItem { get; set; }
|
||||||
|
|
||||||
|
public DataTemplate? Separator { get; set; }
|
||||||
|
|
||||||
|
public DataTemplate? Section { get; set; }
|
||||||
|
|
||||||
|
protected override DataTemplate? SelectTemplateCore(object item, DependencyObject container)
|
||||||
|
{
|
||||||
|
DataTemplate? dataTemplate = ListItem;
|
||||||
|
|
||||||
|
if (container is ListViewItem listItem)
|
||||||
|
{
|
||||||
|
if (item is ListItemViewModel element)
|
||||||
|
{
|
||||||
|
if (container is ListViewItem li && element.IsSectionOrSeparator)
|
||||||
|
{
|
||||||
|
li.IsEnabled = false;
|
||||||
|
li.AllowFocusWhenDisabled = false;
|
||||||
|
li.AllowFocusOnInteraction = false;
|
||||||
|
li.IsHitTestVisible = false;
|
||||||
|
dataTemplate = string.IsNullOrWhiteSpace(element.Section) ? Separator : Section;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
listItem.IsEnabled = true;
|
||||||
|
listItem.AllowFocusWhenDisabled = true;
|
||||||
|
listItem.AllowFocusOnInteraction = true;
|
||||||
|
listItem.IsHitTestVisible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,8 @@
|
|||||||
<CornerRadius x:Key="MediumGridViewItemCornerRadius">8</CornerRadius>
|
<CornerRadius x:Key="MediumGridViewItemCornerRadius">8</CornerRadius>
|
||||||
|
|
||||||
<Style x:Key="IconGridViewItemStyle" TargetType="GridViewItem">
|
<Style x:Key="IconGridViewItemStyle" TargetType="GridViewItem">
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="GridViewItem">
|
<ControlTemplate TargetType="GridViewItem">
|
||||||
@@ -90,6 +92,8 @@
|
|||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style x:Key="GalleryGridViewItemStyle" TargetType="GridViewItem">
|
<Style x:Key="GalleryGridViewItemStyle" TargetType="GridViewItem">
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="GridViewItem">
|
<ControlTemplate TargetType="GridViewItem">
|
||||||
@@ -168,8 +172,17 @@
|
|||||||
Gallery="{StaticResource GalleryGridItemViewModelTemplate}"
|
Gallery="{StaticResource GalleryGridItemViewModelTemplate}"
|
||||||
GridProperties="{x:Bind ViewModel.GridProperties, Mode=OneWay}"
|
GridProperties="{x:Bind ViewModel.GridProperties, Mode=OneWay}"
|
||||||
Medium="{StaticResource MediumGridItemViewModelTemplate}"
|
Medium="{StaticResource MediumGridItemViewModelTemplate}"
|
||||||
|
Section="{StaticResource ListSectionViewModelTemplate}"
|
||||||
|
Separator="{StaticResource ListSeparatorViewModelTemplate}"
|
||||||
Small="{StaticResource SmallGridItemViewModelTemplate}" />
|
Small="{StaticResource SmallGridItemViewModelTemplate}" />
|
||||||
|
|
||||||
|
<cmdpalUI:ListItemTemplateSelector
|
||||||
|
x:Key="ListItemTemplateSelector"
|
||||||
|
x:DataType="coreViewModels:ListItemViewModel"
|
||||||
|
ListItem="{StaticResource ListItemViewModelTemplate}"
|
||||||
|
Section="{StaticResource ListSectionViewModelTemplate}"
|
||||||
|
Separator="{StaticResource ListSeparatorViewModelTemplate}" />
|
||||||
|
|
||||||
<cmdpalUI:GridItemContainerStyleSelector
|
<cmdpalUI:GridItemContainerStyleSelector
|
||||||
x:Key="GridItemContainerStyleSelector"
|
x:Key="GridItemContainerStyleSelector"
|
||||||
Gallery="{StaticResource GalleryGridViewItemStyle}"
|
Gallery="{StaticResource GalleryGridViewItemStyle}"
|
||||||
@@ -241,12 +254,46 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="ListSeparatorViewModelTemplate" x:DataType="coreViewModels:ListItemViewModel">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Rectangle
|
||||||
|
Grid.Column="1"
|
||||||
|
Height="1"
|
||||||
|
Margin="0,2,0,2"
|
||||||
|
Fill="{ThemeResource DividerStrokeColorDefaultBrush}" />
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="ListSectionViewModelTemplate" x:DataType="coreViewModels:ListItemViewModel">
|
||||||
|
<Grid
|
||||||
|
Margin="0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
cpcontrols:WrapPanel.IsFullLine="True"
|
||||||
|
ColumnSpacing="8"
|
||||||
|
IsTabStop="False"
|
||||||
|
IsTapEnabled="True">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="0"
|
||||||
|
Foreground="{ThemeResource TextFillColorDisabled}"
|
||||||
|
Style="{ThemeResource CaptionTextBlockStyle}"
|
||||||
|
Text="{x:Bind Section}" />
|
||||||
|
<Rectangle
|
||||||
|
Grid.Column="1"
|
||||||
|
Height="1"
|
||||||
|
Fill="{ThemeResource DividerStrokeColorDefaultBrush}" />
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
<!-- Grid item templates for visual grid representation -->
|
<!-- Grid item templates for visual grid representation -->
|
||||||
<DataTemplate x:Key="SmallGridItemViewModelTemplate" x:DataType="coreViewModels:ListItemViewModel">
|
<DataTemplate x:Key="SmallGridItemViewModelTemplate" x:DataType="coreViewModels:ListItemViewModel">
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Width="60"
|
|
||||||
Height="60"
|
|
||||||
Padding="8,16"
|
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
AutomationProperties.Name="{x:Bind Title, Mode=OneWay}"
|
AutomationProperties.Name="{x:Bind Title, Mode=OneWay}"
|
||||||
@@ -265,7 +312,6 @@
|
|||||||
Foreground="{ThemeResource TextFillColorPrimary}"
|
Foreground="{ThemeResource TextFillColorPrimary}"
|
||||||
SourceKey="{x:Bind Icon, Mode=OneWay}"
|
SourceKey="{x:Bind Icon, Mode=OneWay}"
|
||||||
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
|
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
@@ -399,7 +445,7 @@
|
|||||||
IsDoubleTapEnabled="True"
|
IsDoubleTapEnabled="True"
|
||||||
IsItemClickEnabled="True"
|
IsItemClickEnabled="True"
|
||||||
ItemClick="Items_ItemClick"
|
ItemClick="Items_ItemClick"
|
||||||
ItemTemplate="{StaticResource ListItemViewModelTemplate}"
|
ItemTemplateSelector="{StaticResource ListItemTemplateSelector}"
|
||||||
ItemsSource="{x:Bind ViewModel.FilteredItems, Mode=OneWay}"
|
ItemsSource="{x:Bind ViewModel.FilteredItems, Mode=OneWay}"
|
||||||
RightTapped="Items_RightTapped"
|
RightTapped="Items_RightTapped"
|
||||||
SelectionChanged="Items_SelectionChanged">
|
SelectionChanged="Items_SelectionChanged">
|
||||||
@@ -411,7 +457,7 @@
|
|||||||
<controls:Case Value="True">
|
<controls:Case Value="True">
|
||||||
<GridView
|
<GridView
|
||||||
x:Name="ItemsGrid"
|
x:Name="ItemsGrid"
|
||||||
Padding="8"
|
Padding="16,0"
|
||||||
ContextCanceled="Items_OnContextCanceled"
|
ContextCanceled="Items_OnContextCanceled"
|
||||||
ContextRequested="Items_OnContextRequested"
|
ContextRequested="Items_OnContextRequested"
|
||||||
DoubleTapped="Items_DoubleTapped"
|
DoubleTapped="Items_DoubleTapped"
|
||||||
@@ -423,10 +469,14 @@
|
|||||||
ItemsSource="{x:Bind ViewModel.FilteredItems, Mode=OneWay}"
|
ItemsSource="{x:Bind ViewModel.FilteredItems, Mode=OneWay}"
|
||||||
RightTapped="Items_RightTapped"
|
RightTapped="Items_RightTapped"
|
||||||
SelectionChanged="Items_SelectionChanged">
|
SelectionChanged="Items_SelectionChanged">
|
||||||
|
<GridView.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<cpcontrols:WrapPanel HorizontalSpacing="8" Orientation="Horizontal" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</GridView.ItemsPanel>
|
||||||
<GridView.ItemContainerTransitions>
|
<GridView.ItemContainerTransitions>
|
||||||
<TransitionCollection />
|
<TransitionCollection />
|
||||||
</GridView.ItemContainerTransitions>
|
</GridView.ItemContainerTransitions>
|
||||||
<GridView.ItemContainerStyle />
|
|
||||||
</GridView>
|
</GridView>
|
||||||
</controls:Case>
|
</controls:Case>
|
||||||
</controls:SwitchPresenter>
|
</controls:SwitchPresenter>
|
||||||
|
|||||||
@@ -76,12 +76,18 @@ public sealed partial class ListPage : Page,
|
|||||||
|
|
||||||
ViewModel = listViewModel;
|
ViewModel = listViewModel;
|
||||||
|
|
||||||
if (e.NavigationMode == NavigationMode.Back
|
if (e.NavigationMode == NavigationMode.Back)
|
||||||
|| (e.NavigationMode == NavigationMode.New && ItemView.Items.Count > 0))
|
|
||||||
{
|
{
|
||||||
// Upon navigating _back_ to this page, immediately select the
|
// Must dispatch the selection to run at a lower priority; otherwise, GetFirstSelectableIndex
|
||||||
// first item in the list
|
// may return an incorrect index because item containers are not yet rendered.
|
||||||
ItemView.SelectedIndex = 0;
|
_ = DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Low, () =>
|
||||||
|
{
|
||||||
|
var firstUsefulIndex = GetFirstSelectableIndex();
|
||||||
|
if (firstUsefulIndex != -1)
|
||||||
|
{
|
||||||
|
ItemView.SelectedIndex = firstUsefulIndex;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterAll isn't AOT compatible
|
// RegisterAll isn't AOT compatible
|
||||||
@@ -128,6 +134,29 @@ public sealed partial class ListPage : Page,
|
|||||||
GC.Collect();
|
GC.Collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the index of the first item in the list that is not a separator.
|
||||||
|
/// Returns -1 if the list is empty or only contains separators.
|
||||||
|
/// </summary>
|
||||||
|
private int GetFirstSelectableIndex()
|
||||||
|
{
|
||||||
|
var items = ItemView.Items;
|
||||||
|
if (items is null || items.Count == 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
if (!IsSeparator(items[i]))
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS is too aggressive at pruning methods bound in XAML")]
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS is too aggressive at pruning methods bound in XAML")]
|
||||||
private void Items_ItemClick(object sender, ItemClickEventArgs e)
|
private void Items_ItemClick(object sender, ItemClickEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -183,19 +212,33 @@ public sealed partial class ListPage : Page,
|
|||||||
// here, then in Page_ItemsUpdated trying to select that cached item if
|
// here, then in Page_ItemsUpdated trying to select that cached item if
|
||||||
// it's in the list (otherwise, clear the cache), but that seems
|
// it's in the list (otherwise, clear the cache), but that seems
|
||||||
// aggressively BODGY for something that mostly just works today.
|
// aggressively BODGY for something that mostly just works today.
|
||||||
if (ItemView.SelectedItem is not null)
|
if (ItemView.SelectedItem is not null && !IsSeparator(ItemView.SelectedItem))
|
||||||
|
{
|
||||||
|
var items = ItemView.Items;
|
||||||
|
var firstUsefulIndex = GetFirstSelectableIndex();
|
||||||
|
var shouldScroll = false;
|
||||||
|
|
||||||
|
if (e.RemovedItems.Count > 0)
|
||||||
|
{
|
||||||
|
shouldScroll = true;
|
||||||
|
}
|
||||||
|
else if (ItemView.SelectedIndex > firstUsefulIndex)
|
||||||
|
{
|
||||||
|
shouldScroll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldScroll)
|
||||||
{
|
{
|
||||||
ItemView.ScrollIntoView(ItemView.SelectedItem);
|
ItemView.ScrollIntoView(ItemView.SelectedItem);
|
||||||
|
}
|
||||||
|
|
||||||
// Automation notification for screen readers
|
// Automation notification for screen readers
|
||||||
var listViewPeer = Microsoft.UI.Xaml.Automation.Peers.ListViewAutomationPeer.CreatePeerForElement(ItemView);
|
var listViewPeer = Microsoft.UI.Xaml.Automation.Peers.ListViewAutomationPeer.CreatePeerForElement(ItemView);
|
||||||
if (listViewPeer is not null && li is not null)
|
if (listViewPeer is not null && li is not null)
|
||||||
{
|
{
|
||||||
var notificationText = li.Title;
|
|
||||||
|
|
||||||
UIHelper.AnnounceActionForAccessibility(
|
UIHelper.AnnounceActionForAccessibility(
|
||||||
ItemsList,
|
ItemsList,
|
||||||
notificationText,
|
li.Title,
|
||||||
"CommandPaletteSelectedItemChanged");
|
"CommandPaletteSelectedItemChanged");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,14 +314,7 @@ public sealed partial class ListPage : Page,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// For list views, use simple linear navigation
|
// For list views, use simple linear navigation
|
||||||
if (ItemView.SelectedIndex < ItemView.Items.Count - 1)
|
NavigateDown();
|
||||||
{
|
|
||||||
ItemView.SelectedIndex++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ItemView.SelectedIndex = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,15 +327,7 @@ public sealed partial class ListPage : Page,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// For list views, use simple linear navigation
|
NavigateUp();
|
||||||
if (ItemView.SelectedIndex > 0)
|
|
||||||
{
|
|
||||||
ItemView.SelectedIndex--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ItemView.SelectedIndex = ItemView.Items.Count - 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,9 +394,12 @@ public sealed partial class ListPage : Page,
|
|||||||
if (indexes.Value.CurrentIndex != indexes.Value.TargetIndex)
|
if (indexes.Value.CurrentIndex != indexes.Value.TargetIndex)
|
||||||
{
|
{
|
||||||
ItemView.SelectedIndex = indexes.Value.TargetIndex;
|
ItemView.SelectedIndex = indexes.Value.TargetIndex;
|
||||||
|
if (ItemView.SelectedItem is not null)
|
||||||
|
{
|
||||||
ItemView.ScrollIntoView(ItemView.SelectedItem);
|
ItemView.ScrollIntoView(ItemView.SelectedItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Receive(NavigatePageUpCommand message)
|
public void Receive(NavigatePageUpCommand message)
|
||||||
{
|
{
|
||||||
@@ -381,9 +412,12 @@ public sealed partial class ListPage : Page,
|
|||||||
if (indexes.Value.CurrentIndex != indexes.Value.TargetIndex)
|
if (indexes.Value.CurrentIndex != indexes.Value.TargetIndex)
|
||||||
{
|
{
|
||||||
ItemView.SelectedIndex = indexes.Value.TargetIndex;
|
ItemView.SelectedIndex = indexes.Value.TargetIndex;
|
||||||
|
if (ItemView.SelectedItem is not null)
|
||||||
|
{
|
||||||
ItemView.ScrollIntoView(ItemView.SelectedItem);
|
ItemView.ScrollIntoView(ItemView.SelectedItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the item index to target when performing a page up or page down
|
/// Calculates the item index to target when performing a page up or page down
|
||||||
@@ -524,17 +558,65 @@ public sealed partial class ListPage : Page,
|
|||||||
// ItemView_SelectionChanged again to give us another chance to change
|
// ItemView_SelectionChanged again to give us another chance to change
|
||||||
// the selection from null -> something. Better to just update the
|
// the selection from null -> something. Better to just update the
|
||||||
// selection once, at the end of all the updating.
|
// selection once, at the end of all the updating.
|
||||||
if (ItemView.SelectedItem is null)
|
// The selection logic must be deferred to the DispatcherQueue
|
||||||
|
// to ensure the UI has processed the updated ItemsSource binding,
|
||||||
|
// preventing ItemView.Items from appearing empty/null immediately after update.
|
||||||
|
_ = DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Low, () =>
|
||||||
{
|
{
|
||||||
ItemView.SelectedIndex = 0;
|
var items = ItemView.Items;
|
||||||
|
|
||||||
|
// If the list is null or empty, clears the selection and return
|
||||||
|
if (items is null || items.Count == 0)
|
||||||
|
{
|
||||||
|
ItemView.SelectedIndex = -1;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always reset the selected item when the top-level list page changes
|
// Finds the first item that is not a separator
|
||||||
// its items
|
var firstUsefulIndex = GetFirstSelectableIndex();
|
||||||
|
|
||||||
|
// If there is only separators in the list, don't select anything.
|
||||||
|
if (firstUsefulIndex == -1)
|
||||||
|
{
|
||||||
|
ItemView.SelectedIndex = -1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var shouldUpdateSelection = false;
|
||||||
|
|
||||||
|
// If it's a top level list update we force the reset to the top useful item
|
||||||
if (!sender.IsNested)
|
if (!sender.IsNested)
|
||||||
{
|
{
|
||||||
ItemView.SelectedIndex = 0;
|
shouldUpdateSelection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No current selection or current selection is null
|
||||||
|
else if (ItemView.SelectedItem is null)
|
||||||
|
{
|
||||||
|
shouldUpdateSelection = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The current selected item is a separator
|
||||||
|
else if (IsSeparator(ItemView.SelectedItem))
|
||||||
|
{
|
||||||
|
shouldUpdateSelection = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The selected item does not exist in the new list
|
||||||
|
else if (!items.Contains(ItemView.SelectedItem))
|
||||||
|
{
|
||||||
|
shouldUpdateSelection = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldUpdateSelection)
|
||||||
|
{
|
||||||
|
if (firstUsefulIndex != -1)
|
||||||
|
{
|
||||||
|
ItemView.SelectedIndex = firstUsefulIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
private void ViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
@@ -604,6 +686,11 @@ public sealed partial class ListPage : Page,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsSeparator(ItemView.Items[i]))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (ItemView.ContainerFromIndex(i) is FrameworkElement c && c.ActualWidth > 0 && c.ActualHeight > 0)
|
if (ItemView.ContainerFromIndex(i) is FrameworkElement c && c.ActualWidth > 0 && c.ActualHeight > 0)
|
||||||
{
|
{
|
||||||
var p = c.TransformToVisual(ItemView).TransformPoint(new Point(0, 0));
|
var p = c.TransformToVisual(ItemView).TransformPoint(new Point(0, 0));
|
||||||
@@ -764,6 +851,102 @@ public sealed partial class ListPage : Page,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Code stealed from <see cref="Controls.ContextMenu.NavigateUp"/>
|
||||||
|
/// </summary>
|
||||||
|
private void NavigateUp()
|
||||||
|
{
|
||||||
|
var newIndex = ItemView.SelectedIndex;
|
||||||
|
|
||||||
|
if (ItemView.SelectedIndex > 0)
|
||||||
|
{
|
||||||
|
newIndex--;
|
||||||
|
|
||||||
|
while (
|
||||||
|
newIndex >= 0 &&
|
||||||
|
IsSeparator(ItemView.Items[newIndex]) &&
|
||||||
|
newIndex != ItemView.SelectedIndex)
|
||||||
|
{
|
||||||
|
newIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newIndex < 0)
|
||||||
|
{
|
||||||
|
newIndex = ItemView.Items.Count - 1;
|
||||||
|
|
||||||
|
while (
|
||||||
|
newIndex >= 0 &&
|
||||||
|
IsSeparator(ItemView.Items[newIndex]) &&
|
||||||
|
newIndex != ItemView.SelectedIndex)
|
||||||
|
{
|
||||||
|
newIndex--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newIndex = ItemView.Items.Count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemView.SelectedIndex = newIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Code stealed from <see cref="Controls.ContextMenu.NavigateDown"/>
|
||||||
|
/// </summary>
|
||||||
|
private void NavigateDown()
|
||||||
|
{
|
||||||
|
var newIndex = ItemView.SelectedIndex;
|
||||||
|
|
||||||
|
if (ItemView.SelectedIndex == ItemView.Items.Count - 1)
|
||||||
|
{
|
||||||
|
newIndex = 0;
|
||||||
|
while (
|
||||||
|
newIndex < ItemView.Items.Count &&
|
||||||
|
IsSeparator(ItemView.Items[newIndex]))
|
||||||
|
{
|
||||||
|
newIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newIndex >= ItemView.Items.Count)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newIndex++;
|
||||||
|
|
||||||
|
while (
|
||||||
|
newIndex < ItemView.Items.Count &&
|
||||||
|
IsSeparator(ItemView.Items[newIndex]) &&
|
||||||
|
newIndex != ItemView.SelectedIndex)
|
||||||
|
{
|
||||||
|
newIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newIndex >= ItemView.Items.Count)
|
||||||
|
{
|
||||||
|
newIndex = 0;
|
||||||
|
|
||||||
|
while (
|
||||||
|
newIndex < ItemView.Items.Count &&
|
||||||
|
IsSeparator(ItemView.Items[newIndex]) &&
|
||||||
|
newIndex != ItemView.SelectedIndex)
|
||||||
|
{
|
||||||
|
newIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemView.SelectedIndex = newIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Code stealed from <see cref="Controls.ContextMenu.IsSeparator(object)"/>
|
||||||
|
/// </summary>
|
||||||
|
private bool IsSeparator(object? item) => item is ListItemViewModel li && li.IsSectionOrSeparator;
|
||||||
|
|
||||||
private enum InputSource
|
private enum InputSource
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||||
<UseWinRT>true</UseWinRT>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -50,7 +49,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- This lets us actually reference types from Microsoft.Terminal.UI and CmdPalKeyboardService -->
|
<!-- This lets us actually reference types from Microsoft.Terminal.UI -->
|
||||||
<CsWinRTIncludes>Microsoft.Terminal.UI;CmdPalKeyboardService</CsWinRTIncludes>
|
<CsWinRTIncludes>Microsoft.Terminal.UI;CmdPalKeyboardService</CsWinRTIncludes>
|
||||||
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -152,16 +151,12 @@
|
|||||||
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.WindowWalker\Microsoft.CmdPal.Ext.WindowWalker.csproj" />
|
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.WindowWalker\Microsoft.CmdPal.Ext.WindowWalker.csproj" />
|
||||||
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.WinGet\Microsoft.CmdPal.Ext.WinGet.csproj" />
|
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.WinGet\Microsoft.CmdPal.Ext.WinGet.csproj" />
|
||||||
|
|
||||||
<ProjectReference Include="$(ProjectDir)\..\Microsoft.Terminal.UI\Microsoft.Terminal.UI.vcxproj">
|
<ProjectReference Include="..\Microsoft.Terminal.UI\Microsoft.Terminal.UI.vcxproj">
|
||||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
<ReferenceOutputAssembly>True</ReferenceOutputAssembly>
|
||||||
<BuildProject>True</BuildProject>
|
<Private>True</Private>
|
||||||
|
<CopyLocalSatelliteAssemblies>True</CopyLocalSatelliteAssemblies>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<!-- WinRT metadata reference -->
|
|
||||||
<CsWinRTInputs Include="$(OutputPath)\Microsoft.Terminal.UI.winmd" />
|
|
||||||
<!-- Native implementation DLL -->
|
|
||||||
<None Include="$(OutputPath)\Microsoft.Terminal.UI.dll">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<ProjectReference Include="..\CmdPalKeyboardService\CmdPalKeyboardService.vcxproj">
|
<ProjectReference Include="..\CmdPalKeyboardService\CmdPalKeyboardService.vcxproj">
|
||||||
<ReferenceOutputAssembly>True</ReferenceOutputAssembly>
|
<ReferenceOutputAssembly>True</ReferenceOutputAssembly>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
EmptyValue="Collapsed"
|
EmptyValue="Collapsed"
|
||||||
NotEmptyValue="Visible" />
|
NotEmptyValue="Visible" />
|
||||||
|
|
||||||
|
<cmdpalUI:DetailsSizeToGridLengthConverter x:Key="SizeToWidthConverter" />
|
||||||
<cmdpalUI:MessageStateToSeverityConverter x:Key="MessageStateToSeverityConverter" />
|
<cmdpalUI:MessageStateToSeverityConverter x:Key="MessageStateToSeverityConverter" />
|
||||||
|
|
||||||
<cmdpalUI:DetailsDataTemplateSelector
|
<cmdpalUI:DetailsDataTemplateSelector
|
||||||
@@ -370,7 +371,7 @@
|
|||||||
<Grid x:Name="ContentGrid" Grid.Row="1">
|
<Grid x:Name="ContentGrid" Grid.Row="1">
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="3*" />
|
<ColumnDefinition Width="{x:Bind ViewModel.Details.Size, Mode=OneWay, Converter={StaticResource SizeToWidthConverter}}" />
|
||||||
<ColumnDefinition x:Name="DetailsColumn" Width="Auto" />
|
<ColumnDefinition x:Name="DetailsColumn" Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ public sealed partial class SettingsWindow : WindowEx,
|
|||||||
"Extensions" => typeof(ExtensionsPage),
|
"Extensions" => typeof(ExtensionsPage),
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (pageType is not null)
|
if (pageType is not null)
|
||||||
{
|
{
|
||||||
NavFrame.Navigate(pageType);
|
NavFrame.Navigate(pageType);
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ namespace winrt::Microsoft::Terminal::UI::implementation
|
|||||||
// Check if the code point is in the Private Use Area range used by Fluent UI icons.
|
// Check if the code point is in the Private Use Area range used by Fluent UI icons.
|
||||||
[[nodiscard]] constexpr bool _isFluentIconPua(const UChar32 cp) noexcept
|
[[nodiscard]] constexpr bool _isFluentIconPua(const UChar32 cp) noexcept
|
||||||
{
|
{
|
||||||
constexpr UChar32 fluentIconsPrivateUseAreaStart = 0xE700;
|
static constexpr UChar32 _fluentIconsPrivateUseAreaStart = 0xE700;
|
||||||
constexpr UChar32 fluentIconsPrivateUseAreaEnd = 0xF8FF;
|
static constexpr UChar32 _fluentIconsPrivateUseAreaEnd = 0xF8FF;
|
||||||
return cp >= fluentIconsPrivateUseAreaStart && cp <= fluentIconsPrivateUseAreaEnd;
|
return cp >= _fluentIconsPrivateUseAreaStart && cp <= _fluentIconsPrivateUseAreaEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if the given text (as a sequence of UChar code units) is emoji
|
// Determine if the given text (as a sequence of UChar code units) is emoji
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup Label="NuGet">
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
<!-- Tell NuGet this is PackageReference style -->
|
|
||||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
|
||||||
<!-- Tell NuGet we're a native project -->
|
|
||||||
<NuGetTargetMoniker>native,Version=v0.0</NuGetTargetMoniker>
|
|
||||||
<!-- Tell NuGet we target Windows (use your existing WindowsTargetPlatformVersion) -->
|
|
||||||
<NuGetTargetPlatformIdentifier>Windows</NuGetTargetPlatformIdentifier>
|
|
||||||
<NuGetTargetPlatformVersion>$(WindowsTargetPlatformVersion)</NuGetTargetPlatformVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<PathToRoot>..\..\..\..\</PathToRoot>
|
||||||
|
<WasdkNuget>$(PathToRoot)packages\Microsoft.WindowsAppSDK.1.8.250907003</WasdkNuget>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||||
@@ -27,11 +28,6 @@
|
|||||||
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.26100.0</WindowsTargetPlatformVersion>
|
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.26100.0</WindowsTargetPlatformVersion>
|
||||||
<WindowsTargetPlatformMinVersion>10.0.19041.0</WindowsTargetPlatformMinVersion>
|
<WindowsTargetPlatformMinVersion>10.0.19041.0</WindowsTargetPlatformMinVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" GeneratePathProperty="true" />
|
|
||||||
<PackageReference Include="Microsoft.Windows.CppWinRT" GeneratePathProperty="true" />
|
|
||||||
<PackageReference Include="Microsoft.Windows.ImplementationLibrary" GeneratePathProperty="true" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
<ProjectConfiguration Include="Debug|ARM64">
|
<ProjectConfiguration Include="Debug|ARM64">
|
||||||
@@ -51,6 +47,10 @@
|
|||||||
<Platform>x64</Platform>
|
<Platform>x64</Platform>
|
||||||
</ProjectConfiguration>
|
</ProjectConfiguration>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutDir>
|
||||||
|
<IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup Label="Configuration">
|
<PropertyGroup Label="Configuration">
|
||||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
<PlatformToolset>v143</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
@@ -200,11 +200,43 @@
|
|||||||
<Midl Include="FontIconGlyphClassifier.idl" />
|
<Midl Include="FontIconGlyphClassifier.idl" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
<None Include="Microsoft.Terminal.UI.def" />
|
<None Include="Microsoft.Terminal.UI.def" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
|
||||||
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutDir>
|
|
||||||
<IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(MSBuildThisFileDirectory)..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||||
|
<Error Condition="!Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', 'Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
17
src/modules/cmdpal/Microsoft.Terminal.UI/packages.config
Normal file
17
src/modules/cmdpal/Microsoft.Terminal.UI/packages.config
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- The packages.config acts as the global version for all of the NuGet packages contained within. -->
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||||
|
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||||
|
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
|
||||||
|
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
|
||||||
|
</packages>
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.CommandPalette.Extensions;
|
using Microsoft.CommandPalette.Extensions;
|
||||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
|
|
||||||
namespace SamplePagesExtension;
|
namespace SamplePagesExtension;
|
||||||
|
|
||||||
@@ -23,14 +22,34 @@ internal sealed partial class SampleListPageWithDetails : ListPage
|
|||||||
return [
|
return [
|
||||||
new ListItem(new NoOpCommand())
|
new ListItem(new NoOpCommand())
|
||||||
{
|
{
|
||||||
Title = "This page demonstrates Details on ListItems",
|
Title = "Details on ListItems (Small)",
|
||||||
Details = new Details()
|
Details = new Details()
|
||||||
{
|
{
|
||||||
Title = "List Item 1",
|
Title = "This item has default details size",
|
||||||
Body = "Each of these items can have a `Body` formatted with **Markdown**",
|
Body = "Each of these items can have a `Body` formatted with **Markdown**",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ListItem(new NoOpCommand())
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Details on ListItems (Medium)",
|
||||||
|
Details = new Details()
|
||||||
|
{
|
||||||
|
Title = "This item has medium details size",
|
||||||
|
Body = "Each of these items can have a `Body` formatted with **Markdown**",
|
||||||
|
Size = ContentSize.Medium,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Details on ListItems (Large)",
|
||||||
|
Details = new Details()
|
||||||
|
{
|
||||||
|
Title = "This item has large details size",
|
||||||
|
Body = "Each of these items can have a `Body` formatted with **Markdown**",
|
||||||
|
Size = ContentSize.Large,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
{
|
{
|
||||||
Title = "This one has a subtitle too",
|
Title = "This one has a subtitle too",
|
||||||
Subtitle = "Example Subtitle",
|
Subtitle = "Example Subtitle",
|
||||||
@@ -70,11 +89,13 @@ internal sealed partial class SampleListPageWithDetails : ListPage
|
|||||||
new ListItem(new NoOpCommand())
|
new ListItem(new NoOpCommand())
|
||||||
{
|
{
|
||||||
Title = "This one has metadata",
|
Title = "This one has metadata",
|
||||||
|
Subtitle = "And Large Details panel",
|
||||||
Tags = [],
|
Tags = [],
|
||||||
Details = new Details()
|
Details = new Details()
|
||||||
{
|
{
|
||||||
Title = "Metadata Example",
|
Title = "Metadata Example",
|
||||||
Body = "Each of the sections below is some sample metadata",
|
Body = "Each of the sections below is some sample metadata",
|
||||||
|
Size = ContentSize.Large,
|
||||||
Metadata = [
|
Metadata = [
|
||||||
new DetailsElement()
|
new DetailsElement()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// 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.CommandPalette.Extensions;
|
||||||
|
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
|
|
||||||
|
namespace SamplePagesExtension.Pages.SectionsPages;
|
||||||
|
|
||||||
|
internal sealed partial class SampleListPageWithSections : ListPage
|
||||||
|
{
|
||||||
|
public SampleListPageWithSections()
|
||||||
|
{
|
||||||
|
Icon = new IconInfo("\uE7C5");
|
||||||
|
Name = "Sample Gallery List Page";
|
||||||
|
}
|
||||||
|
|
||||||
|
public SampleListPageWithSections(IGridProperties gridProperties)
|
||||||
|
{
|
||||||
|
Icon = new IconInfo("\uE7C5");
|
||||||
|
Name = "Sample Gallery List Page";
|
||||||
|
GridProperties = gridProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IListItem[] GetItems()
|
||||||
|
{
|
||||||
|
var sectionList = new Section("This is a section list", [
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Sample Title",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/RedRectangle.png"),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
var anotherSectionList = new Section("This is another section list", [
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Another Title",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Space.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "More Titles",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Swirls.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Stop With The Titles",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Win-Digital.png"),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
var yesTheresAnother = new Section("There's another", [
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Sample Title",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/RedRectangle.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Another Title",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Swirls.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "More Titles",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Win-Digital.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Stop With The Titles",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/RedRectangle.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Another Title",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Space.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "More Titles",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Swirls.png"),
|
||||||
|
},
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Stop With The Titles",
|
||||||
|
Subtitle = "I don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Win-Digital.png"),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
..sectionList,
|
||||||
|
..anotherSectionList,
|
||||||
|
new Separator(),
|
||||||
|
new ListItem(new NoOpCommand())
|
||||||
|
{
|
||||||
|
Title = "Separators also work",
|
||||||
|
Subtitle = "But I still don't do anything",
|
||||||
|
Icon = IconHelpers.FromRelativePath("Assets/Images/Win-Digital.png"),
|
||||||
|
},
|
||||||
|
..yesTheresAnother
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// 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.CommandPalette.Extensions;
|
||||||
|
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
|
using SamplePagesExtension.Pages.SectionsPages;
|
||||||
|
|
||||||
|
namespace SamplePagesExtension.Pages;
|
||||||
|
|
||||||
|
internal sealed partial class SectionsIndexPage : ListPage
|
||||||
|
{
|
||||||
|
public SectionsIndexPage()
|
||||||
|
{
|
||||||
|
Name = "Sections Index Page";
|
||||||
|
Icon = new IconInfo("\uF168");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IListItem[] GetItems()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new ListItem(new SampleListPageWithSections())
|
||||||
|
{
|
||||||
|
Title = "A list page with sections",
|
||||||
|
},
|
||||||
|
new ListItem(new SampleListPageWithSections(new SmallGridLayout()))
|
||||||
|
{
|
||||||
|
Title = "A small grid page with sections",
|
||||||
|
},
|
||||||
|
new ListItem(new SampleListPageWithSections(new MediumGridLayout()))
|
||||||
|
{
|
||||||
|
Title = "A medium grid page with sections",
|
||||||
|
},
|
||||||
|
new ListItem(new SampleListPageWithSections(new GalleryGridLayout()))
|
||||||
|
{
|
||||||
|
Title = "A Gallery grid page with sections",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,11 @@ public partial class SamplesListPage : ListPage
|
|||||||
Title = "List Page With Details",
|
Title = "List Page With Details",
|
||||||
Subtitle = "A list of items, each with additional details to display",
|
Subtitle = "A list of items, each with additional details to display",
|
||||||
},
|
},
|
||||||
|
new ListItem(new SectionsIndexPage())
|
||||||
|
{
|
||||||
|
Title = "List Pages With Sections",
|
||||||
|
Subtitle = "A list of items, with sections header",
|
||||||
|
},
|
||||||
new ListItem(new SampleUpdatingItemsPage())
|
new ListItem(new SampleUpdatingItemsPage())
|
||||||
{
|
{
|
||||||
Title = "List page with items that change",
|
Title = "List page with items that change",
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
// Copyright (c) Microsoft Corporation
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
using Windows.Foundation.Collections;
|
||||||
|
|
||||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
|
|
||||||
public partial class Details : BaseObservable, IDetails
|
public partial class Details : BaseObservable, IDetails, IExtendedAttributesProvider
|
||||||
{
|
{
|
||||||
public virtual IIconInfo HeroImage
|
public virtual IIconInfo HeroImage
|
||||||
{
|
{
|
||||||
@@ -53,4 +54,21 @@ public partial class Details : BaseObservable, IDetails
|
|||||||
}
|
}
|
||||||
|
|
||||||
= [];
|
= [];
|
||||||
|
|
||||||
|
public virtual ContentSize Size
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
OnPropertyChanged(nameof(Size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
= ContentSize.Small;
|
||||||
|
|
||||||
|
public IDictionary<string, object>? GetProperties() => new ValueSet()
|
||||||
|
{
|
||||||
|
{ "Size", (int)Size },
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// 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.Collections;
|
||||||
|
|
||||||
|
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
|
|
||||||
|
public sealed partial class Section : IEnumerable<IListItem>
|
||||||
|
{
|
||||||
|
public IListItem[] Items { get; set; } = [];
|
||||||
|
|
||||||
|
public string SectionTitle { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
private Separator CreateSectionListItem()
|
||||||
|
{
|
||||||
|
return new Separator(SectionTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Section(string sectionName, IListItem[] items)
|
||||||
|
{
|
||||||
|
SectionTitle = sectionName;
|
||||||
|
var listItems = items.ToList();
|
||||||
|
|
||||||
|
if (listItems.Count > 0)
|
||||||
|
{
|
||||||
|
listItems.Insert(0, CreateSectionListItem());
|
||||||
|
Items = [.. listItems];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Section()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<IListItem> GetEnumerator() => Items.ToList().GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
}
|
||||||
@@ -4,6 +4,40 @@
|
|||||||
|
|
||||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||||
|
|
||||||
public partial class Separator : ISeparatorContextItem, ISeparatorFilterItem
|
public partial class Separator : IListItem, ISeparatorContextItem, ISeparatorFilterItem
|
||||||
{
|
{
|
||||||
|
public Separator(string? title = "")
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
Section = title ?? string.Empty;
|
||||||
|
Command = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDetails? Details => null;
|
||||||
|
|
||||||
|
public string? Section { get; private set; }
|
||||||
|
|
||||||
|
public ITag[]? Tags => null;
|
||||||
|
|
||||||
|
public string? TextToSuggest => null;
|
||||||
|
|
||||||
|
public ICommand? Command { get; private set; }
|
||||||
|
|
||||||
|
public IIconInfo? Icon => null;
|
||||||
|
|
||||||
|
public IContextItem[]? MoreCommands => null;
|
||||||
|
|
||||||
|
public string? Subtitle => null;
|
||||||
|
|
||||||
|
public string? Title
|
||||||
|
{
|
||||||
|
get => Section;
|
||||||
|
set => Section = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Windows.Foundation.TypedEventHandler<object, IPropChangedEventArgs>? PropChanged
|
||||||
|
{
|
||||||
|
add { }
|
||||||
|
remove { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,6 +160,15 @@ namespace Microsoft.CommandPalette.Extensions
|
|||||||
[uuid("6a6dd345-37a3-4a1e-914d-4f658a4d583d")]
|
[uuid("6a6dd345-37a3-4a1e-914d-4f658a4d583d")]
|
||||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||||
interface IDetailsData {}
|
interface IDetailsData {}
|
||||||
|
|
||||||
|
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||||
|
enum ContentSize
|
||||||
|
{
|
||||||
|
Small = 0,
|
||||||
|
Medium = 1,
|
||||||
|
Large = 2,
|
||||||
|
};
|
||||||
|
|
||||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||||
interface IDetailsElement {
|
interface IDetailsElement {
|
||||||
String Key { get; };
|
String Key { get; };
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using FancyZonesEditor.Models;
|
using FancyZonesEditor.Models;
|
||||||
@@ -49,19 +50,16 @@ namespace UITests_FancyZones
|
|||||||
[TestInitialize]
|
[TestInitialize]
|
||||||
public void TestInitialize()
|
public void TestInitialize()
|
||||||
{
|
{
|
||||||
// ClearOpenWindows
|
Session.KillAllProcessesByName("PowerToys");
|
||||||
ClearOpenWindows();
|
ClearOpenWindows();
|
||||||
|
|
||||||
// kill all processes related to FancyZones Editor to ensure a clean state
|
|
||||||
Session.KillAllProcessesByName("PowerToys.FancyZonesEditor");
|
|
||||||
|
|
||||||
AppZoneHistory.DeleteFile();
|
AppZoneHistory.DeleteFile();
|
||||||
this.RestartScopeExe();
|
|
||||||
FancyZonesEditorHelper.Files.Restore();
|
FancyZonesEditorHelper.Files.Restore();
|
||||||
|
|
||||||
// Set a custom layout with 1 subzones and clear app zone history
|
|
||||||
SetupCustomLayouts();
|
SetupCustomLayouts();
|
||||||
|
|
||||||
|
RestartScopeExe("Hosts");
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
|
||||||
// Get the current mouse button setting
|
// Get the current mouse button setting
|
||||||
nonPrimaryMouseButton = SystemInformation.MouseButtonsSwapped ? "Left" : "Right";
|
nonPrimaryMouseButton = SystemInformation.MouseButtonsSwapped ? "Left" : "Right";
|
||||||
|
|
||||||
@@ -72,99 +70,6 @@ namespace UITests_FancyZones
|
|||||||
LaunchFancyZones();
|
LaunchFancyZones();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Test Use Shift key to activate zones while dragging a window in FancyZones Zone Behaviour Settings
|
|
||||||
/// <list type="bullet">
|
|
||||||
/// <item>
|
|
||||||
/// <description>Verifies that holding Shift while dragging shows all zones as expected.</description>
|
|
||||||
/// </item>
|
|
||||||
/// </list>
|
|
||||||
/// </summary>
|
|
||||||
[TestMethod("FancyZones.Settings.TestShowZonesOnShiftDuringDrag")]
|
|
||||||
[TestCategory("FancyZones_Dragging #1")]
|
|
||||||
public void TestShowZonesOnShiftDuringDrag()
|
|
||||||
{
|
|
||||||
string testCaseName = nameof(TestShowZonesOnShiftDuringDrag);
|
|
||||||
Pane dragElement = Find<Pane>(By.Name("Non Client Input Sink Window")); // element to drag
|
|
||||||
var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY);
|
|
||||||
|
|
||||||
var (initialColor, withShiftColor) = RunDragInteractions(
|
|
||||||
preAction: () =>
|
|
||||||
{
|
|
||||||
dragElement.DragAndHold(offSet.Dx, offSet.Dy);
|
|
||||||
},
|
|
||||||
postAction: () =>
|
|
||||||
{
|
|
||||||
Session.PressKey(Key.Shift);
|
|
||||||
Task.Delay(500).Wait();
|
|
||||||
},
|
|
||||||
releaseAction: () =>
|
|
||||||
{
|
|
||||||
Session.ReleaseKey(Key.Shift);
|
|
||||||
Task.Delay(5000).Wait(); // Optional: Wait for a moment to ensure window switch
|
|
||||||
},
|
|
||||||
testCaseName: testCaseName);
|
|
||||||
|
|
||||||
string zoneColorWithoutShift = GetOutWindowPixelColor(30);
|
|
||||||
|
|
||||||
Assert.AreNotEqual(initialColor, withShiftColor, $"[{testCaseName}] Zone display failed.");
|
|
||||||
Assert.IsTrue(
|
|
||||||
withShiftColor == inactivateColor || withShiftColor == highlightColor,
|
|
||||||
$"[{testCaseName}] Zone display failed: withShiftColor was {withShiftColor}, expected {inactivateColor} or {highlightColor}.");
|
|
||||||
Assert.AreEqual(inactivateColor, withShiftColor, $"[{testCaseName}] Zone display failed.");
|
|
||||||
|
|
||||||
Assert.AreEqual(zoneColorWithoutShift, initialColor, $"[{testCaseName}] Zone deactivated failed.");
|
|
||||||
dragElement.ReleaseDrag();
|
|
||||||
|
|
||||||
Clean();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Test dragging a window during Shift key press in FancyZones Zone Behaviour Settings
|
|
||||||
/// <list type="bullet">
|
|
||||||
/// <item>
|
|
||||||
/// <description>Verifies that dragging activates zones as expected.</description>
|
|
||||||
/// </item>
|
|
||||||
/// </list>
|
|
||||||
/// </summary>
|
|
||||||
[TestMethod("FancyZones.Settings.TestShowZonesOnDragDuringShift")]
|
|
||||||
[TestCategory("FancyZones_Dragging #2")]
|
|
||||||
public void TestShowZonesOnDragDuringShift()
|
|
||||||
{
|
|
||||||
string testCaseName = nameof(TestShowZonesOnDragDuringShift);
|
|
||||||
|
|
||||||
var dragElement = Find<Pane>(By.Name("Non Client Input Sink Window"));
|
|
||||||
var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY);
|
|
||||||
|
|
||||||
var (initialColor, withDragColor) = RunDragInteractions(
|
|
||||||
preAction: () =>
|
|
||||||
{
|
|
||||||
dragElement.Drag(offSet.Dx, offSet.Dy);
|
|
||||||
Session.PressKey(Key.Shift);
|
|
||||||
},
|
|
||||||
postAction: () =>
|
|
||||||
{
|
|
||||||
dragElement.DragAndHold(0, 0);
|
|
||||||
Task.Delay(5000).Wait();
|
|
||||||
},
|
|
||||||
releaseAction: () =>
|
|
||||||
{
|
|
||||||
dragElement.ReleaseDrag();
|
|
||||||
Session.ReleaseKey(Key.Shift);
|
|
||||||
},
|
|
||||||
testCaseName: testCaseName);
|
|
||||||
|
|
||||||
Assert.AreNotEqual(initialColor, withDragColor, $"[{testCaseName}] Zone color did not change; zone activation failed.");
|
|
||||||
Assert.AreEqual(highlightColor, withDragColor, $"[{testCaseName}] Zone color did not match the highlight color; activation failed.");
|
|
||||||
|
|
||||||
// double check by app-zone-history.json
|
|
||||||
string appZoneHistoryJson = AppZoneHistory.GetData();
|
|
||||||
string? zoneNumber = ZoneSwitchHelper.GetZoneIndexSetByAppName(powertoysWindowName, appZoneHistoryJson);
|
|
||||||
Assert.IsNull(zoneNumber, $"[{testCaseName}] AppZoneHistory layout was unexpectedly set.");
|
|
||||||
|
|
||||||
Clean();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test toggling zones using a non-primary mouse click during window dragging.
|
/// Test toggling zones using a non-primary mouse click during window dragging.
|
||||||
/// <list type="bullet">
|
/// <list type="bullet">
|
||||||
@@ -178,14 +83,19 @@ namespace UITests_FancyZones
|
|||||||
public void TestToggleZonesWithNonPrimaryMouseClick()
|
public void TestToggleZonesWithNonPrimaryMouseClick()
|
||||||
{
|
{
|
||||||
string testCaseName = nameof(TestToggleZonesWithNonPrimaryMouseClick);
|
string testCaseName = nameof(TestToggleZonesWithNonPrimaryMouseClick);
|
||||||
var dragElement = Find<Pane>(By.Name("Non Client Input Sink Window"));
|
|
||||||
var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY);
|
var windowRect = Session.GetMainWindowRect();
|
||||||
|
int startX = windowRect.Left + 70;
|
||||||
|
int startY = windowRect.Top + 25;
|
||||||
|
int endX = startX + 300;
|
||||||
|
int endY = startY + 300;
|
||||||
|
|
||||||
var (initialColor, withMouseColor) = RunDragInteractions(
|
var (initialColor, withMouseColor) = RunDragInteractions(
|
||||||
preAction: () =>
|
preAction: () =>
|
||||||
{
|
{
|
||||||
// activate zone
|
Session.MoveMouseTo(startX, startY);
|
||||||
dragElement.DragAndHold(offSet.Dx, offSet.Dy);
|
Session.PerformMouseAction(MouseActionType.LeftDown);
|
||||||
|
Session.MoveMouseTo(endX, endY);
|
||||||
},
|
},
|
||||||
postAction: () =>
|
postAction: () =>
|
||||||
{
|
{
|
||||||
@@ -195,7 +105,7 @@ namespace UITests_FancyZones
|
|||||||
},
|
},
|
||||||
releaseAction: () =>
|
releaseAction: () =>
|
||||||
{
|
{
|
||||||
dragElement.ReleaseDrag();
|
Session.PerformMouseAction(MouseActionType.LeftUp);
|
||||||
},
|
},
|
||||||
testCaseName: testCaseName);
|
testCaseName: testCaseName);
|
||||||
|
|
||||||
@@ -204,8 +114,6 @@ namespace UITests_FancyZones
|
|||||||
|
|
||||||
// check the zone color is activated
|
// check the zone color is activated
|
||||||
Assert.AreEqual(highlightColor, initialColor, $"[{testCaseName}] Zone activation failed.");
|
Assert.AreEqual(highlightColor, initialColor, $"[{testCaseName}] Zone activation failed.");
|
||||||
|
|
||||||
Clean();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -221,32 +129,35 @@ namespace UITests_FancyZones
|
|||||||
public void TestShowZonesWhenShiftAndMouseOff()
|
public void TestShowZonesWhenShiftAndMouseOff()
|
||||||
{
|
{
|
||||||
string testCaseName = nameof(TestShowZonesWhenShiftAndMouseOff);
|
string testCaseName = nameof(TestShowZonesWhenShiftAndMouseOff);
|
||||||
Pane dragElement = Find<Pane>(By.Name("Non Client Input Sink Window"));
|
|
||||||
var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY);
|
var windowRect = Session.GetMainWindowRect();
|
||||||
|
int startX = windowRect.Left + 70;
|
||||||
|
int startY = windowRect.Top + 25;
|
||||||
|
int endX = startX + 300;
|
||||||
|
int endY = startY + 300;
|
||||||
|
|
||||||
var (initialColor, withShiftColor) = RunDragInteractions(
|
var (initialColor, withShiftColor) = RunDragInteractions(
|
||||||
preAction: () =>
|
preAction: () =>
|
||||||
{
|
{
|
||||||
// activate zone
|
Session.MoveMouseTo(startX, startY);
|
||||||
dragElement.DragAndHold(offSet.Dx, offSet.Dy);
|
Session.PerformMouseAction(MouseActionType.LeftDown);
|
||||||
|
Session.MoveMouseTo(endX, endY);
|
||||||
},
|
},
|
||||||
postAction: () =>
|
postAction: () =>
|
||||||
{
|
{
|
||||||
// press Shift Key to deactivate zones
|
// press Shift Key to deactivate zones
|
||||||
Session.PressKey(Key.Shift);
|
Session.PressKey(Key.Shift);
|
||||||
Task.Delay(500).Wait();
|
Task.Delay(1000).Wait();
|
||||||
},
|
},
|
||||||
releaseAction: () =>
|
releaseAction: () =>
|
||||||
{
|
{
|
||||||
dragElement.ReleaseDrag();
|
Session.PerformMouseAction(MouseActionType.LeftUp);
|
||||||
Session.ReleaseKey(Key.Shift);
|
Session.ReleaseKey(Key.Shift);
|
||||||
},
|
},
|
||||||
testCaseName: testCaseName);
|
testCaseName: testCaseName);
|
||||||
|
|
||||||
Assert.AreEqual(highlightColor, initialColor, $"[{testCaseName}] Zone activation failed.");
|
Assert.AreEqual(highlightColor, initialColor, $"[{testCaseName}] Zone activation failed.");
|
||||||
Assert.AreNotEqual(highlightColor, withShiftColor, $"[{testCaseName}] Zone deactivation failed.");
|
Assert.AreNotEqual(highlightColor, withShiftColor, $"[{testCaseName}] Zone deactivation failed.");
|
||||||
|
|
||||||
Clean();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -263,12 +174,17 @@ namespace UITests_FancyZones
|
|||||||
{
|
{
|
||||||
string testCaseName = nameof(TestShowZonesWhenShiftAndMouseOn);
|
string testCaseName = nameof(TestShowZonesWhenShiftAndMouseOn);
|
||||||
|
|
||||||
var dragElement = Find<Pane>(By.Name("Non Client Input Sink Window"));
|
var windowRect = Session.GetMainWindowRect();
|
||||||
var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY);
|
int startX = windowRect.Left + 70;
|
||||||
|
int startY = windowRect.Top + 25;
|
||||||
|
int endX = startX + 300;
|
||||||
|
int endY = startY + 300;
|
||||||
var (initialColor, withShiftColor) = RunDragInteractions(
|
var (initialColor, withShiftColor) = RunDragInteractions(
|
||||||
preAction: () =>
|
preAction: () =>
|
||||||
{
|
{
|
||||||
dragElement.DragAndHold(offSet.Dx, offSet.Dy);
|
Session.MoveMouseTo(startX, startY);
|
||||||
|
Session.PerformMouseAction(MouseActionType.LeftDown);
|
||||||
|
Session.MoveMouseTo(endX, endY);
|
||||||
},
|
},
|
||||||
postAction: () =>
|
postAction: () =>
|
||||||
{
|
{
|
||||||
@@ -279,7 +195,7 @@ namespace UITests_FancyZones
|
|||||||
},
|
},
|
||||||
testCaseName: testCaseName);
|
testCaseName: testCaseName);
|
||||||
|
|
||||||
Assert.AreEqual(inactivateColor, withShiftColor, $"[{testCaseName}] show zone failed.");
|
Assert.AreEqual(highlightColor, withShiftColor, $"[{testCaseName}] show zone failed.");
|
||||||
|
|
||||||
Session.PerformMouseAction(
|
Session.PerformMouseAction(
|
||||||
nonPrimaryMouseButton == "Right" ? MouseActionType.RightClick : MouseActionType.LeftClick);
|
nonPrimaryMouseButton == "Right" ? MouseActionType.RightClick : MouseActionType.LeftClick);
|
||||||
@@ -288,9 +204,7 @@ namespace UITests_FancyZones
|
|||||||
Assert.AreEqual(initialColor, zoneColorWithMouse, $"[{nameof(TestShowZonesWhenShiftAndMouseOff)}] Zone deactivate failed.");
|
Assert.AreEqual(initialColor, zoneColorWithMouse, $"[{nameof(TestShowZonesWhenShiftAndMouseOff)}] Zone deactivate failed.");
|
||||||
|
|
||||||
Session.ReleaseKey(Key.Shift);
|
Session.ReleaseKey(Key.Shift);
|
||||||
dragElement.ReleaseDrag();
|
Session.PerformMouseAction(MouseActionType.LeftUp);
|
||||||
|
|
||||||
Clean();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -307,8 +221,6 @@ namespace UITests_FancyZones
|
|||||||
{
|
{
|
||||||
var pixel = GetPixelWhenMakeDraggedWindow();
|
var pixel = GetPixelWhenMakeDraggedWindow();
|
||||||
Assert.AreNotEqual(pixel.PixelInWindow, pixel.TransPixel, $"[{nameof(TestMakeDraggedWindowTransparentOn)}] Window transparency failed.");
|
Assert.AreNotEqual(pixel.PixelInWindow, pixel.TransPixel, $"[{nameof(TestMakeDraggedWindowTransparentOn)}] Window transparency failed.");
|
||||||
|
|
||||||
Clean();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -325,14 +237,103 @@ namespace UITests_FancyZones
|
|||||||
{
|
{
|
||||||
var pixel = GetPixelWhenMakeDraggedWindow();
|
var pixel = GetPixelWhenMakeDraggedWindow();
|
||||||
Assert.AreEqual(pixel.PixelInWindow, pixel.TransPixel, $"[{nameof(TestMakeDraggedWindowTransparentOff)}] Window without transparency failed.");
|
Assert.AreEqual(pixel.PixelInWindow, pixel.TransPixel, $"[{nameof(TestMakeDraggedWindowTransparentOff)}] Window without transparency failed.");
|
||||||
|
|
||||||
Clean();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Clean()
|
/// <summary>
|
||||||
|
/// Test Use Shift key to activate zones while dragging a window in FancyZones Zone Behaviour Settings
|
||||||
|
/// <list type="bullet">
|
||||||
|
/// <item>
|
||||||
|
/// <description>Verifies that holding Shift while dragging shows all zones as expected.</description>
|
||||||
|
/// </item>
|
||||||
|
/// </list>
|
||||||
|
/// </summary>
|
||||||
|
[TestMethod("FancyZones.Settings.TestShowZonesOnShiftDuringDrag")]
|
||||||
|
[TestCategory("FancyZones_Dragging #1")]
|
||||||
|
public void TestShowZonesOnShiftDuringDrag()
|
||||||
{
|
{
|
||||||
// clean app zone history file
|
string testCaseName = nameof(TestShowZonesOnShiftDuringDrag);
|
||||||
AppZoneHistory.DeleteFile();
|
|
||||||
|
var windowRect = Session.GetMainWindowRect();
|
||||||
|
int startX = windowRect.Left + 70;
|
||||||
|
int startY = windowRect.Top + 25;
|
||||||
|
int endX = startX + 300;
|
||||||
|
int endY = startY + 300;
|
||||||
|
|
||||||
|
var (initialColor, withShiftColor) = RunDragInteractions(
|
||||||
|
preAction: () =>
|
||||||
|
{
|
||||||
|
Session.MoveMouseTo(startX, startY);
|
||||||
|
Session.PerformMouseAction(MouseActionType.LeftDown);
|
||||||
|
Session.MoveMouseTo(endX, endY);
|
||||||
|
},
|
||||||
|
postAction: () =>
|
||||||
|
{
|
||||||
|
Session.PressKey(Key.Shift);
|
||||||
|
Task.Delay(500).Wait();
|
||||||
|
},
|
||||||
|
releaseAction: () =>
|
||||||
|
{
|
||||||
|
Session.ReleaseKey(Key.Shift);
|
||||||
|
Task.Delay(1000).Wait(); // Optional: Wait for a moment to ensure window switch
|
||||||
|
},
|
||||||
|
testCaseName: testCaseName);
|
||||||
|
|
||||||
|
string zoneColorWithoutShift = GetOutWindowPixelColor(30);
|
||||||
|
|
||||||
|
Assert.AreNotEqual(initialColor, withShiftColor, $"[{testCaseName}] Zone color did not change; zone activation failed.");
|
||||||
|
Assert.AreEqual(highlightColor, withShiftColor, $"[{testCaseName}] Zone color did not match the highlight color; activation failed.");
|
||||||
|
|
||||||
|
Session.PerformMouseAction(MouseActionType.LeftUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test dragging a window during Shift key press in FancyZones Zone Behaviour Settings
|
||||||
|
/// <list type="bullet">
|
||||||
|
/// <item>
|
||||||
|
/// <description>Verifies that dragging activates zones as expected.</description>
|
||||||
|
/// </item>
|
||||||
|
/// </list>
|
||||||
|
/// </summary>
|
||||||
|
[TestMethod("FancyZones.Settings.TestShowZonesOnDragDuringShift")]
|
||||||
|
[TestCategory("FancyZones_Dragging #2")]
|
||||||
|
public void TestShowZonesOnDragDuringShift()
|
||||||
|
{
|
||||||
|
string testCaseName = nameof(TestShowZonesOnDragDuringShift);
|
||||||
|
|
||||||
|
var windowRect = Session.GetMainWindowRect();
|
||||||
|
int startX = windowRect.Left + 70;
|
||||||
|
int startY = windowRect.Top + 25;
|
||||||
|
int endX = startX + 300;
|
||||||
|
int endY = startY + 300;
|
||||||
|
|
||||||
|
var (initialColor, withDragColor) = RunDragInteractions(
|
||||||
|
preAction: () =>
|
||||||
|
{
|
||||||
|
Session.PressKey(Key.Shift);
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
},
|
||||||
|
postAction: () =>
|
||||||
|
{
|
||||||
|
Session.MoveMouseTo(startX, startY);
|
||||||
|
Session.PerformMouseAction(MouseActionType.LeftDown);
|
||||||
|
Session.MoveMouseTo(endX, endY);
|
||||||
|
Task.Delay(1000).Wait();
|
||||||
|
},
|
||||||
|
releaseAction: () =>
|
||||||
|
{
|
||||||
|
Session.PerformMouseAction(MouseActionType.LeftUp);
|
||||||
|
Session.ReleaseKey(Key.Shift);
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
},
|
||||||
|
testCaseName: testCaseName);
|
||||||
|
|
||||||
|
Assert.AreNotEqual(initialColor, withDragColor, $"[{testCaseName}] Zone color did not change; zone activation failed.");
|
||||||
|
Assert.AreEqual(highlightColor, withDragColor, $"[{testCaseName}] Zone color did not match the highlight color; activation failed.");
|
||||||
|
|
||||||
|
// double check by app-zone-history.json
|
||||||
|
string appZoneHistoryJson = AppZoneHistory.GetData();
|
||||||
|
string? zoneNumber = ZoneSwitchHelper.GetZoneIndexSetByAppName(powertoysWindowName, appZoneHistoryJson);
|
||||||
|
Assert.IsNull(zoneNumber, $"[{testCaseName}] AppZoneHistory layout was unexpectedly set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to ensure the desktop has no open windows by clicking the "Show Desktop" button
|
// Helper method to ensure the desktop has no open windows by clicking the "Show Desktop" button
|
||||||
@@ -352,7 +353,7 @@ namespace UITests_FancyZones
|
|||||||
desktopButtonName = "Show Desktop";
|
desktopButtonName = "Show Desktop";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Find<Microsoft.PowerToys.UITest.Button>(By.Name(desktopButtonName), 5000, true).Click(false, 500, 2000);
|
this.Find<Microsoft.PowerToys.UITest.Button>(By.Name(desktopButtonName), 5000, true).Click(false, 500, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup custom layout with 1 subzones
|
// Setup custom layout with 1 subzones
|
||||||
@@ -382,6 +383,11 @@ namespace UITests_FancyZones
|
|||||||
this.Scroll(6, "Down"); // Pull the settings page up to make sure the settings are visible
|
this.Scroll(6, "Down"); // Pull the settings page up to make sure the settings are visible
|
||||||
ZoneBehaviourSettings(TestContext.TestName);
|
ZoneBehaviourSettings(TestContext.TestName);
|
||||||
|
|
||||||
|
// Go back and forth to make sure settings applied
|
||||||
|
this.Find<NavigationViewItem>("Workspaces").Click();
|
||||||
|
Task.Delay(200).Wait();
|
||||||
|
this.Find<NavigationViewItem>("FancyZones").Click();
|
||||||
|
|
||||||
this.Find<Microsoft.PowerToys.UITest.Button>(By.AccessibilityId("LaunchLayoutEditorButton")).Click(false, 500, 10000);
|
this.Find<Microsoft.PowerToys.UITest.Button>(By.AccessibilityId("LaunchLayoutEditorButton")).Click(false, 500, 10000);
|
||||||
this.Session.Attach(PowerToysModule.FancyZone);
|
this.Session.Attach(PowerToysModule.FancyZone);
|
||||||
|
|
||||||
@@ -435,22 +441,26 @@ namespace UITests_FancyZones
|
|||||||
// Get the mouse color of the pixel when make dragged window
|
// Get the mouse color of the pixel when make dragged window
|
||||||
private (string PixelInWindow, string TransPixel) GetPixelWhenMakeDraggedWindow()
|
private (string PixelInWindow, string TransPixel) GetPixelWhenMakeDraggedWindow()
|
||||||
{
|
{
|
||||||
var dragElement = Find<Pane>(By.Name("Non Client Input Sink Window"));
|
var windowRect = Session.GetMainWindowRect();
|
||||||
|
int startX = windowRect.Left + 70;
|
||||||
|
int startY = windowRect.Top + 25;
|
||||||
|
int endX = startX + 100;
|
||||||
|
int endY = startY + 100;
|
||||||
|
|
||||||
// maximize the window to make sure get pixel color more accurate
|
Session.MoveMouseTo(startX, startY);
|
||||||
dragElement.DoubleClick();
|
|
||||||
|
|
||||||
var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY);
|
// Session.PerformMouseAction(MouseActionType.LeftDoubleClick);
|
||||||
Session.PressKey(Key.Shift);
|
Session.PressKey(Key.Shift);
|
||||||
dragElement.DragAndHold(offSet.Dx, offSet.Dy);
|
Session.PerformMouseAction(MouseActionType.LeftDown);
|
||||||
Task.Delay(1000).Wait(); // Optional: Wait for a moment to ensure the window is in position
|
Session.MoveMouseTo(endX, endY);
|
||||||
|
|
||||||
Tuple<int, int> pos = GetMousePosition();
|
Tuple<int, int> pos = GetMousePosition();
|
||||||
string pixelInWindow = this.GetPixelColorString(pos.Item1, pos.Item2);
|
string pixelInWindow = this.GetPixelColorString(pos.Item1, pos.Item2);
|
||||||
Session.ReleaseKey(Key.Shift);
|
Session.ReleaseKey(Key.Shift);
|
||||||
Task.Delay(1000).Wait(); // Optional: Wait for a moment to ensure the window is in position
|
Task.Delay(1000).Wait();
|
||||||
string transPixel = this.GetPixelColorString(pos.Item1, pos.Item2);
|
string transPixel = this.GetPixelColorString(pos.Item1, pos.Item2);
|
||||||
dragElement.ReleaseDrag();
|
|
||||||
|
|
||||||
|
Session.PerformMouseAction(MouseActionType.LeftUp);
|
||||||
return (pixelInWindow, transPixel);
|
return (pixelInWindow, transPixel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ namespace UITests_FancyZones
|
|||||||
};
|
};
|
||||||
FancyZonesEditorHelper.Files.AppliedLayoutsIOHelper.WriteData(appliedLayouts.Serialize(appliedLayoutsWrapper));
|
FancyZonesEditorHelper.Files.AppliedLayoutsIOHelper.WriteData(appliedLayouts.Serialize(appliedLayoutsWrapper));
|
||||||
|
|
||||||
this.RestartScopeExe();
|
RestartScopeExe("Hosts");
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod("FancyZones.Settings.TestApplyHotKey")]
|
[TestMethod("FancyZones.Settings.TestApplyHotKey")]
|
||||||
@@ -598,10 +598,12 @@ namespace UITests_FancyZones
|
|||||||
this.TryReaction();
|
this.TryReaction();
|
||||||
int tries = 24;
|
int tries = 24;
|
||||||
Pull(tries, "down"); // Pull the setting page up to make sure the setting is visible
|
Pull(tries, "down"); // Pull the setting page up to make sure the setting is visible
|
||||||
this.Find<ToggleSwitch>("Enable quick layout switch").Toggle(flag);
|
this.Find<ToggleSwitch>("FancyZonesQuickLayoutSwitch").Toggle(flag);
|
||||||
|
|
||||||
tries = 24;
|
// Go back and forth to make sure settings applied
|
||||||
Pull(tries, "up");
|
this.Find<NavigationViewItem>("Workspaces").Click();
|
||||||
|
Task.Delay(200).Wait();
|
||||||
|
this.Find<NavigationViewItem>("FancyZones").Click();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TryReaction()
|
private void TryReaction()
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace UITests_FancyZones
|
|||||||
Session.KillAllProcessesByName("PowerToys.FancyZonesEditor");
|
Session.KillAllProcessesByName("PowerToys.FancyZonesEditor");
|
||||||
AppZoneHistory.DeleteFile();
|
AppZoneHistory.DeleteFile();
|
||||||
|
|
||||||
this.RestartScopeExe();
|
RestartScopeExe("Hosts");
|
||||||
FancyZonesEditorHelper.Files.Restore();
|
FancyZonesEditorHelper.Files.Restore();
|
||||||
|
|
||||||
// Set a custom layout with 1 subzones and clear app zone history
|
// Set a custom layout with 1 subzones and clear app zone history
|
||||||
@@ -137,7 +137,7 @@ namespace UITests_FancyZones
|
|||||||
Task.Delay(500).Wait(); // Optional: Wait for a moment to ensure window switch
|
Task.Delay(500).Wait(); // Optional: Wait for a moment to ensure window switch
|
||||||
|
|
||||||
activeWindowTitle = ZoneSwitchHelper.GetActiveWindowTitle();
|
activeWindowTitle = ZoneSwitchHelper.GetActiveWindowTitle();
|
||||||
Assert.AreNotEqual(preWindow, activeWindowTitle);
|
Assert.AreEqual(postWindow, activeWindowTitle);
|
||||||
|
|
||||||
Clean(); // close the windows
|
Clean(); // close the windows
|
||||||
}
|
}
|
||||||
@@ -151,9 +151,23 @@ namespace UITests_FancyZones
|
|||||||
|
|
||||||
var rect = Session.GetMainWindowRect();
|
var rect = Session.GetMainWindowRect();
|
||||||
var (targetX, targetY) = ZoneSwitchHelper.GetScreenMargins(rect, 4);
|
var (targetX, targetY) = ZoneSwitchHelper.GetScreenMargins(rect, 4);
|
||||||
var offSet = ZoneSwitchHelper.GetOffset(hostsView, targetX, targetY);
|
|
||||||
|
|
||||||
DragWithShift(hostsView, offSet);
|
// Snap first window (Hosts) to left zone using shift+drag with direct mouse movement
|
||||||
|
var hostsRect = hostsView.Rect ?? throw new InvalidOperationException("Failed to get hosts window rect");
|
||||||
|
int hostsStartX = hostsRect.Left + 70;
|
||||||
|
int hostsStartY = hostsRect.Top + 25;
|
||||||
|
|
||||||
|
// For a 2-column layout, left zone is at approximately 1/4 of screen width
|
||||||
|
int hostsEndX = rect.Left + (3 * (rect.Right - rect.Left) / 4);
|
||||||
|
int hostsEndY = rect.Top + ((rect.Bottom - rect.Top) / 2);
|
||||||
|
|
||||||
|
Session.MoveMouseTo(hostsStartX, hostsStartY);
|
||||||
|
Session.PerformMouseAction(MouseActionType.LeftDown);
|
||||||
|
Session.PressKey(Key.Shift);
|
||||||
|
Session.MoveMouseTo(hostsEndX, hostsEndY);
|
||||||
|
Session.PerformMouseAction(MouseActionType.LeftUp);
|
||||||
|
Session.ReleaseKey(Key.Shift);
|
||||||
|
Task.Delay(500).Wait(); // Wait for snap to complete
|
||||||
|
|
||||||
string preWindow = ZoneSwitchHelper.GetActiveWindowTitle();
|
string preWindow = ZoneSwitchHelper.GetActiveWindowTitle();
|
||||||
|
|
||||||
@@ -163,11 +177,26 @@ namespace UITests_FancyZones
|
|||||||
Pane settingsView = Find<Pane>(By.Name("Non Client Input Sink Window"));
|
Pane settingsView = Find<Pane>(By.Name("Non Client Input Sink Window"));
|
||||||
settingsView.DoubleClick(); // maximize the window
|
settingsView.DoubleClick(); // maximize the window
|
||||||
|
|
||||||
DragWithShift(settingsView, offSet);
|
var windowRect = Session.GetMainWindowRect();
|
||||||
|
var settingsRect = settingsView.Rect ?? throw new InvalidOperationException("Failed to get settings window rect");
|
||||||
|
int settingsStartX = settingsRect.Left + 70;
|
||||||
|
int settingsStartY = settingsRect.Top + 25;
|
||||||
|
|
||||||
|
// For a 2-column layout, right zone is at approximately 3/4 of screen width
|
||||||
|
int settingsEndX = windowRect.Left + (3 * (windowRect.Right - windowRect.Left) / 4);
|
||||||
|
int settingsEndY = windowRect.Top + ((windowRect.Bottom - windowRect.Top) / 2);
|
||||||
|
|
||||||
|
Session.MoveMouseTo(settingsStartX, settingsStartY);
|
||||||
|
Session.PerformMouseAction(MouseActionType.LeftDown);
|
||||||
|
Session.PressKey(Key.Shift);
|
||||||
|
Session.MoveMouseTo(settingsEndX, settingsEndY);
|
||||||
|
Session.PerformMouseAction(MouseActionType.LeftUp);
|
||||||
|
Session.ReleaseKey(Key.Shift);
|
||||||
|
Task.Delay(500).Wait(); // Wait for snap to complete
|
||||||
|
|
||||||
string appZoneHistoryJson = AppZoneHistory.GetData();
|
string appZoneHistoryJson = AppZoneHistory.GetData();
|
||||||
|
|
||||||
string? zoneIndexOfFileWindow = ZoneSwitchHelper.GetZoneIndexSetByAppName("PowerToys.Hosts.exe", appZoneHistoryJson); // explorer.exe
|
string? zoneIndexOfFileWindow = ZoneSwitchHelper.GetZoneIndexSetByAppName("PowerToys.Hosts.exe", appZoneHistoryJson);
|
||||||
string? zoneIndexOfPowertoys = ZoneSwitchHelper.GetZoneIndexSetByAppName("PowerToys.Settings.exe", appZoneHistoryJson);
|
string? zoneIndexOfPowertoys = ZoneSwitchHelper.GetZoneIndexSetByAppName("PowerToys.Settings.exe", appZoneHistoryJson);
|
||||||
|
|
||||||
// check the AppZoneHistory layout is set and in the same zone
|
// check the AppZoneHistory layout is set and in the same zone
|
||||||
@@ -176,16 +205,6 @@ namespace UITests_FancyZones
|
|||||||
return (preWindow, powertoysWindowName);
|
return (preWindow, powertoysWindowName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DragWithShift(Pane settingsView, (int Dx, int Dy) offSet)
|
|
||||||
{
|
|
||||||
Session.PressKey(Key.Shift);
|
|
||||||
settingsView.DragAndHold(offSet.Dx, offSet.Dy);
|
|
||||||
Task.Delay(1000).Wait(); // Wait for drag to start (optional)
|
|
||||||
settingsView.ReleaseDrag();
|
|
||||||
Task.Delay(1000).Wait(); // Wait after drag (optional)
|
|
||||||
Session.ReleaseKey(Key.Shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly CustomLayouts.CustomLayoutListWrapper CustomLayoutsList = new CustomLayouts.CustomLayoutListWrapper
|
private static readonly CustomLayouts.CustomLayoutListWrapper CustomLayoutsList = new CustomLayouts.CustomLayoutListWrapper
|
||||||
{
|
{
|
||||||
CustomLayouts = new List<CustomLayouts.CustomLayoutWrapper>
|
CustomLayouts = new List<CustomLayouts.CustomLayoutWrapper>
|
||||||
@@ -253,11 +272,14 @@ namespace UITests_FancyZones
|
|||||||
this.Scroll(9, "Down"); // Pull the setting page up to make sure the setting is visible
|
this.Scroll(9, "Down"); // Pull the setting page up to make sure the setting is visible
|
||||||
bool switchWindowEnable = TestContext.TestName == "TestSwitchShortCutDisable" ? false : true;
|
bool switchWindowEnable = TestContext.TestName == "TestSwitchShortCutDisable" ? false : true;
|
||||||
|
|
||||||
this.Find<ToggleSwitch>("Switch between windows in the current zone").Toggle(switchWindowEnable);
|
this.Find<ToggleSwitch>("FancyZonesWindowSwitchingToggle").Toggle(switchWindowEnable);
|
||||||
|
|
||||||
Task.Delay(500).Wait(); // Wait for the setting to be applied
|
// Go back and forth to make sure settings applied
|
||||||
this.Scroll(9, "Up"); // Pull the setting page down to make sure the setting is visible
|
this.Find<NavigationViewItem>("Workspaces").Click();
|
||||||
this.Find<Button>("Launch layout editor").Click(false, 500, 5000);
|
Task.Delay(200).Wait();
|
||||||
|
this.Find<NavigationViewItem>("FancyZones").Click();
|
||||||
|
|
||||||
|
this.Find<Button>("Open layout editor").Click(false, 500, 5000);
|
||||||
this.Session.Attach(PowerToysModule.FancyZone);
|
this.Session.Attach(PowerToysModule.FancyZone);
|
||||||
|
|
||||||
// pipeline machine may have an unstable delays, causing the custom layout to be unavailable as we set. then A retry is required.
|
// pipeline machine may have an unstable delays, causing the custom layout to be unavailable as we set. then A retry is required.
|
||||||
@@ -273,7 +295,7 @@ namespace UITests_FancyZones
|
|||||||
this.Find<Microsoft.PowerToys.UITest.Button>("Close").Click();
|
this.Find<Microsoft.PowerToys.UITest.Button>("Close").Click();
|
||||||
this.Session.Attach(PowerToysModule.PowerToysSettings);
|
this.Session.Attach(PowerToysModule.PowerToysSettings);
|
||||||
SetupCustomLayouts();
|
SetupCustomLayouts();
|
||||||
this.Find<Microsoft.PowerToys.UITest.Button>("Launch layout editor").Click(false, 5000, 5000);
|
this.Find<Microsoft.PowerToys.UITest.Button>("Open layout editor").Click(false, 5000, 5000);
|
||||||
this.Session.Attach(PowerToysModule.FancyZone);
|
this.Session.Attach(PowerToysModule.FancyZone);
|
||||||
|
|
||||||
// customLayoutData = FancyZonesEditorHelper.Files.CustomLayoutsIOHelper.GetData();
|
// customLayoutData = FancyZonesEditorHelper.Files.CustomLayoutsIOHelper.GetData();
|
||||||
@@ -301,11 +323,11 @@ namespace UITests_FancyZones
|
|||||||
Task.Delay(1000).Wait();
|
Task.Delay(1000).Wait();
|
||||||
|
|
||||||
this.Find<ToggleSwitch>("Enable Hosts File Editor").Toggle(true);
|
this.Find<ToggleSwitch>("Enable Hosts File Editor").Toggle(true);
|
||||||
this.Find<ToggleSwitch>("Launch as administrator").Toggle(launchAsAdmin);
|
this.Find<ToggleSwitch>("Open as administrator").Toggle(launchAsAdmin);
|
||||||
this.Find<ToggleSwitch>("Show a warning at startup").Toggle(showWarning);
|
this.Find<ToggleSwitch>("Show a warning at startup").Toggle(showWarning);
|
||||||
|
|
||||||
// launch Hosts File Editor
|
// launch Hosts File Editor
|
||||||
this.Find<Button>("Launch Hosts File Editor").Click();
|
this.Find<Button>("Open Hosts File Editor").Click();
|
||||||
|
|
||||||
Task.Delay(5000).Wait();
|
Task.Delay(5000).Wait();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,8 +54,6 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
|
|
||||||
private bool IsPng() => Item.Extension == ".png";
|
private bool IsPng() => Item.Extension == ".png";
|
||||||
|
|
||||||
private bool IsSvg() => Item.Extension == ".svg";
|
|
||||||
|
|
||||||
private bool IsQoi() => Item.Extension == ".qoi";
|
private bool IsQoi() => Item.Extension == ".qoi";
|
||||||
|
|
||||||
private DispatcherQueue Dispatcher { get; }
|
private DispatcherQueue Dispatcher { get; }
|
||||||
@@ -63,7 +61,7 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
private static readonly HashSet<string> _supportedFileTypes =
|
private static readonly HashSet<string> _supportedFileTypes =
|
||||||
BitmapDecoder.GetDecoderInformationEnumerator()
|
BitmapDecoder.GetDecoderInformationEnumerator()
|
||||||
.SelectMany(di => di.FileExtensions)
|
.SelectMany(di => di.FileExtensions)
|
||||||
.Union([".svg", ".qoi"])
|
.Union([".qoi"])
|
||||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
public static bool IsItemSupported(IFileSystemItem item)
|
public static bool IsItemSupported(IFileSystemItem item)
|
||||||
@@ -75,15 +73,7 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
if (IsSvg())
|
if (IsQoi())
|
||||||
{
|
|
||||||
var size = await Task.Run(Item.GetSvgSize);
|
|
||||||
if (size != null)
|
|
||||||
{
|
|
||||||
ImageSize = size.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IsQoi())
|
|
||||||
{
|
{
|
||||||
var size = await Task.Run(Item.GetQoiSize);
|
var size = await Task.Run(Item.GetQoiSize);
|
||||||
if (size != null)
|
if (size != null)
|
||||||
@@ -176,31 +166,16 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
if (IsQoi())
|
||||||
|
{
|
||||||
using FileStream stream = ReadHelper.OpenReadOnly(Item.Path);
|
using FileStream stream = ReadHelper.OpenReadOnly(Item.Path);
|
||||||
|
|
||||||
if (IsSvg())
|
|
||||||
{
|
|
||||||
var source = new SvgImageSource();
|
|
||||||
source.RasterizePixelHeight = ImageSize?.Height ?? 0;
|
|
||||||
source.RasterizePixelWidth = ImageSize?.Width ?? 0;
|
|
||||||
|
|
||||||
var loadStatus = await source.SetSourceAsync(stream.AsRandomAccessStream());
|
|
||||||
if (loadStatus != SvgImageSourceLoadStatus.Success)
|
|
||||||
{
|
|
||||||
Logger.LogError("Error loading SVG: " + loadStatus.ToString());
|
|
||||||
throw new ImageLoadingException(nameof(source));
|
|
||||||
}
|
|
||||||
|
|
||||||
Preview = source;
|
|
||||||
}
|
|
||||||
else if (IsQoi())
|
|
||||||
{
|
|
||||||
using var bitmap = QoiImage.FromStream(stream);
|
using var bitmap = QoiImage.FromStream(stream);
|
||||||
|
|
||||||
Preview = await BitmapHelper.BitmapToImageSource(bitmap, true, cancellationToken);
|
Preview = await BitmapHelper.BitmapToImageSource(bitmap, true, cancellationToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
using FileStream stream = ReadHelper.OpenReadOnly(Item.Path);
|
||||||
Preview = new BitmapImage();
|
Preview = new BitmapImage();
|
||||||
await ((BitmapImage)Preview).SetSourceAsync(stream.AsRandomAccessStream());
|
await ((BitmapImage)Preview).SetSourceAsync(stream.AsRandomAccessStream());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
|
|
||||||
// Markdown
|
// Markdown
|
||||||
".md",
|
".md",
|
||||||
|
|
||||||
|
// SVG - using WebView2 for better compatibility with complex SVGs
|
||||||
|
// (e.g., from Adobe Illustrator, Inkscape)
|
||||||
|
".svg",
|
||||||
};
|
};
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
@@ -111,9 +115,10 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
{
|
{
|
||||||
bool isHtml = File.Extension == ".html" || File.Extension == ".htm";
|
bool isHtml = File.Extension == ".html" || File.Extension == ".htm";
|
||||||
bool isMarkdown = File.Extension == ".md";
|
bool isMarkdown = File.Extension == ".md";
|
||||||
|
bool isSvg = File.Extension == ".svg";
|
||||||
|
|
||||||
bool supportedByMonaco = MonacoHelper.SupportedMonacoFileTypes.Contains(File.Extension);
|
bool supportedByMonaco = MonacoHelper.SupportedMonacoFileTypes.Contains(File.Extension);
|
||||||
bool useMonaco = supportedByMonaco && !isHtml && !isMarkdown;
|
bool useMonaco = supportedByMonaco && !isHtml && !isMarkdown && !isSvg;
|
||||||
|
|
||||||
IsDevFilePreview = supportedByMonaco;
|
IsDevFilePreview = supportedByMonaco;
|
||||||
CustomContextMenu = useMonaco;
|
CustomContextMenu = useMonaco;
|
||||||
@@ -128,6 +133,13 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
var raw = await ReadHelper.Read(File.Path.ToString());
|
var raw = await ReadHelper.Read(File.Path.ToString());
|
||||||
Preview = new Uri(MarkdownHelper.PreviewTempFile(raw, File.Path, TempFolderPath.Path));
|
Preview = new Uri(MarkdownHelper.PreviewTempFile(raw, File.Path, TempFolderPath.Path));
|
||||||
}
|
}
|
||||||
|
else if (isSvg)
|
||||||
|
{
|
||||||
|
// SVG files are rendered directly by WebView2 for better compatibility
|
||||||
|
// with complex SVGs from Adobe Illustrator, Inkscape, etc.
|
||||||
|
IsDevFilePreview = false;
|
||||||
|
Preview = new Uri(File.Path);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Simple html file to preview. Shouldn't do things like enabling scripts or using a virtual mapped directory.
|
// Simple html file to preview. Shouldn't do things like enabling scripts or using a virtual mapped directory.
|
||||||
|
|||||||
@@ -124,9 +124,6 @@ public class PeekFilePreviewTests : UITestBase
|
|||||||
settings["properties"] = properties;
|
settings["properties"] = properties;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Disable all modules except Peek in global settings
|
|
||||||
SettingsConfigHelper.ConfigureGlobalModuleSettings("Peek");
|
|
||||||
|
|
||||||
Debug.WriteLine("Successfully updated all settings - Peek shortcut configured and all modules except Peek disabled");
|
Debug.WriteLine("Successfully updated all settings - Peek shortcut configured and all modules except Peek disabled");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -138,6 +135,7 @@ public class PeekFilePreviewTests : UITestBase
|
|||||||
[TestInitialize]
|
[TestInitialize]
|
||||||
public void TestInitialize()
|
public void TestInitialize()
|
||||||
{
|
{
|
||||||
|
RestartScopeExe("Peek");
|
||||||
Session.CloseMainWindow();
|
Session.CloseMainWindow();
|
||||||
SendKeys(Key.Win, Key.M);
|
SendKeys(Key.Win, Key.M);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup Label="NuGet">
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||||
<!-- Tell NuGet this is PackageReference style -->
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
|
||||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
|
||||||
|
|
||||||
<!-- Tell NuGet we're a native project -->
|
|
||||||
<NuGetTargetMoniker>native,Version=v0.0</NuGetTargetMoniker>
|
|
||||||
|
|
||||||
<!-- Tell NuGet we target Windows (use your existing WindowsTargetPlatformVersion) -->
|
|
||||||
<NuGetTargetPlatformIdentifier>Windows</NuGetTargetPlatformIdentifier>
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||||
<NuGetTargetPlatformVersion>$(WindowsTargetPlatformVersion)</NuGetTargetPlatformVersion>
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.props')" />
|
||||||
</PropertyGroup>
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||||
@@ -35,17 +37,9 @@
|
|||||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||||
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
|
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
|
||||||
<ProjectPriFileName>PowerToys.PowerRename.pri</ProjectPriFileName>
|
<ProjectPriFileName>PowerToys.PowerRename.pri</ProjectPriFileName>
|
||||||
<RuntimeIdentifier>win10-x64;win10-arm64</RuntimeIdentifier>
|
|
||||||
<WindowsAppSDKVerifyTransitiveDependencies>false</WindowsAppSDKVerifyTransitiveDependencies>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" GeneratePathProperty="true" />
|
|
||||||
<PackageReference Include="Microsoft.Windows.CppWinRT" GeneratePathProperty="true" />
|
|
||||||
<PackageReference Include="Microsoft.Windows.ImplementationLibrary" GeneratePathProperty="true" />
|
|
||||||
<PackageReference Include="boost" GeneratePathProperty="true" />
|
|
||||||
<PackageReference Include="boost_regex-vc143" GeneratePathProperty="true" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||||
<PropertyGroup Label="Configuration">
|
<PropertyGroup Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<PlatformToolset>v143</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
@@ -218,7 +212,51 @@
|
|||||||
<ResourceCompile Include="PowerRenameUI.rc" />
|
<ResourceCompile Include="PowerRenameUI.rc" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\boost.1.87.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.87.0\build\boost.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets'))" />
|
||||||
|
</Target>
|
||||||
<Target Name="AddWildCardItems" AfterTargets="BuildGenerateSources">
|
<Target Name="AddWildCardItems" AfterTargets="BuildGenerateSources">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PRIResource Include="@(_WildCardPRIResource)" />
|
<PRIResource Include="@(_WildCardPRIResource)" />
|
||||||
|
|||||||
16
src/runner/packages.config
Normal file
16
src/runner/packages.config
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||||
|
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||||
|
<!-- Windows App SDK and all transitive dependencies -->
|
||||||
|
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
|
||||||
|
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
|
||||||
|
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||||
|
</packages>
|
||||||
@@ -1,34 +1,27 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
|
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
|
||||||
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted ..\..\tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h runner.base.rc runner.rc" />
|
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)\tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h runner.base.rc runner.rc" />
|
||||||
</Target>
|
</Target>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<NoWarn>81010002</NoWarn>
|
<NoWarn>81010002</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Label="NuGet">
|
|
||||||
<!-- Tell NuGet this is PackageReference style -->
|
|
||||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
|
||||||
<!-- Tell NuGet we're a native project -->
|
|
||||||
<NuGetTargetMoniker>native,Version=v0.0</NuGetTargetMoniker>
|
|
||||||
<!-- Tell NuGet we target Windows (use your existing WindowsTargetPlatformVersion) -->
|
|
||||||
<NuGetTargetPlatformIdentifier>Windows</NuGetTargetPlatformIdentifier>
|
|
||||||
<NuGetTargetPlatformVersion>$(WindowsTargetPlatformVersion)</NuGetTargetPlatformVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<VCProjectVersion>15.0</VCProjectVersion>
|
<VCProjectVersion>15.0</VCProjectVersion>
|
||||||
<ProjectGuid>{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}</ProjectGuid>
|
<ProjectGuid>{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}</ProjectGuid>
|
||||||
<RootNamespace>powertoys</RootNamespace>
|
<RootNamespace>powertoys</RootNamespace>
|
||||||
<ProjectName>runner</ProjectName>
|
<ProjectName>runner</ProjectName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" GeneratePathProperty="true" />
|
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK.Foundation" GeneratePathProperty="true" />
|
|
||||||
<PackageReference Include="Microsoft.Windows.CppWinRT" GeneratePathProperty="true" />
|
|
||||||
<PackageReference Include="Microsoft.Windows.ImplementationLibrary" GeneratePathProperty="true" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<Import Project="..\..\deps\expected.props" />
|
<Import Project="..\..\deps\expected.props" />
|
||||||
|
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||||
|
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
|
||||||
|
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
|
||||||
|
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
|
||||||
|
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
|
||||||
|
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
|
||||||
|
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
|
||||||
|
<Import Project="..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
<ImportGroup Label="Shared" />
|
<ImportGroup Label="Shared" />
|
||||||
<PropertyGroup Label="Configuration">
|
<PropertyGroup Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
@@ -38,6 +31,10 @@
|
|||||||
<WindowsAppSdkUndockedRegFreeWinRTInitialize>true</WindowsAppSdkUndockedRegFreeWinRTInitialize>
|
<WindowsAppSdkUndockedRegFreeWinRTInitialize>true</WindowsAppSdkUndockedRegFreeWinRTInitialize>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
<ImportGroup Label="PropertySheets">
|
<ImportGroup Label="PropertySheets">
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
@@ -143,16 +140,39 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<Import Project="..\..\deps\spdlog.props" />
|
<Import Project="..\..\deps\spdlog.props" />
|
||||||
<!-- Deduplicate WindowsAppRuntimeAutoInitializer.cpp (added twice via transitive imports causing LNK4042). Remove all then add exactly once. -->
|
<ImportGroup Label="ExtensionTargets">
|
||||||
<Target Name="FixWinAppSDKAutoInitializer" BeforeTargets="ClCompile" AfterTargets="WindowsAppRuntimeAutoInitializer">
|
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||||
<ItemGroup>
|
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
|
||||||
<!-- Remove ALL injected versions of the file -->
|
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
|
||||||
<ClCompile Remove="@(ClCompile)" Condition="'%(Filename)' == 'WindowsAppRuntimeAutoInitializer'" />
|
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
|
||||||
|
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
|
||||||
<!-- Add ONE copy back manually -->
|
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
|
||||||
<ClCompile Include="$(PkgMicrosoft_WindowsAppSDK_Foundation)\include\WindowsAppRuntimeAutoInitializer.cpp">
|
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
|
||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
<Import Project="$(SolutionDir)packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
|
||||||
</ClCompile>
|
<Import Project="$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
</ItemGroup>
|
<Import Project="$(SolutionDir)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -11,6 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers
|
|||||||
{
|
{
|
||||||
public const string ColorsSettings = "ms-settings:colors";
|
public const string ColorsSettings = "ms-settings:colors";
|
||||||
public const string DiagnosticsAndFeedback = "ms-settings:privacy-feedback";
|
public const string DiagnosticsAndFeedback = "ms-settings:privacy-feedback";
|
||||||
|
public const string NightLightSettings = "ms-settings:nightlight";
|
||||||
|
|
||||||
public static string AnimationsSettings => OSVersionHelper.IsWindows11()
|
public static string AnimationsSettings => OSVersionHelper.IsWindows11()
|
||||||
? "ms-settings:easeofaccess-visualeffects"
|
? "ms-settings:easeofaccess-visualeffects"
|
||||||
|
|||||||
@@ -192,7 +192,10 @@
|
|||||||
x:Uid="FancyZones_WindowSwitching_GroupSettings"
|
x:Uid="FancyZones_WindowSwitching_GroupSettings"
|
||||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||||
IsExpanded="True">
|
IsExpanded="True">
|
||||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.WindowSwitching, Mode=TwoWay}" />
|
<ToggleSwitch
|
||||||
|
x:Uid="ToggleSwitch"
|
||||||
|
AutomationProperties.Name="FancyZonesWindowSwitchingToggle"
|
||||||
|
IsOn="{x:Bind ViewModel.WindowSwitching, Mode=TwoWay}" />
|
||||||
<tkcontrols:SettingsExpander.Items>
|
<tkcontrols:SettingsExpander.Items>
|
||||||
<!-- HACK: For some weird reason, a Shortcut Control is not working correctly if it's the first item in the expander, so we add an invisible card as the first one. -->
|
<!-- HACK: For some weird reason, a Shortcut Control is not working correctly if it's the first item in the expander, so we add an invisible card as the first one. -->
|
||||||
<tkcontrols:SettingsCard Name="FancyZonesWindowSwitchingPlaceholder" Visibility="Collapsed" />
|
<tkcontrols:SettingsCard Name="FancyZonesWindowSwitchingPlaceholder" Visibility="Collapsed" />
|
||||||
@@ -259,7 +262,10 @@
|
|||||||
Name="FancyZonesQuickLayoutSwitch"
|
Name="FancyZonesQuickLayoutSwitch"
|
||||||
x:Uid="FancyZones_QuickLayoutSwitch"
|
x:Uid="FancyZones_QuickLayoutSwitch"
|
||||||
HeaderIcon="{ui:FontIcon Glyph=}">
|
HeaderIcon="{ui:FontIcon Glyph=}">
|
||||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.QuickLayoutSwitch, Mode=TwoWay}" />
|
<ToggleSwitch
|
||||||
|
x:Uid="ToggleSwitch"
|
||||||
|
AutomationProperties.Name="FancyZonesQuickLayoutSwitch"
|
||||||
|
IsOn="{x:Bind ViewModel.QuickLayoutSwitch, Mode=TwoWay}" />
|
||||||
<tkcontrols:SettingsExpander.Items>
|
<tkcontrols:SettingsExpander.Items>
|
||||||
<tkcontrols:SettingsCard
|
<tkcontrols:SettingsCard
|
||||||
Name="FancyZonesFlashZonesOnQuickSwitch"
|
Name="FancyZonesFlashZonesOnQuickSwitch"
|
||||||
|
|||||||
@@ -67,6 +67,10 @@
|
|||||||
x:Uid="LightSwitch_ModeSunsetToSunrise"
|
x:Uid="LightSwitch_ModeSunsetToSunrise"
|
||||||
AutomationProperties.AutomationId="SunCBItem_LightSwitch"
|
AutomationProperties.AutomationId="SunCBItem_LightSwitch"
|
||||||
Tag="SunsetToSunrise" />
|
Tag="SunsetToSunrise" />
|
||||||
|
<ComboBoxItem
|
||||||
|
x:Uid="LightSwitch_ModeFollowNightLight"
|
||||||
|
AutomationProperties.AutomationId="FollowNightLightCBItem_LightSwitch"
|
||||||
|
Tag="FollowNightLight" />
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
<tkcontrols:SettingsExpander.Items>
|
<tkcontrols:SettingsExpander.Items>
|
||||||
<tkcontrols:SettingsCard
|
<tkcontrols:SettingsCard
|
||||||
@@ -152,6 +156,33 @@
|
|||||||
IsOpen="True"
|
IsOpen="True"
|
||||||
Severity="Informational" />
|
Severity="Informational" />
|
||||||
</tkcontrols:SettingsCard>
|
</tkcontrols:SettingsCard>
|
||||||
|
<tkcontrols:SettingsCard
|
||||||
|
x:Name="FollowNightLightCard"
|
||||||
|
Padding="0"
|
||||||
|
HorizontalContentAlignment="Stretch"
|
||||||
|
Background="{ThemeResource InfoBarInformationalSeverityBackgroundBrush}"
|
||||||
|
ContentAlignment="Vertical"
|
||||||
|
Visibility="Collapsed">
|
||||||
|
<InfoBar
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="0"
|
||||||
|
IsClosable="False"
|
||||||
|
IsOpen="True"
|
||||||
|
Severity="Informational">
|
||||||
|
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
|
||||||
|
<TextBlock VerticalAlignment="Center">
|
||||||
|
<Run x:Uid="LightSwitch_FollowNightLightCardMessage" />
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<HyperlinkButton
|
||||||
|
x:Uid="LightSwitch_NightLightSettingsButton"
|
||||||
|
Margin="3,0,0,0"
|
||||||
|
Padding="0"
|
||||||
|
Click="OpenNightLightSettings_Click" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
</InfoBar>
|
||||||
|
</tkcontrols:SettingsCard>
|
||||||
</tkcontrols:SettingsExpander.Items>
|
</tkcontrols:SettingsExpander.Items>
|
||||||
</tkcontrols:SettingsExpander>
|
</tkcontrols:SettingsExpander>
|
||||||
<InfoBar
|
<InfoBar
|
||||||
@@ -350,13 +381,25 @@
|
|||||||
<Setter Target="SunLocation_Card.Visibility" Value="Visible" />
|
<Setter Target="SunLocation_Card.Visibility" Value="Visible" />
|
||||||
<Setter Target="SunOffset_Card.Visibility" Value="Visible" />
|
<Setter Target="SunOffset_Card.Visibility" Value="Visible" />
|
||||||
<Setter Target="NoScheduleCard.Visibility" Value="Collapsed" />
|
<Setter Target="NoScheduleCard.Visibility" Value="Collapsed" />
|
||||||
|
<Setter Target="FollowNightLightCard.Visibility" Value="Collapsed" />
|
||||||
</VisualState.Setters>
|
</VisualState.Setters>
|
||||||
</VisualState>
|
</VisualState>
|
||||||
|
|
||||||
<VisualState x:Name="ManualState">
|
<VisualState x:Name="ManualState">
|
||||||
<VisualState.Setters>
|
<VisualState.Setters>
|
||||||
<Setter Target="Fixed_TurnOnCard.Visibility" Value="Visible" />
|
<Setter Target="Fixed_TurnOnCard.Visibility" Value="Visible" />
|
||||||
<Setter Target="Fixed_TurnOffCard.Visibility" Value="Visible" />
|
<Setter Target="Fixed_TurnOffCard.Visibility" Value="Visible" />
|
||||||
<Setter Target="NoScheduleCard.Visibility" Value="Collapsed" />
|
<Setter Target="NoScheduleCard.Visibility" Value="Collapsed" />
|
||||||
|
<Setter Target="FollowNightLightCard.Visibility" Value="Collapsed" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
|
||||||
|
<VisualState x:Name="FollowNightLightState">
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="Fixed_TurnOnCard.Visibility" Value="Collapsed" />
|
||||||
|
<Setter Target="Fixed_TurnOffCard.Visibility" Value="Collapsed" />
|
||||||
|
<Setter Target="NoScheduleCard.Visibility" Value="Collapsed" />
|
||||||
|
<Setter Target="FollowNightLightCard.Visibility" Value="Visible" />
|
||||||
</VisualState.Setters>
|
</VisualState.Setters>
|
||||||
</VisualState>
|
</VisualState>
|
||||||
</VisualStateGroup>
|
</VisualStateGroup>
|
||||||
|
|||||||
@@ -355,6 +355,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
|||||||
VisualStateManager.GoToState(this, "SunsetToSunriseState", true);
|
VisualStateManager.GoToState(this, "SunsetToSunriseState", true);
|
||||||
this.SunriseModeChartState();
|
this.SunriseModeChartState();
|
||||||
break;
|
break;
|
||||||
|
case "FollowNightLight":
|
||||||
|
VisualStateManager.GoToState(this, "FollowNightLightState", true);
|
||||||
|
TimelineCard.Visibility = Visibility.Collapsed;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
VisualStateManager.GoToState(this, "OffState", true);
|
VisualStateManager.GoToState(this, "OffState", true);
|
||||||
this.TimelineCard.Visibility = Visibility.Collapsed;
|
this.TimelineCard.Visibility = Visibility.Collapsed;
|
||||||
@@ -362,6 +366,18 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OpenNightLightSettings_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Helpers.StartProcessHelper.Start(Helpers.StartProcessHelper.NightLightSettings);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError("Error while trying to open the system night light settings", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SunriseModeChartState()
|
private void SunriseModeChartState()
|
||||||
{
|
{
|
||||||
if (this.ViewModel.Latitude != "0.0" && this.ViewModel.Longitude != "0.0")
|
if (this.ViewModel.Latitude != "0.0" && this.ViewModel.Longitude != "0.0")
|
||||||
|
|||||||
@@ -5760,4 +5760,13 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
|
|||||||
<value>A modern UI built with Fluent Design</value>
|
<value>A modern UI built with Fluent Design</value>
|
||||||
<comment>Fluent Design is a product name, do not loc</comment>
|
<comment>Fluent Design is a product name, do not loc</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="LightSwitch_ModeFollowNightLight.Content" xml:space="preserve">
|
||||||
|
<value>Follow Night Light</value>
|
||||||
|
</data>
|
||||||
|
<data name="LightSwitch_NightLightSettingsButton.Content" xml:space="preserve">
|
||||||
|
<value>Personalize your Night Light settings.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LightSwitch_FollowNightLightCardMessage.Text" xml:space="preserve">
|
||||||
|
<value>Following Night Light settings.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -42,6 +42,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
"Off",
|
"Off",
|
||||||
"FixedHours",
|
"FixedHours",
|
||||||
"SunsetToSunrise",
|
"SunsetToSunrise",
|
||||||
|
"FollowNightLight",
|
||||||
};
|
};
|
||||||
|
|
||||||
_toggleThemeHotkey = _moduleSettings.Properties.ToggleThemeHotkey.Value;
|
_toggleThemeHotkey = _moduleSettings.Properties.ToggleThemeHotkey.Value;
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ if ($Help) {
|
|||||||
Write-Host " -Platform Target platform (default: auto-detect or x64)"
|
Write-Host " -Platform Target platform (default: auto-detect or x64)"
|
||||||
Write-Host " -Configuration Build configuration (default: Release)"
|
Write-Host " -Configuration Build configuration (default: Release)"
|
||||||
Write-Host " -PerUser Build per-user installer (default: true)"
|
Write-Host " -PerUser Build per-user installer (default: true)"
|
||||||
Write-Host " -Version Overwrites the PowerToys version (default: from src\Version.props)"
|
Write-Host " -Version Sets the PowerToys version (default: from src\Version.props)"
|
||||||
Write-Host " -EnableCmdPalAOT Enable AOT compilation for CmdPal (slower build)"
|
Write-Host " -EnableCmdPalAOT Enable AOT compilation for CmdPal (slower build)"
|
||||||
Write-Host " -Clean Clean output directories before building"
|
Write-Host " -Clean Clean output directories before building"
|
||||||
Write-Host " -SkipBuild Skip building the main solution and tools (assumes they are already built)"
|
Write-Host " -SkipBuild Skip building the main solution and tools (assumes they are already built)"
|
||||||
@@ -144,7 +144,8 @@ if ($currentScriptPath.StartsWith($repoRoot)) {
|
|||||||
|
|
||||||
Push-Location $repoRoot
|
Push-Location $repoRoot
|
||||||
try {
|
try {
|
||||||
if (git status --porcelain) {
|
$gitStatus = git status --porcelain
|
||||||
|
if ($gitStatus.Length -gt 0) {
|
||||||
Write-Host "[GIT] Uncommitted changes detected. Stashing (excluding this script)..."
|
Write-Host "[GIT] Uncommitted changes detected. Stashing (excluding this script)..."
|
||||||
$stashCountBefore = (git stash list).Count
|
$stashCountBefore = (git stash list).Count
|
||||||
|
|
||||||
@@ -257,7 +258,7 @@ try {
|
|||||||
$versionPropsPath = Join-Path $repoRoot "src\Version.props"
|
$versionPropsPath = Join-Path $repoRoot "src\Version.props"
|
||||||
[xml]$versionProps = Get-Content $versionPropsPath
|
[xml]$versionProps = Get-Content $versionPropsPath
|
||||||
$ptVersion = $versionProps.Project.PropertyGroup.Version
|
$ptVersion = $versionProps.Project.PropertyGroup.Version
|
||||||
# Directory.Build.props appends .0 to the version for csproj files
|
# Directory.Build.props appends .0 to the version for .csproj files
|
||||||
$ptVersionFull = "$ptVersion.0"
|
$ptVersionFull = "$ptVersion.0"
|
||||||
|
|
||||||
# 2. Build the Generator
|
# 2. Build the Generator
|
||||||
@@ -359,16 +360,6 @@ try {
|
|||||||
RestoreThenBuild 'tools\StylesReportTool\StylesReportTool.sln' $commonArgs $Platform $Configuration
|
RestoreThenBuild 'tools\StylesReportTool\StylesReportTool.sln' $commonArgs $Platform $Configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($Clean) {
|
|
||||||
Write-Host '[CLEAN] installer (keep *.exe)'
|
|
||||||
Push-Location $repoRoot
|
|
||||||
try {
|
|
||||||
git clean -xfd -e '*.exe' -- .\installer\ | Out-Null
|
|
||||||
} finally {
|
|
||||||
Pop-Location
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set NUGET_PACKAGES environment variable if not set, to help wixproj find heat.exe
|
# Set NUGET_PACKAGES environment variable if not set, to help wixproj find heat.exe
|
||||||
if (-not $env:NUGET_PACKAGES) {
|
if (-not $env:NUGET_PACKAGES) {
|
||||||
$env:NUGET_PACKAGES = Join-Path $env:USERPROFILE ".nuget\packages"
|
$env:NUGET_PACKAGES = Join-Path $env:USERPROFILE ".nuget\packages"
|
||||||
|
|||||||
Reference in New Issue
Block a user