Compare commits

..

2 Commits

Author SHA1 Message Date
Gordon Lam (SH)
9d4d1bf383 2nd round 2025-07-04 12:42:39 +08:00
Gordon Lam (SH)
2a051e9c67 first commit 2025-07-04 11:40:34 +08:00
37 changed files with 428 additions and 1786 deletions

View File

@@ -141,7 +141,6 @@ boxmodel
BPBF
bpmf
bpp
breadcrumb
Breadcrumb
Browsable
BROWSEINFO

View File

@@ -11,13 +11,14 @@
"PowerToys.ActionRunner.exe",
"PowerToys.Update.exe",
"PowerToys.BackgroundActivatorDLL.dll",
"Notifications.dll",
"os-detection.dll",
"PowerToys.exe",
"PowerToys.FilePreviewCommon.dll",
"PowerToys.Interop.dll",
"Tools\\PowerToys.BugReportTool.exe",
"StylesReportTool\\PowerToys.StylesReportTool.exe",
"Telemetry.dll",
"CalculatorEngineCommon.dll",
"PowerToys.ManagedTelemetry.dll",
"PowerToys.ManagedCommon.dll",
@@ -32,7 +33,7 @@
"PowerToys.AlwaysOnTopModuleInterface.dll",
"PowerToys.CmdNotFoundModuleInterface.dll",
"PowerToys.CmdNotFound.dll",
"PowerToys.ColorPicker.dll",
"PowerToys.ColorPickerUI.dll",
@@ -53,7 +54,7 @@
"PowerToys.Awake.exe",
"PowerToys.Awake.dll",
"fancyzones.dll",
"PowerToys.FancyZonesEditor.exe",
"PowerToys.FancyZonesEditor.dll",
"PowerToys.FancyZonesEditorCommon.dll",
@@ -135,7 +136,7 @@
"PowerToys.PowerLauncher.dll",
"PowerToys.PowerLauncher.exe",
"PowerToys.PowerLauncher.Telemetry.dll",
"Wox.dll",
"Wox.Infrastructure.dll",
"Wox.Plugin.dll",
"RunPlugins\\Calculator\\Microsoft.PowerToys.Run.Plugin.Calculator.dll",
@@ -274,16 +275,16 @@
"Mono.Cecil.Pdb.dll",
"Mono.Cecil.Rocks.dll",
"Newtonsoft.Json.dll",
"Newtonsoft.Json.Bson.dll",
"NLog.dll",
"HtmlAgilityPack.dll",
"Markdig.Signed.dll",
"HelixToolkit.dll",
"HelixToolkit.Core.Wpf.dll",
"Mages.Core.dll",
"JetBrains.Annotations.dll",
"NLog.Extensions.Logging.dll",
"getfilesiginforedist.dll",
"concrt140_app.dll",
"msvcp140_1_app.dll",
"msvcp140_2_app.dll",
@@ -293,8 +294,22 @@
"vcomp140_app.dll",
"vcruntime140_1_app.dll",
"vcruntime140_app.dll",
"WinUI3Apps\\CommunityToolkit.Labs.WinUI.SettingsControls.dll",
"UnicodeInformation.dll",
"Vanara.Core.dll",
"Vanara.PInvoke.ComCtl32.dll",
"Vanara.PInvoke.Cryptography.dll",
"Vanara.PInvoke.Gdi32.dll",
"Vanara.PInvoke.Kernel32.dll",
"Vanara.PInvoke.Ole.dll",
"Vanara.PInvoke.Rpc.dll",
"Vanara.PInvoke.Security.dll",
"Vanara.PInvoke.Shared.dll",
"Vanara.PInvoke.Shell32.dll",
"Vanara.PInvoke.ShlwApi.dll",
"Vanara.PInvoke.User32.dll",
"WinUI3Apps\\clrcompression.dll",
"WinUI3Apps\\Microsoft.Graphics.Canvas.Interop.dll",
"Microsoft.Web.WebView2.Core.dll",
"Microsoft.Web.WebView2.WinForms.dll",
"Microsoft.Web.WebView2.Wpf.dll",
@@ -321,7 +336,7 @@
"Testably.Abstractions.FileSystem.Interface.dll",
"WinUI3Apps\\Testably.Abstractions.FileSystem.Interface.dll",
"ColorCode.Core.dll",
"ColorCode.UWP.dll",
"UnitsNet.dll",
"UtfUnknown.dll",
"Wpf.Ui.dll"

View File

@@ -11,35 +11,26 @@ namespace Common.UI
{
public enum SettingsWindow
{
Dashboard = 0,
Overview,
AlwaysOnTop,
Overview = 0,
Awake,
ColorPicker,
CmdNotFound,
FancyZones,
FileLocksmith,
Run,
ImageResizer,
KBM,
MouseUtils,
MouseWithoutBorders,
Peek,
PowerAccent,
PowerLauncher,
PowerPreview,
PowerRename,
FileExplorer,
ShortcutGuide,
Hosts,
MeasureTool,
PowerOCR,
Workspaces,
RegistryPreview,
CropAndLock,
EnvironmentVariables,
Dashboard,
AdvancedPaste,
NewPlus,
Workspaces,
CmdPal,
ZoomIt,
}
@@ -48,22 +39,14 @@ namespace Common.UI
{
switch (value)
{
case SettingsWindow.Dashboard:
return "Dashboard";
case SettingsWindow.Overview:
return "Overview";
case SettingsWindow.AlwaysOnTop:
return "AlwaysOnTop";
case SettingsWindow.Awake:
return "Awake";
case SettingsWindow.ColorPicker:
return "ColorPicker";
case SettingsWindow.CmdNotFound:
return "CmdNotFound";
case SettingsWindow.FancyZones:
return "FancyZones";
case SettingsWindow.FileLocksmith:
return "FileLocksmith";
case SettingsWindow.Run:
return "Run";
case SettingsWindow.ImageResizer:
@@ -72,16 +55,6 @@ namespace Common.UI
return "KBM";
case SettingsWindow.MouseUtils:
return "MouseUtils";
case SettingsWindow.MouseWithoutBorders:
return "MouseWithoutBorders";
case SettingsWindow.Peek:
return "Peek";
case SettingsWindow.PowerAccent:
return "PowerAccent";
case SettingsWindow.PowerLauncher:
return "PowerLauncher";
case SettingsWindow.PowerPreview:
return "PowerPreview";
case SettingsWindow.PowerRename:
return "PowerRename";
case SettingsWindow.FileExplorer:
@@ -94,18 +67,18 @@ namespace Common.UI
return "MeasureTool";
case SettingsWindow.PowerOCR:
return "PowerOcr";
case SettingsWindow.Workspaces:
return "Workspaces";
case SettingsWindow.RegistryPreview:
return "RegistryPreview";
case SettingsWindow.CropAndLock:
return "CropAndLock";
case SettingsWindow.EnvironmentVariables:
return "EnvironmentVariables";
case SettingsWindow.Dashboard:
return "Dashboard";
case SettingsWindow.AdvancedPaste:
return "AdvancedPaste";
case SettingsWindow.NewPlus:
return "NewPlus";
case SettingsWindow.Workspaces:
return "Workspaces";
case SettingsWindow.CmdPal:
return "CmdPal";
case SettingsWindow.ZoomIt:

View File

@@ -21,7 +21,7 @@ public static partial class Kernel32
out int pBytesReturned,
IntPtr lpOverlapped);
[LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16, EntryPoint = "CreateFileW")]
[LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
public static partial int CreateFile(
string lpFileName,
FileAccessType dwDesiredAccess,

View File

@@ -4,9 +4,18 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using Windows.Devices.Display.Core;
using Windows.Foundation.Metadata;
using static Microsoft.PowerToys.UITest.UITestBase.NativeMethods;
using static Microsoft.PowerToys.UITest.WindowHelper;
namespace Microsoft.PowerToys.UITest
{

View File

@@ -11,7 +11,7 @@ using System.Threading.Tasks;
namespace Microsoft.PowerToys.UITest
{
public static class WindowHelper
internal static class WindowHelper
{
internal const string AdministratorPrefix = "Administrator: ";

View File

@@ -1,4 +1,4 @@
<Page
<Page
x:Class="WorkspacesEditor.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -172,7 +172,7 @@
VerticalContentAlignment="Stretch"
VerticalScrollBarVisibility="Auto"
Visibility="{Binding IsWorkspacesViewEmpty, Mode=OneWay, Converter={StaticResource BooleanToInvertedVisibilityConverter}, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl x:Name="WorkspacesItemsControl" ItemsSource="{Binding WorkspacesView, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl ItemsSource="{Binding WorkspacesView, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
@@ -235,10 +235,7 @@
Grid.Column="1"
Margin="12,12,12,12"
Orientation="Vertical">
<StackPanel
x:Name="WorkspaceActionGroup"
HorizontalAlignment="Right"
Orientation="Horizontal">
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<Button
x:Name="MoreButton"
HorizontalAlignment="Right"
@@ -251,8 +248,7 @@
</Button>
<Popup
AllowsTransparency="True"
Closed="PopupClosed"
IsOpen="{Binding IsPopupVisible, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsOpen="{Binding IsPopupVisible, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Placement="Left"
PlacementTarget="{Binding ElementName=MoreButton}"
StaysOpen="False">

View File

@@ -4,8 +4,7 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using ManagedCommon;
using WorkspacesEditor.Models;
using WorkspacesEditor.ViewModels;
@@ -43,28 +42,17 @@ namespace WorkspacesEditor
e.Handled = true;
Button button = sender as Button;
Project selectedProject = button.DataContext as Project;
selectedProject.IsPopupVisible = false;
_mainViewModel.DeleteProject(selectedProject);
}
private void MoreButton_Click(object sender, RoutedEventArgs e)
{
_mainViewModel.CloseAllPopups();
e.Handled = true;
Button button = sender as Button;
Project project = button.DataContext as Project;
project.IsPopupVisible = true;
}
private void PopupClosed(object sender, object e)
{
if (sender is Popup p && p.DataContext is Project proj)
{
proj.IsPopupVisible = false;
}
}
private void LaunchButton_Click(object sender, RoutedEventArgs e)
{
e.Handled = true;

View File

@@ -437,11 +437,7 @@ namespace WorkspacesEditor.ViewModels
private void RunSnapshotTool(bool isExistingProjectLaunched)
{
Process process = new Process();
var exeDir = Path.GetDirectoryName(Environment.ProcessPath);
var snapshotUtilsPath = Path.Combine(exeDir, "PowerToys.WorkspacesSnapshotTool.exe");
process.StartInfo = new ProcessStartInfo(snapshotUtilsPath);
process.StartInfo = new ProcessStartInfo(@".\PowerToys.WorkspacesSnapshotTool.exe");
process.StartInfo.CreateNoWindow = true;
process.StartInfo.Arguments = isExistingProjectLaunched ? $"{(int)InvokePoint.LaunchAndEdit}" : string.Empty;

View File

@@ -51,8 +51,6 @@
MouseLeave="AppBorder_MouseLeave">
<Expander
Margin="0,0,20,0"
AutomationProperties.AutomationId="{Binding AppName}"
AutomationProperties.Name="{Binding AppName}"
FlowDirection="RightToLeft"
IsEnabled="{Binding IsIncluded, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
IsExpanded="{Binding IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
@@ -401,11 +399,7 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ItemsControl
x:Name="CapturedAppList"
AutomationProperties.Name="Captured Application List"
ItemTemplateSelector="{StaticResource AppListDataTemplateSelector}"
ItemsSource="{Binding ApplicationsListed, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
<ItemsControl ItemTemplateSelector="{StaticResource AppListDataTemplateSelector}" ItemsSource="{Binding ApplicationsListed, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</StackPanel>
</ScrollViewer>

View File

@@ -1,605 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace WorkspacesEditorUITest;
[TestClass]
[Ignore("not stable")]
public class WorkspacesEditingPageTests : WorkspacesUiAutomationBase
{
public WorkspacesEditingPageTests()
: base()
{
}
[TestMethod("WorkspacesEditingPage.RemoveApp")]
[TestCategory("Workspaces Editing Page UI")]
public void TestRemoveAppFromWorkspace()
{
// Find app list
var appList = Find<Custom>("AppList");
var initialAppCount = appList.FindAll<Custom>(By.ClassName("AppItem")).Count;
if (initialAppCount == 0)
{
Assert.Inconclusive("No apps in workspace to remove");
return;
}
// Remove first app
var firstApp = appList.FindAll<Custom>(By.ClassName("AppItem"))[0];
var appName = firstApp.GetAttribute("Name");
var removeButton = firstApp.Find<Button>("Remove");
removeButton.Click();
Thread.Sleep(500);
// Verify app removed from list
var finalAppCount = appList.FindAll<Custom>(By.ClassName("AppItem")).Count;
Assert.AreEqual(initialAppCount - 1, finalAppCount, "App should be removed from list");
// Verify preview updated
var previewPane = Find<Pane>("Preview");
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
Assert.AreEqual(finalAppCount, windowPreviews.Count, "Preview should show correct number of windows");
}
[TestMethod("WorkspacesEditingPage.RemoveAndAddBackApp")]
[TestCategory("Workspaces Editing Page UI")]
public void TestRemoveAndAddBackApp()
{
// Find app list
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
var appName = firstApp.GetAttribute("Name");
// Remove app
var removeButton = firstApp.Find<Button>("Remove");
removeButton.Click();
Thread.Sleep(500);
// Verify removed app shows in "removed apps" section
Assert.IsTrue(Has<Button>("Add back"), "Should have 'Add back' button for removed apps");
// Add back the app
var addBackButton = Find<Button>("Add back");
addBackButton.Click();
Thread.Sleep(500);
// Verify app is back in the list
var restoredApp = appList.Find<Custom>(By.Name(appName), timeoutMS: 2000);
Assert.IsNotNull(restoredApp, "App should be restored to the list");
// Verify preview updated
var previewPane = Find<Pane>("Preview");
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
var currentAppCount = appList.FindAll<Custom>(By.ClassName("AppItem")).Count;
Assert.AreEqual(currentAppCount, windowPreviews.Count, "Preview should show all windows again");
}
[TestMethod("WorkspacesEditingPage.SetAppMinimized")]
[TestCategory("Workspaces Editing Page UI")]
public void TestSetAppMinimized()
{
// Find first app
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
// Find and toggle minimized checkbox
var minimizedCheckbox = firstApp.Find<CheckBox>("Minimized");
bool wasMinimized = minimizedCheckbox.IsChecked;
minimizedCheckbox.Click();
Thread.Sleep(500);
// Verify state changed
Assert.AreNotEqual(wasMinimized, minimizedCheckbox.IsChecked, "Minimized state should toggle");
// Verify preview reflects the change
var previewPane = Find<Pane>("Preview");
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
// The first window preview should indicate minimized state
if (minimizedCheckbox.IsChecked && windowPreviews.Count > 0)
{
var firstPreview = windowPreviews[0];
var opacity = firstPreview.GetAttribute("Opacity");
// Minimized windows might have reduced opacity or other visual indicator
Assert.IsNotNull(opacity, "Minimized window should have visual indication in preview");
}
}
[TestMethod("WorkspacesEditingPage.SetAppMaximized")]
[TestCategory("Workspaces Editing Page UI")]
public void TestSetAppMaximized()
{
// Find first app
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
// Find and toggle maximized checkbox
var maximizedCheckbox = firstApp.Find<CheckBox>("Maximized");
bool wasMaximized = maximizedCheckbox.IsChecked;
maximizedCheckbox.Click();
Thread.Sleep(500);
// Verify state changed
Assert.AreNotEqual(wasMaximized, maximizedCheckbox.IsChecked, "Maximized state should toggle");
// Verify preview reflects the change
var previewPane = Find<Pane>("Preview");
if (maximizedCheckbox.IsChecked)
{
// Maximized window should fill the preview area
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
if (windowPreviews.Count > 0)
{
var firstPreview = windowPreviews[0];
// Check if preview shows maximized state
var width = firstPreview.GetAttribute("Width");
var height = firstPreview.GetAttribute("Height");
Assert.IsNotNull(width, "Maximized window should have width in preview");
Assert.IsNotNull(height, "Maximized window should have height in preview");
}
}
}
[TestMethod("WorkspacesEditingPage.LaunchAsAdmin")]
[TestCategory("Workspaces Editing Page UI")]
public void TestSetLaunchAsAdmin()
{
// Find app that supports admin launch
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
bool foundAdminCapableApp = false;
foreach (var app in apps)
{
try
{
var adminCheckbox = app.Find<CheckBox>("Launch as admin", timeoutMS: 1000);
if (adminCheckbox != null && adminCheckbox.IsChecked)
{
foundAdminCapableApp = true;
bool wasAdmin = adminCheckbox.IsChecked;
adminCheckbox.Click();
Thread.Sleep(500);
// Verify state changed
Assert.AreNotEqual(wasAdmin, adminCheckbox.IsChecked, "Admin launch state should toggle");
break;
}
}
catch
{
// This app doesn't support admin launch
continue;
}
}
if (!foundAdminCapableApp)
{
Assert.Inconclusive("No apps in workspace support admin launch");
}
}
[TestMethod("WorkspacesEditingPage.AddCLIArgs")]
[TestCategory("Workspaces Editing Page UI")]
public void TestAddCommandLineArguments()
{
// Find first app
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
// Find CLI args textbox
var cliArgsTextBox = firstApp.Find<TextBox>("Command line arguments", timeoutMS: 2000);
if (cliArgsTextBox == null)
{
Assert.Inconclusive("App does not support command line arguments");
return;
}
// Add test arguments
string testArgs = "--test-arg value";
cliArgsTextBox.SetText(testArgs);
Thread.Sleep(500);
// Verify arguments were entered
Assert.AreEqual(testArgs, cliArgsTextBox.Text, "Command line arguments should be set");
}
[TestMethod("WorkspacesEditingPage.ChangeAppPosition")]
[TestCategory("Workspaces Editing Page UI")]
public void TestManuallyChangeAppPosition()
{
// Find first app
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
// Find position controls
var xPositionBox = firstApp.Find<TextBox>("X position", timeoutMS: 2000);
var yPositionBox = firstApp.Find<TextBox>("Y position", timeoutMS: 2000);
if (xPositionBox == null || yPositionBox == null)
{
// Try alternate approach with spinners
var positionSpinners = firstApp.FindAll<Custom>(By.ClassName("SpinBox"));
if (positionSpinners.Count >= 2)
{
xPositionBox = positionSpinners[0].Find<TextBox>(By.ClassName("TextBox"));
yPositionBox = positionSpinners[1].Find<TextBox>(By.ClassName("TextBox"));
}
}
if (xPositionBox != null && yPositionBox != null)
{
// Change position
xPositionBox.SetText("200");
Thread.Sleep(500);
yPositionBox.SetText("150");
Thread.Sleep(500);
// Verify preview updated
var previewPane = Find<Pane>("Preview");
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
Assert.IsTrue(windowPreviews.Count > 0, "Preview should show window at new position");
}
else
{
Assert.Inconclusive("Could not find position controls");
}
}
[TestMethod("WorkspacesEditingPage.ChangeWorkspaceName")]
[TestCategory("Workspaces Editing Page UI")]
public void TestChangeWorkspaceName()
{
// Find workspace name textbox
var nameTextBox = Find<TextBox>("Workspace name");
string originalName = nameTextBox.Text;
// Change name
string newName = "Renamed_Workspace_" + DateTime.Now.Ticks;
nameTextBox.SetText(newName);
Thread.Sleep(500);
// Save changes
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(1000);
// Verify we're back at main list
Assert.IsTrue(Has<Custom>("WorkspacesList"), "Should return to main list after saving");
// Verify workspace was renamed
var workspacesList = Find<Custom>("WorkspacesList");
var renamedWorkspace = workspacesList.Find<Custom>(By.Name(newName), timeoutMS: 2000);
Assert.IsNotNull(renamedWorkspace, "Workspace should be renamed in the list");
}
[TestMethod("WorkspacesEditingPage.SaveAndCancelWork")]
[TestCategory("Workspaces Editing Page UI")]
public void TestSaveAndCancelButtons()
{
// Make a change
var nameTextBox = Find<TextBox>("Workspace name");
string originalName = nameTextBox.Text;
string tempName = originalName + "_temp";
nameTextBox.SetText(tempName);
Thread.Sleep(500);
// Test Cancel button
var cancelButton = Find<Button>("Cancel");
cancelButton.Click();
Thread.Sleep(1000);
// Verify returned to main list without saving
Assert.IsTrue(Has<Custom>("WorkspacesList"), "Should return to main list");
// Go back to editing
var workspacesList = Find<Custom>("WorkspacesList");
var workspace = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
workspace.Click();
Thread.Sleep(1000);
// Verify name wasn't changed
nameTextBox = Find<TextBox>("Workspace name");
Assert.AreEqual(originalName, nameTextBox.Text, "Name should not be changed after cancel");
// Now test Save button
nameTextBox.SetText(tempName);
Thread.Sleep(500);
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(1000);
// Verify saved
workspacesList = Find<Custom>("WorkspacesList");
var savedWorkspace = workspacesList.Find<Custom>(By.Name(tempName), timeoutMS: 2000);
Assert.IsNotNull(savedWorkspace, "Workspace should be saved with new name");
}
[TestMethod("WorkspacesEditingPage.NavigateWithoutSaving")]
[TestCategory("Workspaces Editing Page UI")]
public void TestNavigateToMainPageWithoutSaving()
{
// Make a change
var nameTextBox = Find<TextBox>("Workspace name");
string originalName = nameTextBox.Text;
nameTextBox.SetText(originalName + "_unsaved");
Thread.Sleep(500);
// Click on "Workspaces" navigation/breadcrumb
if (Has<NavigationViewItem>("Workspaces", timeoutMS: 1000))
{
var workspacesNav = Find<NavigationViewItem>("Workspaces");
workspacesNav.Click();
Thread.Sleep(1000);
}
else if (Has<HyperlinkButton>("Workspaces", timeoutMS: 1000))
{
var workspacesBreadcrumb = Find<HyperlinkButton>("Workspaces");
workspacesBreadcrumb.Click();
Thread.Sleep(1000);
}
// If there's a confirmation dialog, handle it
if (Has<Button>("Discard", timeoutMS: 1000))
{
Find<Button>("Discard").Click();
Thread.Sleep(500);
}
// Verify returned to main list
Assert.IsTrue(Has<Custom>("WorkspacesList"), "Should return to main list");
// Verify changes weren't saved
var workspacesList = Find<Custom>("WorkspacesList");
var unsavedWorkspace = workspacesList.Find<Custom>(By.Name(originalName + "_unsaved"), timeoutMS: 1000);
Assert.IsNull(unsavedWorkspace, "Unsaved changes should not persist");
}
[TestMethod("WorkspacesEditingPage.CreateDesktopShortcut")]
[TestCategory("Workspaces Editing Page UI")]
public void TestCreateDesktopShortcut()
{
// Find desktop shortcut checkbox
var shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
// Get desktop path
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
// Get workspace name to check for shortcut
var nameTextBox = Find<TextBox>("Workspace name");
string workspaceName = nameTextBox.Text;
string shortcutPath = Path.Combine(desktopPath, $"{workspaceName}.lnk");
// Clean up any existing shortcut
if (File.Exists(shortcutPath))
{
File.Delete(shortcutPath);
Thread.Sleep(500);
}
// Check the checkbox
if (!shortcutCheckbox.IsChecked)
{
shortcutCheckbox.Click();
Thread.Sleep(500);
}
// Save
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(2000); // Give time for shortcut creation
// Verify shortcut was created
Assert.IsTrue(File.Exists(shortcutPath), "Desktop shortcut should be created");
// Clean up
if (File.Exists(shortcutPath))
{
File.Delete(shortcutPath);
}
}
[TestMethod("WorkspacesEditingPage.DesktopShortcutState")]
[TestCategory("Workspaces Editing Page UI")]
public void TestDesktopShortcutCheckboxState()
{
// Get workspace name
var nameTextBox = Find<TextBox>("Workspace name");
string workspaceName = nameTextBox.Text;
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string shortcutPath = Path.Combine(desktopPath, $"{workspaceName}.lnk");
// Find checkbox
var shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
// Test 1: When shortcut exists
if (!File.Exists(shortcutPath))
{
// Create shortcut first
if (!shortcutCheckbox.IsChecked)
{
shortcutCheckbox.Click();
Thread.Sleep(500);
}
Find<Button>("Save").Click();
Thread.Sleep(2000);
// Navigate back to editing
NavigateToEditingPage();
}
shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
Assert.IsTrue(shortcutCheckbox.IsChecked, "Checkbox should be checked when shortcut exists");
// Test 2: Remove shortcut
if (File.Exists(shortcutPath))
{
File.Delete(shortcutPath);
Thread.Sleep(500);
}
// Re-navigate to refresh state
Find<Button>("Cancel").Click();
Thread.Sleep(1000);
NavigateToEditingPage();
shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
Assert.IsFalse(shortcutCheckbox.IsChecked, "Checkbox should be unchecked when shortcut doesn't exist");
}
[TestMethod("WorkspacesEditingPage.LaunchAndEdit")]
[TestCategory("Workspaces Editing Page UI")]
public void TestLaunchAndEditCapture()
{
// Find Launch and Edit button
var launchEditButton = Find<Button>("Launch and Edit");
launchEditButton.Click();
Thread.Sleep(3000); // Wait for apps to launch
// Open a new application
Process.Start("calc.exe");
Thread.Sleep(2000);
// Click Capture
var captureButton = Find<Button>("Capture");
captureButton.Click();
Thread.Sleep(2000);
// Verify new app was added
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
bool foundCalculator = false;
foreach (var app in apps)
{
var appName = app.GetAttribute("Name");
if (appName.Contains("Calculator", StringComparison.OrdinalIgnoreCase))
{
foundCalculator = true;
break;
}
}
Assert.IsTrue(foundCalculator, "Newly opened Calculator should be captured and added");
// Clean up
foreach (var process in Process.GetProcessesByName("CalculatorApp"))
{
process.Kill();
}
foreach (var process in Process.GetProcessesByName("Calculator"))
{
process.Kill();
}
}
// Helper methods
private void NavigateToEditingPage()
{
// Ensure we have at least one workspace
if (!Has<Custom>("WorkspacesList", timeoutMS: 1000))
{
CreateTestWorkspace();
}
// Click on first workspace to edit
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
if (workspaceItems.Count == 0)
{
CreateTestWorkspace();
workspaceItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
}
workspaceItems[0].Click();
Thread.Sleep(1000);
}
private void CreateTestWorkspace()
{
// Open a test app
Process.Start("notepad.exe");
Thread.Sleep(1000);
// Create workspace
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Thread.Sleep(1000);
// Capture
var captureButton = Find<Button>("Capture");
captureButton.Click();
Thread.Sleep(2000);
// Save with default name
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(1000);
// Close test app
foreach (var process in Process.GetProcessesByName("notepad"))
{
process.Kill();
}
}
}

View File

@@ -8,10 +8,10 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace WorkspacesEditorUITest;
[TestClass]
public class WorkspacesEditorTests : WorkspacesUiAutomationBase
public class WorkspacesEditorTests : UITestBase
{
public WorkspacesEditorTests()
: base()
: base(PowerToysModule.Workspaces, WindowSize.Medium)
{
}
@@ -21,217 +21,4 @@ public class WorkspacesEditorTests : WorkspacesUiAutomationBase
{
Assert.IsTrue(this.Has<Button>("Create Workspace"), "Should have create workspace button");
}
/*
[TestMethod("WorkspacesEditor.Editor.NewWorkspaceAppearsInList")]
[TestCategory("Workspaces UI")]
public void TestNewWorkspaceAppearsInListAfterCapture()
{
// Open test application
OpenNotepad();
Thread.Sleep(2000);
// Create workspace
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Thread.Sleep(1000);
// Capture
var captureButton = Find<Button>("Capture");
captureButton.Click();
Thread.Sleep(2000);
// Save workspace
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(1000);
// Verify workspace appears in list
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
Assert.IsTrue(workspaceItems.Count > 0, "New workspace should appear in the list");
CloseNotepad();
}
[TestMethod("WorkspacesEditor.Editor.CancelCaptureDoesNotAddWorkspace")]
[TestCategory("Workspaces UI")]
public void TestCancelCaptureDoesNotAddWorkspace()
{
// Count existing workspaces
var workspacesList = Find<Custom>("WorkspacesList");
var initialCount = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem")).Count;
// Create workspace
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Thread.Sleep(1000);
// Cancel
var cancelButton = Find<Button>("Cancel");
cancelButton.Click();
Thread.Sleep(1000);
// Verify count hasn't changed
var finalCount = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem")).Count;
Assert.AreEqual(initialCount, finalCount, "Workspace count should not change after canceling");
}
[TestMethod("WorkspacesEditor.Editor.SearchFiltersWorkspaces")]
[TestCategory("Workspaces UI")]
public void TestSearchFiltersWorkspaces()
{
// Create test workspaces first
CreateTestWorkspace("TestWorkspace1");
CreateTestWorkspace("TestWorkspace2");
CreateTestWorkspace("DifferentName");
// Find search box
var searchBox = Find<TextBox>("Search");
searchBox.SetText("TestWorkspace");
Thread.Sleep(1000);
// Verify filtered results
var workspacesList = Find<Custom>("WorkspacesList");
var visibleItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
// Should only show items matching "TestWorkspace"
Assert.IsTrue(visibleItems.Count >= 2, "Should show at least 2 TestWorkspace items");
// Clear search
searchBox.SetText(string.Empty);
Thread.Sleep(500);
}
[TestMethod("WorkspacesEditor.Editor.SortByWorks")]
[TestCategory("Workspaces UI")]
public void TestSortByFunctionality()
{
// Find sort dropdown
var sortDropdown = Find<ComboBox>("SortBy");
sortDropdown.Click();
Thread.Sleep(500);
// Select different sort options
var sortOptions = FindAll<Custom>(By.ClassName("ComboBoxItem"));
if (sortOptions.Count > 1)
{
sortOptions[1].Click(); // Select second option
Thread.Sleep(1000);
// Verify list is updated (we can't easily verify sort order in UI tests)
var workspacesList = Find<Custom>("WorkspacesList");
Assert.IsNotNull(workspacesList, "Workspaces list should still be visible after sorting");
}
}
[TestMethod("WorkspacesEditor.Editor.SortByPersists")]
[TestCategory("Workspaces UI")]
public void TestSortByPersistsAfterRestart()
{
// Set sort option
var sortDropdown = Find<ComboBox>("SortBy");
sortDropdown.Click();
Thread.Sleep(500);
var sortOptions = FindAll<Custom>(By.ClassName("ComboBoxItem"));
string selectedOption = string.Empty;
if (sortOptions.Count > 1)
{
sortOptions[1].Click(); // Select second option
selectedOption = sortDropdown.Text;
Thread.Sleep(1000);
}
// Restart editor
RestartScopeExe();
Thread.Sleep(2000);
// Verify sort option persisted
sortDropdown = Find<ComboBox>("SortBy");
Assert.AreEqual(selectedOption, sortDropdown.Text, "Sort option should persist after restart");
}
[TestMethod("WorkspacesEditor.Editor.RemoveWorkspace")]
[TestCategory("Workspaces UI")]
public void TestRemoveWorkspace()
{
// Create a test workspace
CreateTestWorkspace("WorkspaceToRemove");
// Find the workspace in the list
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.Find<Custom>(By.Name("WorkspaceToRemove"));
// Click remove button
var removeButton = workspaceItem.Find<Button>("Remove");
removeButton.Click();
Thread.Sleep(1000);
// Confirm removal if dialog appears
if (Has<Button>("Yes"))
{
Find<Button>("Yes").Click();
Thread.Sleep(1000);
}
// Verify workspace is removed
Assert.IsFalse(Has(By.Name("WorkspaceToRemove")), "Workspace should be removed from list");
}
[TestMethod("WorkspacesEditor.Editor.EditOpensEditingPage")]
[TestCategory("Workspaces UI")]
public void TestEditOpensEditingPage()
{
// Create a test workspace if none exist
if (!Has<Custom>("WorkspacesList"))
{
CreateTestWorkspace("TestWorkspace");
}
// Find first workspace
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
// Click edit button
var editButton = workspaceItem.Find<Button>("Edit");
editButton.Click();
Thread.Sleep(1000);
// Verify editing page opened
Assert.IsTrue(Has<Button>("Save"), "Should have Save button on editing page");
Assert.IsTrue(Has<Button>("Cancel"), "Should have Cancel button on editing page");
// Go back
Find<Button>("Cancel").Click();
Thread.Sleep(1000);
}
[TestMethod("WorkspacesEditor.Editor.ClickWorkspaceOpensEditingPage")]
[TestCategory("Workspaces UI")]
public void TestClickWorkspaceOpensEditingPage()
{
// Create a test workspace if none exist
if (!Has<Custom>("WorkspacesList"))
{
CreateTestWorkspace("TestWorkspace");
}
// Find first workspace
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
// Click on the workspace item itself
workspaceItem.Click();
Thread.Sleep(1000);
// Verify editing page opened
Assert.IsTrue(Has<Button>("Save"), "Should have Save button on editing page");
Assert.IsTrue(Has<Button>("Cancel"), "Should have Cancel button on editing page");
// Go back
Find<Button>("Cancel").Click();
Thread.Sleep(1000);
}
*/
}

View File

@@ -23,7 +23,6 @@
<ItemGroup>
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
<ProjectReference Include="..\WorkspacesEditor\WorkspacesEditor.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,100 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace WorkspacesEditorUITest;
[TestClass]
[Ignore("NOT STABLE")]
public class WorkspacesLauncherTest : WorkspacesUiAutomationBase
{
public WorkspacesLauncherTest()
: base()
{
}
[TestMethod("WorkspacesEditor.Launcher.LaunchFromEditor")]
[TestCategory("Workspaces UI")]
public void TestLaunchWorkspaceFromEditor()
{
ClearWorkspaces();
var uuid = Guid.NewGuid().ToString("N").Substring(0, 8);
CreateTestWorkspace(uuid);
CloseNotepad();
var launchButton = Find<Button>(By.Name("Launch"));
launchButton.Click();
Task.Delay(2000).Wait();
var processes = System.Diagnostics.Process.GetProcessesByName("notepad");
Assert.IsTrue(processes?.Length > 0);
}
[TestMethod("WorkspacesEditor.Launcher.CancelLaunch")]
[TestCategory("Workspaces UI")]
public void TestCancelLaunch()
{
// Create workspace with multiple apps
CreateWorkspaceWithApps();
// Launch workspace
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
var launchButton = workspaceItem.Find<Button>("Launch");
launchButton.Click();
Thread.Sleep(1000);
// Cancel launch
if (Has<Button>("Cancel launch"))
{
Find<Button>("Cancel launch").Click();
Thread.Sleep(1000);
// Verify launcher closed
Assert.IsFalse(Has<Window>("Workspaces Launcher"), "Launcher window should close after cancel");
}
// Close any apps that may have launched
CloseTestApplications();
}
[TestMethod("WorkspacesEditor.Launcher.DismissKeepsLaunching")]
[TestCategory("Workspaces UI")]
public void TestDismissKeepsAppsLaunching()
{
// Create workspace with apps
CreateWorkspaceWithApps();
// Launch workspace
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
var launchButton = workspaceItem.Find<Button>("Launch");
launchButton.Click();
Thread.Sleep(1000);
// Dismiss launcher
if (Has<Button>("Dismiss"))
{
Find<Button>("Dismiss").Click();
Thread.Sleep(1000);
// Verify launcher closed but apps continue launching
Assert.IsFalse(Has<Window>("Workspaces Launcher"), "Launcher window should close after dismiss");
// Wait for apps to finish launching
Thread.Sleep(3000);
// Verify apps launched (notepad should be open)
Assert.IsTrue(WindowHelper.IsWindowOpen("Notepad"), "Apps should continue launching after dismiss");
}
// Close launched apps
CloseTestApplications();
}
}

View File

@@ -1,195 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace WorkspacesEditorUITest;
[TestClass]
public class WorkspacesSettingsTests : UITestBase
{
public WorkspacesSettingsTests()
: base(PowerToysModule.PowerToysSettings, WindowSize.Medium)
{
}
[TestMethod("WorkspacesSettings.LaunchFromSettings")]
[TestCategory("Workspaces Settings UI")]
public void TestLaunchEditorFromSettingsPage()
{
GoToSettingsPageAndEnable();
}
[TestMethod("WorkspacesSettings.ActivationShortcut")]
[TestCategory("Workspaces Settings UI")]
public void TestActivationShortcutCustomization()
{
GoToSettingsPageAndEnable();
// Find the activation shortcut control
var shortcutButton = Find<Button>("Activation shortcut");
Assert.IsNotNull(shortcutButton, "Activation shortcut control should exist");
// Test customizing the shortcut
shortcutButton.Click();
Task.Delay(1000).Wait();
// Send new key combination (Win+Ctrl+W)
SendKeys(Key.Win, Key.Ctrl, Key.W);
var saveButton = Find<Button>("Save");
Assert.IsNotNull(saveButton, "Save button should exist after editing shortcut");
saveButton.Click();
var helpText = shortcutButton.HelpText;
Assert.AreEqual("Win + Ctrl + W", helpText, "Activation shortcut should be updated to Win + Ctrl + W");
}
[TestMethod("WorkspacesSettings.EnableToggle")]
[TestCategory("Workspaces Settings UI")]
public void TestEnableDisableModule()
{
GoToSettingsPageAndEnable();
// Find the enable toggle
var enableToggle = Find<ToggleSwitch>("Enable Workspaces");
Assert.IsNotNull(enableToggle, "Enable Workspaces toggle should exist");
Assert.IsTrue(enableToggle.IsOn, "Enable Workspaces toggle should be in the 'on' state");
// Toggle the state
enableToggle.Click();
Task.Delay(500).Wait();
// Verify state changed
Assert.IsFalse(enableToggle.IsOn, "Toggle state should change");
// Verify related controls are enabled/disabled accordingly
var launchButton = Find<Button>("Launch editor");
Assert.IsFalse(launchButton.Enabled, "Launch editor button should be disabled when module is disabled");
}
[TestMethod("WorkspacesSettings.LaunchByActivationShortcut")]
[TestCategory("Workspaces Settings UI")]
[Ignore("Wait until settings & runner can be connected in framework")]
public void TestLaunchEditorByActivationShortcut()
{
// Ensure module is enabled
var enableToggle = Find<ToggleSwitch>("Enable Workspaces");
if (!enableToggle.IsOn)
{
enableToggle.Click();
Thread.Sleep(500);
}
// Close settings window to test shortcut
ExitScopeExe();
Thread.Sleep(1000);
// Default shortcut is Win+Ctrl+`
SendKeys(Key.Win, Key.Ctrl, Key.W);
Thread.Sleep(2000);
// Verify editor opened
Assert.IsTrue(WindowHelper.IsWindowOpen("Workspaces Editor"), "Workspaces Editor should open with activation shortcut");
// Reopen settings for next tests
RestartScopeExe();
NavigateToWorkspacesSettings();
}
[TestMethod("WorkspacesSettings.DisableModuleNoLaunch")]
[TestCategory("Workspaces Settings UI")]
[Ignore("Wait until settings & runner can be connected in framework")]
public void TestDisabledModuleDoesNotLaunchByShortcut()
{
// Disable the module
var enableToggle = Find<ToggleSwitch>("Enable Workspaces");
if (enableToggle.IsOn)
{
enableToggle.Click();
Thread.Sleep(500);
}
// Close settings to test shortcut
ExitScopeExe();
Thread.Sleep(1000);
// Try to launch with shortcut
SendKeys(Key.Win, Key.Ctrl, Key.W);
Thread.Sleep(2000);
// Verify editor did not open
Assert.IsFalse(WindowHelper.IsWindowOpen("Workspaces Editor"), "Workspaces Editor should not open when module is disabled");
// Reopen settings and re-enable the module
RestartScopeExe();
NavigateToWorkspacesSettings();
enableToggle = Find<ToggleSwitch>("Enable Workspaces");
if (!enableToggle.IsOn)
{
enableToggle.Click();
Thread.Sleep(500);
}
}
[TestMethod("WorkspacesSettings.QuickAccessLaunch")]
[TestCategory("Workspaces Settings UI")]
[Ignore("Wait until tray icon supported is in framework")]
public void TestLaunchFromQuickAccess()
{
// This test verifies the "quick access" mention in settings
// Look for any quick access related UI elements
var quickAccessInfo = FindAll(By.LinkText("quick access"));
if (quickAccessInfo.Count > 0)
{
Assert.IsTrue(quickAccessInfo.Count > 0, "Quick access information should be present in settings");
}
// Note: Actual system tray/quick access interaction would require
// more complex automation that might be platform-specific
}
private void NavigateToWorkspacesSettings()
{
// Find and click Workspaces in the navigation
var workspacesNavItem = Find<NavigationViewItem>("Workspaces");
workspacesNavItem.Click();
Thread.Sleep(1000);
}
private void GoToSettingsPageAndEnable()
{
if (this.FindAll<NavigationViewItem>("Workspaces").Count == 0)
{
this.Find<NavigationViewItem>("Windowing & Layouts").Click();
}
this.Find<NavigationViewItem>("Workspaces").Click();
var enableButton = this.Find<ToggleSwitch>("Enable Workspaces");
Assert.IsNotNull(enableButton, "Enable Workspaces toggle should exist");
if (!enableButton.IsOn)
{
enableButton.Click();
Task.Delay(500).Wait(); // Wait for the toggle animation and state change
}
// Verify it's now enabled
Assert.IsTrue(enableButton.IsOn, "Enable Workspaces toggle should be in the 'on' state");
}
private void AttachWorkspacesEditor()
{
Task.Delay(200).Wait();
this.Session.Attach(PowerToysModule.Workspaces);
}
}

View File

@@ -1,82 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WorkspacesEditor.Utils;
namespace WorkspacesEditorUITest;
[TestClass]
public class WorkspacesSnapshotTests : WorkspacesUiAutomationBase
{
public WorkspacesSnapshotTests()
: base()
{
}
[TestMethod("WorkspacesSnapshot.CancelCapture")]
[TestCategory("Workspaces Snapshot UI")]
public void TestCaptureCancel()
{
AttachWorkspacesEditor();
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Task.Delay(1000).Wait();
AttachSnapshotWindow();
var cancelButton = Find<Button>("Cancel");
Assert.IsNotNull(cancelButton, "Capture button should exist");
cancelButton.Click();
}
[TestMethod("WorkspacesSnapshot.CapturePackagedApps")]
[TestCategory("Workspaces Snapshot UI")]
public void TestCapturePackagedApplications()
{
OpenCalculator();
// OpenWindowsSettings();
Task.Delay(2000).Wait();
AttachWorkspacesEditor();
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Task.Delay(1000).Wait();
AttachSnapshotWindow();
var captureButton = Find<Button>("Capture");
captureButton.Click();
Task.Delay(3000).Wait();
// Verify captured windows by reading the temporary workspaces file as the ground truth.
var editorIO = new WorkspacesEditorIO();
var workspace = editorIO.ParseTempProject();
Assert.IsNotNull(workspace, "Workspace data should be deserialized.");
Assert.IsNotNull(workspace.Applications, "Workspace should contain a list of apps.");
bool isCalculatorFound = workspace.Applications.Any(app => app.AppPath.Contains("Calculator", StringComparison.OrdinalIgnoreCase));
// bool isSettingsFound = workspace.Applications.Any(app => app.AppPath.Contains("Settings", StringComparison.OrdinalIgnoreCase));
Assert.IsTrue(isCalculatorFound, "Calculator should be captured in the workspace data.");
// Assert.IsTrue(isSettingsFound, "Settings should be captured in the workspace data.");
// Cancel to clean up
AttachWorkspacesEditor();
Find<Button>("Cancel").Click();
Task.Delay(1000).Wait();
// Close test applications
CloseCalculator();
// CloseWindowsSettings();
}
}

View File

@@ -1,253 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using Microsoft.PowerToys.UITest;
namespace WorkspacesEditorUITest
{
public class WorkspacesUiAutomationBase : UITestBase
{
public WorkspacesUiAutomationBase()
: base(PowerToysModule.Workspaces, WindowSize.Medium)
{
}
protected void CreateTestWorkspace(string name)
{
// Open notepad for capture
OpenNotepad();
Task.Delay(1000).Wait();
// Create workspace
AttachWorkspacesEditor();
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Task.Delay(500).Wait();
// Capture
AttachSnapshotWindow();
var captureButton = Find<Button>("Capture");
captureButton.Click();
Task.Delay(5000).Wait();
// Set name
var nameTextBox = Find<TextBox>("EditNameTextBox");
nameTextBox.SetText(name);
// Save
Find<Button>("Save Workspace").Click();
// Close notepad
CloseNotepad();
}
// DO NOT USE UNTIL FRAMEWORK AVAILABLE, CAN'T FIND BUTTON FOR SECOND LOOP
protected void ClearWorkspaces()
{
while (true)
{
try
{
var root = Find<Element>(By.AccessibilityId("WorkspacesItemsControl"));
var buttons = root.FindAll<Button>(By.AccessibilityId("MoreButton"));
Debug.WriteLine($"Found {buttons.Count} button");
var button = buttons[^1];
button.Click();
Task.Delay(500).Wait();
var remove = Find<Button>(By.Name("Remove"));
remove.Click();
Task.Delay(500).Wait();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
break;
}
}
}
protected void CreateWorkspaceWithApps()
{
// Open multiple test applications
OpenTestApplications();
Thread.Sleep(3000);
// Create workspace
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Thread.Sleep(1000);
// Capture
var captureButton = Find<Button>("Capture");
captureButton.Click();
Thread.Sleep(2000);
// Save
Find<Button>("Save").Click();
Thread.Sleep(1000);
// Close test applications
CloseTestApplications();
}
protected void OpenTestApplications()
{
OpenNotepad();
// Could add more applications here
Thread.Sleep(1000);
}
protected void CloseTestApplications()
{
CloseNotepad();
}
protected void CloseWorkspacesEditor()
{
// Find and close the Workspaces Editor window
if (WindowHelper.IsWindowOpen("Workspaces Editor"))
{
var editorWindow = Find<Window>("Workspaces Editor");
editorWindow.Close();
Thread.Sleep(1000);
}
}
protected void ResetShortcutToDefault(Custom shortcutControl)
{
// Right-click on the shortcut control to open context menu
shortcutControl.Click(rightClick: true);
Thread.Sleep(500);
// Look for a "Reset to default" or similar option in the context menu
try
{
// Try to find various possible menu item texts for reset option
var resetOption = Find("Reset to default");
resetOption?.Click();
}
catch
{
try
{
// Try alternative text
var resetOption = Find("Reset");
resetOption?.Click();
}
catch
{
try
{
// Try another alternative
var resetOption = Find("Default");
resetOption?.Click();
}
catch
{
// If context menu doesn't have reset option, try keyboard shortcut
// ESC to close any open menus first
SendKeys(Key.Esc);
Thread.Sleep(200);
// Click on the control and try to clear it with standard shortcuts
shortcutControl.Click();
Thread.Sleep(200);
// Try Ctrl+A to select all, then Delete to clear
SendKeys(Key.Ctrl, Key.A);
Thread.Sleep(100);
SendKeys(Key.Delete);
Thread.Sleep(500);
}
}
}
}
protected void OpenNotepad()
{
var process = System.Diagnostics.Process.Start("notepad.exe");
Task.Delay(1000).Wait();
}
protected void CloseNotepad()
{
var processes = System.Diagnostics.Process.GetProcessesByName("notepad");
foreach (var process in processes)
{
try
{
process.Kill();
process.WaitForExit();
}
catch
{
// ignore
}
}
}
private void AttachPowertoySetting()
{
Task.Delay(200).Wait();
this.Session.Attach(PowerToysModule.PowerToysSettings);
}
protected void AttachWorkspacesEditor()
{
Task.Delay(200).Wait();
this.Session.Attach(PowerToysModule.Workspaces);
}
protected void AttachSnapshotWindow()
{
Task.Delay(5000).Wait();
this.Session.Attach("Snapshot Creator");
}
protected void OpenCalculator()
{
Process.Start("calc.exe");
Task.Delay(1000).Wait();
}
protected void CloseCalculator()
{
foreach (var process in Process.GetProcessesByName("CalculatorApp"))
{
process.Kill();
}
foreach (var process in Process.GetProcessesByName("Calculator"))
{
process.Kill();
}
}
protected void OpenWindowsSettings()
{
Process.Start(new ProcessStartInfo
{
FileName = "ms-settings:",
UseShellExecute = true,
});
Task.Delay(500).Wait();
}
protected void CloseWindowsSettings()
{
foreach (var process in Process.GetProcessesByName("SystemSettings"))
{
process.Kill();
}
}
}
}

View File

@@ -21,7 +21,7 @@ public partial class AppStateModel : ObservableObject
///////////////////////////////////////////////////////////////////////////
// STATE HERE
public RecentCommandsManager RecentCommands { get; set; } = new();
public RecentCommandsManager RecentCommands { get; private set; } = new();
// END SETTINGS
///////////////////////////////////////////////////////////////////////////

View File

@@ -264,6 +264,17 @@ public partial class ContentPageViewModel : PageViewModel, ICommandBarContext
if (model != null)
{
model.ItemsChanged -= Model_ItemsChanged;
if (model is IDisposable disposableModel)
{
try
{
disposableModel.Dispose();
}
catch
{
// Ignore exceptions during cleanup
}
}
}
}
}

View File

@@ -571,6 +571,17 @@ public partial class ListViewModel : PageViewModel, IDisposable
if (model != null)
{
model.ItemsChanged -= Model_ItemsChanged;
if (model is IDisposable disposableModel)
{
try
{
disposableModel.Dispose();
}
catch
{
// Ignore exceptions during cleanup
}
}
}
}
}

View File

@@ -243,6 +243,17 @@ public partial class PageViewModel : ExtensionObjectViewModel, IPageContext
if (model != null)
{
model.PropChanged -= Model_PropChanged;
if (model is IDisposable disposableModel)
{
try
{
disposableModel.Dispose();
}
catch
{
// Ignore exceptions during cleanup
}
}
}
}
}

View File

@@ -130,7 +130,7 @@ public partial class SettingsModel : ObservableObject
{
foreach (var item in newSettings)
{
savedSettings[item.Key] = item.Value?.DeepClone();
savedSettings[item.Key] = item.Value != null ? item.Value.DeepClone() : null;
}
var serialized = savedSettings.ToJsonString(JsonSerializationContext.Default.Options);
@@ -188,8 +188,6 @@ public partial class SettingsModel : ObservableObject
[JsonSerializable(typeof(HistoryItem))]
[JsonSerializable(typeof(SettingsModel))]
[JsonSerializable(typeof(AppStateModel))]
[JsonSerializable(typeof(RecentCommandsManager))]
[JsonSerializable(typeof(List<string>), TypeInfoPropertyName = "StringList")]
[JsonSerializable(typeof(List<HistoryItem>), TypeInfoPropertyName = "HistoryList")]
[JsonSerializable(typeof(Dictionary<string, object>), TypeInfoPropertyName = "Dictionary")]
[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true, IncludeFields = true, PropertyNameCaseInsensitive = true, AllowTrailingCommas = true)]

View File

@@ -18,7 +18,7 @@ using WinRT;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class ShellViewModel(IServiceProvider _serviceProvider, TaskScheduler _scheduler) : ObservableObject
public partial class ShellViewModel(IServiceProvider _serviceProvider, TaskScheduler _scheduler) : ObservableObject, IDisposable
{
[ObservableProperty]
public partial bool IsLoaded { get; set; } = false;
@@ -31,6 +31,8 @@ public partial class ShellViewModel(IServiceProvider _serviceProvider, TaskSched
private PageViewModel _currentPage = new LoadingPageViewModel(null, _scheduler);
private bool _isDisposed;
public PageViewModel CurrentPage
{
get => _currentPage;
@@ -201,6 +203,48 @@ public partial class ShellViewModel(IServiceProvider _serviceProvider, TaskSched
SetActiveExtension(null);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
_isDisposed = true;
if (disposing)
{
// Dispose managed resources
if (_mainListPage is IDisposable disposable)
{
try
{
disposable.Dispose();
}
catch (Exception)
{
}
}
if (_currentPage is IDisposable disposablePage)
{
try
{
disposablePage.Dispose();
}
catch
{
}
}
_mainListPage = null;
_activeExtension = null;
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// You may ask yourself, why aren't we using CsWin32 for this?
// The CsWin32 projected version includes some object marshalling, like so:
//

View File

@@ -21,6 +21,8 @@ internal sealed partial class FallbackOpenFileItem : FallbackCommandItem, System
private uint _queryCookie = 10;
private bool _isDisposed;
public FallbackOpenFileItem()
: base(new NoOpCommand(), Resources.Indexer_Find_Path_fallback_display_title)
{
@@ -120,9 +122,17 @@ internal sealed partial class FallbackOpenFileItem : FallbackCommandItem, System
}
}
public void Dispose()
protected override void Dispose(bool disposing)
{
_searchEngine.Dispose();
GC.SuppressFinalize(this);
if (!_isDisposed)
{
_isDisposed = true;
if (disposing)
{
_searchEngine?.Dispose();
}
}
base.Dispose(disposing);
}
}

View File

@@ -21,6 +21,7 @@ internal sealed partial class ActionsListContextItem : CommandContextItem, IDisp
{
private readonly string fullPath;
private readonly List<CommandContextItem> actions = [];
private bool _isDisposed;
private static readonly Lock UpdateMoreCommandsLock = new();
private static ActionRuntime actionRuntime;
@@ -97,14 +98,24 @@ internal sealed partial class ActionsListContextItem : CommandContextItem, IDisp
}
}
public void Dispose()
protected override void Dispose(bool disposing)
{
lock (UpdateMoreCommandsLock)
if (!_isDisposed)
{
if (actionRuntime != null)
_isDisposed = true;
if (disposing)
{
actionRuntime.ActionCatalog.Changed -= ActionCatalog_Changed;
lock (UpdateMoreCommandsLock)
{
if (actionRuntime != null)
{
actionRuntime.ActionCatalog.Changed -= ActionCatalog_Changed;
actionRuntime = null;
}
}
}
}
base.Dispose(disposing);
}
}

View File

@@ -21,6 +21,8 @@ internal sealed partial class IndexerPage : DynamicListPage, IDisposable
private string initialQuery = string.Empty;
private bool _isDisposed;
public IndexerPage()
{
Id = "com.microsoft.indexer.fileSearch";
@@ -33,6 +35,7 @@ internal sealed partial class IndexerPage : DynamicListPage, IDisposable
public IndexerPage(string query, SearchEngine searchEngine, uint queryCookie, IList<IListItem> firstPageData)
{
disposeSearchEngine = false;
Icon = Icons.FileExplorer;
Name = Resources.Indexer_Title;
_searchEngine = searchEngine;
@@ -40,7 +43,6 @@ internal sealed partial class IndexerPage : DynamicListPage, IDisposable
_indexerListItems.AddRange(firstPageData);
initialQuery = query;
SearchText = query;
disposeSearchEngine = false;
}
public override void UpdateSearchText(string oldSearch, string newSearch)
@@ -76,12 +78,21 @@ internal sealed partial class IndexerPage : DynamicListPage, IDisposable
_searchEngine.Query(query, _queryCookie);
}
public void Dispose()
protected override void Dispose(bool disposing)
{
if (disposeSearchEngine)
if (!_isDisposed)
{
_searchEngine.Dispose();
GC.SuppressFinalize(this);
_isDisposed = true;
if (disposing)
{
if (disposeSearchEngine)
{
_searchEngine?.Dispose();
}
}
}
base.Dispose(disposing);
}
}

View File

@@ -34,6 +34,8 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
private IEnumerable<CatalogPackage>? _results;
private bool _disposed;
public static IconInfo WinGetIcon { get; } = IconHelpers.FromRelativePath("Assets\\WinGet.svg");
public static IconInfo ExtensionsIcon { get; } = IconHelpers.FromRelativePath("Assets\\Extension.svg");
@@ -189,7 +191,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
return [];
}
string searchDebugText = $"{query}{(HasTag ? "+" : string.Empty)}{_tag}";
var searchDebugText = $"{query}{(HasTag ? "+" : string.Empty)}{_tag}";
Logger.LogDebug($"Starting search for '{searchDebugText}'");
HashSet<CatalogPackage> results = new(new PackageIdCompare());
@@ -271,7 +273,19 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
return results;
}
public void Dispose() => throw new NotImplementedException();
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
if (disposing)
{
_cancellationTokenSource?.Dispose();
}
}
base.Dispose(disposing);
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "I just like it")]

View File

@@ -46,21 +46,17 @@ internal sealed partial class WindowWalkerListPage : DynamicListPage, IDisposabl
public override IListItem[] GetItems() => Query(SearchText).ToArray();
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
if (disposing)
{
_cancellationTokenSource?.Dispose();
_disposed = true;
}
}
base.Dispose(disposing);
}
}

View File

@@ -10,12 +10,39 @@ namespace Microsoft.CommandPalette.Extensions.Toolkit;
// asynchronously, so as to not block the extension app while it's being
// processed in the host app.
// (also consider this for ItemsChanged in ListPage)
public partial class BaseObservable : INotifyPropChanged
public partial class BaseObservable : INotifyPropChanged, IDisposable
{
private bool _isDisposed;
public event TypedEventHandler<object, IPropChangedEventArgs>? PropChanged;
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
_isDisposed = true;
if (disposing)
{
PropChanged = null;
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void OnPropertyChanged(string propertyName)
{
if (_isDisposed)
{
return;
}
try
{
// TODO #181 - This is dangerous! If the original host goes away,

View File

@@ -6,7 +6,7 @@ using Windows.Foundation;
namespace Microsoft.CommandPalette.Extensions.Toolkit;
public partial class ListPage : Page, IListPage
public partial class ListPage : Page, IListPage, IDisposable
{
private string _placeholderText = string.Empty;
private string _searchText = string.Empty;
@@ -15,6 +15,7 @@ public partial class ListPage : Page, IListPage
private IFilters? _filters;
private IGridProperties? _gridProperties;
private ICommandItem? _emptyContent;
private bool _isDisposed;
public event TypedEventHandler<object, IItemsChangedEventArgs>? ItemsChanged;
@@ -94,8 +95,28 @@ public partial class ListPage : Page, IListPage
{
}
protected override void Dispose(bool disposing)
{
if (!_isDisposed)
{
_isDisposed = true;
if (disposing)
{
ItemsChanged = null;
}
}
base.Dispose(disposing);
}
protected void RaiseItemsChanged(int totalItems = -1)
{
if (_isDisposed)
{
return;
}
try
{
// TODO #181 - This is the same thing that BaseObservable has to deal with.

View File

@@ -671,22 +671,14 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value)
{
switch (value)
{
case ESettingsWindowNames::Dashboard:
return "Dashboard";
case ESettingsWindowNames::Overview:
return "Overview";
case ESettingsWindowNames::AlwaysOnTop:
return "AlwaysOnTop";
case ESettingsWindowNames::Awake:
return "Awake";
case ESettingsWindowNames::ColorPicker:
return "ColorPicker";
case ESettingsWindowNames::CmdNotFound:
return "CmdNotFound";
case ESettingsWindowNames::FancyZones:
return "FancyZones";
case ESettingsWindowNames::FileLocksmith:
return "FileLocksmith";
case ESettingsWindowNames::Run:
return "Run";
case ESettingsWindowNames::ImageResizer:
@@ -695,16 +687,6 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value)
return "KBM";
case ESettingsWindowNames::MouseUtils:
return "MouseUtils";
case ESettingsWindowNames::MouseWithoutBorders:
return "MouseWithoutBorders";
case ESettingsWindowNames::Peek:
return "Peek";
case ESettingsWindowNames::PowerAccent:
return "PowerAccent";
case ESettingsWindowNames::PowerLauncher:
return "PowerLauncher";
case ESettingsWindowNames::PowerPreview:
return "PowerPreview";
case ESettingsWindowNames::PowerRename:
return "PowerRename";
case ESettingsWindowNames::FileExplorer:
@@ -725,6 +707,8 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value)
return "CropAndLock";
case ESettingsWindowNames::EnvironmentVariables:
return "EnvironmentVariables";
case ESettingsWindowNames::Dashboard:
return "Dashboard";
case ESettingsWindowNames::AdvancedPaste:
return "AdvancedPaste";
case ESettingsWindowNames::NewPlus:
@@ -744,18 +728,10 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value)
ESettingsWindowNames ESettingsWindowNames_from_string(std::string value)
{
if (value == "Dashboard")
{
return ESettingsWindowNames::Dashboard;
}
else if (value == "Overview")
if (value == "Overview")
{
return ESettingsWindowNames::Overview;
}
else if (value == "AlwaysOnTop")
{
return ESettingsWindowNames::AlwaysOnTop;
}
else if (value == "Awake")
{
return ESettingsWindowNames::Awake;
@@ -764,18 +740,10 @@ ESettingsWindowNames ESettingsWindowNames_from_string(std::string value)
{
return ESettingsWindowNames::ColorPicker;
}
else if (value == "CmdNotFound")
{
return ESettingsWindowNames::CmdNotFound;
}
else if (value == "FancyZones")
{
return ESettingsWindowNames::FancyZones;
}
else if (value == "FileLocksmith")
{
return ESettingsWindowNames::FileLocksmith;
}
else if (value == "Run")
{
return ESettingsWindowNames::Run;
@@ -792,26 +760,6 @@ ESettingsWindowNames ESettingsWindowNames_from_string(std::string value)
{
return ESettingsWindowNames::MouseUtils;
}
else if (value == "MouseWithoutBorders")
{
return ESettingsWindowNames::MouseWithoutBorders;
}
else if (value == "Peek")
{
return ESettingsWindowNames::Peek;
}
else if (value == "PowerAccent")
{
return ESettingsWindowNames::PowerAccent;
}
else if (value == "PowerLauncher")
{
return ESettingsWindowNames::PowerLauncher;
}
else if (value == "PowerPreview")
{
return ESettingsWindowNames::PowerPreview;
}
else if (value == "PowerRename")
{
return ESettingsWindowNames::PowerRename;
@@ -852,6 +800,10 @@ ESettingsWindowNames ESettingsWindowNames_from_string(std::string value)
{
return ESettingsWindowNames::EnvironmentVariables;
}
else if (value == "Dashboard")
{
return ESettingsWindowNames::Dashboard;
}
else if (value == "AdvancedPaste")
{
return ESettingsWindowNames::AdvancedPaste;

View File

@@ -6,21 +6,13 @@ enum class ESettingsWindowNames
{
Dashboard = 0,
Overview,
AlwaysOnTop,
Awake,
ColorPicker,
CmdNotFound,
FancyZones,
FileLocksmith,
Run,
ImageResizer,
KBM,
MouseUtils,
MouseWithoutBorders,
Peek,
PowerAccent,
PowerLauncher,
PowerPreview,
PowerRename,
FileExplorer,
ShortcutGuide,

View File

@@ -436,10 +436,6 @@ namespace Microsoft.PowerToys.Settings.UI
case "KBM": return typeof(KeyboardManagerPage);
case "MouseUtils": return typeof(MouseUtilsPage);
case "MouseWithoutBorders": return typeof(MouseWithoutBordersPage);
case "Peek": return typeof(PeekPage);
case "PowerAccent": return typeof(PowerAccentPage);
case "PowerLauncher": return typeof(PowerLauncherPage);
case "PowerPreview": return typeof(PowerPreviewPage);
case "PowerRename": return typeof(PowerRenamePage);
case "QuickAccent": return typeof(PowerAccentPage);
case "FileExplorer": return typeof(PowerPreviewPage);
@@ -448,6 +444,7 @@ namespace Microsoft.PowerToys.Settings.UI
case "MeasureTool": return typeof(MeasureToolPage);
case "Hosts": return typeof(HostsPage);
case "RegistryPreview": return typeof(RegistryPreviewPage);
case "Peek": return typeof(PeekPage);
case "CropAndLock": return typeof(CropAndLockPage);
case "EnvironmentVariables": return typeof(EnvironmentVariablesPage);
case "NewPlus": return typeof(NewPlusPage);

View File

@@ -12,7 +12,6 @@
<UserControl.Resources>
<x:Double x:Key="PageMaxWidth">1000</x:Double>
<x:Double x:Key="PageHeaderMaxWidth">1020</x:Double>
<tkconverters:DoubleToVisibilityConverter
x:Name="doubleToVisibilityConverter"
FalseValue="Collapsed"
@@ -27,7 +26,7 @@
</Grid.RowDefinitions>
<TextBlock
x:Name="Header"
MaxWidth="{StaticResource PageHeaderMaxWidth}"
MaxWidth="{StaticResource PageMaxWidth}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AutomationProperties.HeadingLevel="1"

View File

@@ -88,9 +88,16 @@
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
<tkcontrols:SettingsCard x:Uid="AlwaysOnTop_Sound" HeaderIcon="{ui:FontIcon Glyph=&#xE7F3;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.SoundEnabled, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsExpander
x:Uid="AlwaysOnTop_SoundTitle"
HeaderIcon="{ui:FontIcon Glyph=&#xE7F3;}"
IsExpanded="True">
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard ContentAlignment="Left">
<CheckBox x:Uid="AlwaysOnTop_Sound" IsChecked="{x:Bind ViewModel.SoundEnabled, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
</controls:SettingsGroup>
<controls:SettingsGroup x:Uid="ExcludedApps" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">

View File

@@ -294,7 +294,7 @@
<tkcontrols:SettingsCard x:Uid="General_SettingsBackupAndRestoreLocationText">
<Grid ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
@@ -405,83 +405,76 @@
</InfoBar.IconSource>
</InfoBar>
</controls:SettingsGroup>
<controls:SettingsGroup x:Uid="General_DiagnosticsAndFeedback">
<tkcontrols:SettingsExpander
x:Uid="GeneralPage_EnableDataDiagnostics"
HeaderIcon="{ui:FontIcon Glyph=&#xE9D9;}"
IsEnabled="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"
IsExpanded="True">
<tkcontrols:SettingsExpander.Description>
<StackPanel Orientation="Vertical">
<TextBlock
x:Uid="GeneralPage_EnableDataDiagnosticsText"
Style="{StaticResource SecondaryTextStyle}"
TextWrapping="WrapWholeWords" />
<HyperlinkButton
x:Uid="GeneralPage_DiagnosticsAndFeedback_Link"
Margin="0,2,0,0"
FontWeight="SemiBold"
NavigateUri="https://aka.ms/powertoys-data-and-privacy-documentation" />
</StackPanel>
</tkcontrols:SettingsExpander.Description>
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.EnableDataDiagnostics, Mode=TwoWay}" />
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard x:Uid="GeneralPage_EnableViewDiagnosticData" IsEnabled="{x:Bind ViewModel.EnableDataDiagnostics, Mode=TwoWay}">
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.EnableViewDataDiagnostics, Mode=TwoWay}" />
<tkcontrols:SettingsCard.Description>
<StackPanel Orientation="Vertical">
<TextBlock
x:Uid="GeneralPage_EnableViewDiagnosticDataText"
Style="{StaticResource SecondaryTextStyle}"
TextWrapping="WrapWholeWords" />
<HyperlinkButton
Margin="0,2,0,0"
Click="ViewDiagnosticData_Click"
Content="View diagnostic data"
FontWeight="SemiBold" />
</StackPanel>
</tkcontrols:SettingsCard.Description>
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsExpander.ItemsFooter>
<InfoBar
x:Uid="GeneralPage_ViewDiagnosticDataViewerInfo"
BorderThickness="0"
CornerRadius="0,0,4,4"
IsClosable="False"
IsOpen="{x:Bind Mode=OneWay, Path=ViewModel.ViewDiagnosticDataViewerChanged}"
IsTabStop="{x:Bind Mode=OneWay, Path=ViewModel.ViewDiagnosticDataViewerChanged}"
Severity="Informational">
<InfoBar.ActionButton>
<Button x:Uid="GeneralPage_ViewDiagnosticDataViewerInfoButton" Click="Click_ViewDiagnosticDataViewerRestart" />
</InfoBar.ActionButton>
</InfoBar>
</tkcontrols:SettingsExpander.ItemsFooter>
</tkcontrols:SettingsExpander>
<InfoBar
x:Uid="GPO_SettingIsManaged"
IsClosable="False"
IsOpen="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay}"
IsTabStop="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay}"
Severity="Informational">
<InfoBar.IconSource>
<FontIconSource FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE72E;" />
</InfoBar.IconSource>
</InfoBar>
<tkcontrols:SettingsCard x:Uid="GeneralPage_ReportBugPackage" HeaderIcon="{ui:FontIcon Glyph=&#xEBE8;}">
<StackPanel Orientation="Horizontal">
<controls:SettingsGroup x:Uid="General_DiagnosticsAndFeedback" Visibility="Visible">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<HyperlinkButton
x:Uid="GeneralPage_DiagnosticsAndFeedback_Link"
Margin="0,0,0,8"
Padding="0"
NavigateUri="https://aka.ms/powertoys-data-and-privacy-documentation" />
<tkcontrols:SettingsCard
x:Uid="GeneralPage_EnableDataDiagnostics"
HeaderIcon="{ui:FontIcon Glyph=&#xE9D9;}"
IsEnabled="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.EnableDataDiagnostics, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<InfoBar
x:Uid="GPO_SettingIsManaged"
IsClosable="False"
IsOpen="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay}"
IsTabStop="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay}"
Severity="Informational">
<InfoBar.IconSource>
<FontIconSource FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE72E;" />
</InfoBar.IconSource>
</InfoBar>
<tkcontrols:SettingsExpander
x:Uid="GeneralPage_ViewDiagnosticData"
HeaderIcon="{ui:FontIcon Glyph=&#xE7EF;}"
IsEnabled="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"
IsExpanded="True">
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard x:Uid="GeneralPage_EnableViewDiagnosticData" IsEnabled="{x:Bind ViewModel.EnableDataDiagnostics, Mode=TwoWay}">
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.EnableViewDataDiagnostics, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Uid="GeneralPage_ViewDiagnosticDataViewer"
ActionIcon="{ui:FontIcon Glyph=&#xE8A7;}"
Click="ViewDiagnosticData_Click"
IsClickEnabled="True" />
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
<InfoBar
x:Uid="GeneralPage_ViewDiagnosticDataViewerInfo"
IsClosable="False"
IsOpen="{x:Bind Mode=OneWay, Path=ViewModel.ViewDiagnosticDataViewerChanged}"
IsTabStop="{x:Bind Mode=OneWay, Path=ViewModel.ViewDiagnosticDataViewerChanged}"
Severity="Informational">
<InfoBar.ActionButton>
<Button x:Uid="GeneralPage_ViewDiagnosticDataViewerInfoButton" Click="Click_ViewDiagnosticDataViewerRestart" />
</InfoBar.ActionButton>
</InfoBar>
<tkcontrols:SettingsCard
x:Uid="GeneralPage_ReportBugPackage"
HeaderIcon="{ui:FontIcon Glyph=&#xEBE8;}"
Visibility="{x:Bind ViewModel.IsBugReportRunning, Converter={StaticResource ReverseBoolToVisibilityConverter}, Mode=OneWay}">
<Button
x:Uid="GeneralPageReportBugPackage"
HorizontalAlignment="Right"
Click="BugReportToolClicked"
Visibility="{x:Bind ViewModel.IsBugReportRunning, Converter={StaticResource ReverseBoolToVisibilityConverter}, Mode=OneWay}" />
IsEnabled="{x:Bind ViewModel.IsBugReportRunning, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Uid="GeneralPage_ReportBugPackage"
HeaderIcon="{ui:FontIcon Glyph=&#xEBE8;}"
Visibility="{x:Bind ViewModel.IsBugReportRunning, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
<ProgressRing
Width="24"
Height="24"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Visibility="{x:Bind ViewModel.IsBugReportRunning, Mode=OneWay}" />
</StackPanel>
</tkcontrols:SettingsCard>
VerticalAlignment="Center" />
</tkcontrols:SettingsCard>
</StackPanel>
</controls:SettingsGroup>
</StackPanel>

View File

@@ -152,10 +152,10 @@
<comment>"Screen Ruler" is the name of the utility</comment>
</data>
<data name="MeasureTool_DefaultMeasureStyle.Header" xml:space="preserve">
<value>Default mode</value>
<value>Default measure style</value>
</data>
<data name="MeasureTool_DefaultMeasureStyle.Description" xml:space="preserve">
<value>The measuring mode that is used when activated</value>
<value>The utility will start having the selected style activated</value>
</data>
<data name="MeasureTool_DefaultMeasureStyle_None.Content" xml:space="preserve">
<value>None</value>
@@ -234,7 +234,7 @@
<value>Security key</value>
</data>
<data name="MouseWithoutBorders_SecurityKey.Description" xml:space="preserve">
<value>To set up, generate the key on one machine, and enter it manually on other machines.</value>
<value>The key must be auto generated in one machine by clicking on New Key, then typed in other machines</value>
</data>
<data name="MouseWithoutBorders_NewKey.Content" xml:space="preserve">
<value>New key</value>
@@ -274,7 +274,7 @@
<value>Adding a firewall rule might help solve connection issues.</value>
</data>
<data name="MouseWithoutBorders_RunAsAdminText.Title" xml:space="preserve">
<value>This setting can only be changed when running as administrator</value>
<value>You need to run as administrator to modify this setting.</value>
</data>
<data name="MouseWithoutBorders_ServiceUserUninstallWarning.Title" xml:space="preserve">
<value>If PowerToys is installed as a user, uninstalling/upgrading may require the Mouse Without Borders service to be removed manually later.</value>
@@ -361,7 +361,7 @@
<value>Hide the mouse cursor at the top edge of the screen when switching to other machine. This option also steals the focus from any full-screen app to ensure the keyboard input is redirected.</value>
</data>
<data name="MouseWithoutBorders_DrawMouseCursor.Description" xml:space="preserve">
<value>The mouse cursor may not appear on Windows 10 and later if no physical mouse is connected.</value>
<value>Mouse cursor may not be visible in Windows 10 and later versions of Windows when there is no physical mouse attached.</value>
</data>
<data name="MouseWithoutBorders_ValidateRemoteMachineIP.Description" xml:space="preserve">
<value>Reverse DNS lookup to validate machine IP Address.</value>
@@ -804,7 +804,7 @@
<comment>launches the FancyZones layout editor application</comment>
</data>
<data name="FancyZones_MakeDraggedWindowTransparentCheckBoxControl.Content" xml:space="preserve">
<value>Make the dragged window transparent</value>
<value>Make dragged window transparent</value>
</data>
<data name="FancyZones_MouseDragCheckBoxControl_Header.Content" xml:space="preserve">
<value>Use a non-primary mouse button to toggle zone activation</value>
@@ -1304,7 +1304,7 @@
<value>Always run as administrator</value>
</data>
<data name="GeneralSettings_AlwaysRunAsAdminText.Description" xml:space="preserve">
<value>This setting can only be changed when running as administrator</value>
<value>You need to run as administrator to use this setting</value>
</data>
<data name="GeneralSettings_RunningAsUserText" xml:space="preserve">
<value>Running as user</value>
@@ -1465,7 +1465,7 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<comment>Preview Pane and File Explorer are app/feature names in Windows. 'Alt + P' is a shortcut</comment>
</data>
<data name="FileExplorerPreview_RunAsAdminRequired.Title" xml:space="preserve">
<value>This setting can only be changed when running as administrator</value>
<value>You need to run as administrator to modify these settings.</value>
</data>
<data name="FileExplorerPreview_RebootRequired.Title" xml:space="preserve">
<value>A reboot may be required for changes to these settings to take effect</value>
@@ -1520,7 +1520,7 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<value>Mouse actions</value>
</data>
<data name="ColorPicker_MouseActions.Description" xml:space="preserve">
<value>Customize the function of each mouse button</value>
<value>Choose what clicking individual buttons does</value>
</data>
<data name="ColorPicker_PrimaryClick.Header" xml:space="preserve">
<value>Primary click</value>
@@ -1544,7 +1544,7 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<value>Picker behavior</value>
</data>
<data name="ColorPicker_CopiedColorRepresentation.Description" xml:space="preserve">
<value>Format will be copied to your clipboard</value>
<value>This format will be copied to your clipboard</value>
</data>
<data name="KBM_KeysCannotBeRemapped.Text" xml:space="preserve">
<value>Learn more about remapping limitations</value>
@@ -1736,7 +1736,7 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<value>Show color name</value>
</data>
<data name="ColorPicker_ShowColorName.Description" xml:space="preserve">
<value>Displays the color name while picking a color</value>
<value>This will show the name of the color when picking a color</value>
</data>
<data name="ImageResizer_DefaultSize_Large" xml:space="preserve">
<value>Large</value>
@@ -2056,7 +2056,7 @@ Take a moment to preview the various utilities listed or view our comprehensive
<value>Diagnostic data</value>
</data>
<data name="Oobe_Overview_EnableDataDiagnostics.Description" xml:space="preserve">
<value>Helps us make PowerToys faster, more stable, and better over time</value>
<value>Helps inform bug fixes, performance, and product decisions</value>
</data>
<data name="Oobe_WhatsNew_DataDiagnostics_InfoBar.Header" xml:space="preserve">
<value>Turn on diagnostic data to help us improve PowerToys?</value>
@@ -2710,7 +2710,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Spotlight initial zoom</value>
</data>
<data name="MouseUtils_FindMyMouse_SpotlightInitialZoom.Description" xml:space="preserve">
<value>Spotlight zoom factor at the start of the animation</value>
<value>Spotlight zoom factor at animation start</value>
</data>
<data name="MouseUtils_FindMyMouse_AnimationDurationMs.Header" xml:space="preserve">
<value>Animation duration (ms)</value>
@@ -2724,23 +2724,23 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Animations are disabled in your system settings.</value>
</data>
<data name="MouseUtils_FindMyMouse_ShakingMinimumDistance.Header" xml:space="preserve">
<value>Minimum shake distance</value>
<value>Shake minimum distance</value>
</data>
<data name="MouseUtils_FindMyMouse_ShakingMinimumDistance.Description" xml:space="preserve">
<value>The minimum distance the mouse must move to be considered a shake. Lower values increase sensitivity.</value>
<value>The minimum distance for mouse shaking activation, for adjusting sensitivity</value>
</data>
<data name="MouseUtils_FindMyMouse_ShakingIntervalMs.Header" xml:space="preserve">
<value>Shake detection interval (ms)</value>
<value>Shake Interval (ms)</value>
<comment>ms = milliseconds</comment>
</data>
<data name="MouseUtils_FindMyMouse_ShakingIntervalMs.Description" xml:space="preserve">
<value>Time window used to monitor mouse movement for shake detection. Shorter intervals may detect quicker shakes.</value>
<value>The span of time during which we track mouse movement to detect shaking, for adjusting sensitivity</value>
</data>
<data name="MouseUtils_FindMyMouse_ShakingFactor.Header" xml:space="preserve">
<value>Shake sensitivity factor (percent)</value>
<value>Shake factor (percent)</value>
</data>
<data name="MouseUtils_FindMyMouse_ShakingFactor.Description" xml:space="preserve">
<value>Determines how far the pointer must move, relative to the screen diagonal, to count as a shake. Lower values make it more sensitive.</value>
<value>Mouse shaking is detected by checking how much the mouse pointer has travelled when compared to the diagonal of the movement area. Reducing this factor increases sensitivity.</value>
</data>
<data name="MouseUtils_MouseHighlighter.Header" xml:space="preserve">
<value>Mouse Highlighter</value>
@@ -2840,7 +2840,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Windows default</value>
</data>
<data name="ColorModeHeader.Header" xml:space="preserve">
<value>Theme</value>
<value>App theme</value>
</data>
<data name="FancyZones_Zone_Appearance.Description" xml:space="preserve">
<value>Customize the way zones look</value>
@@ -2848,6 +2848,9 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<data name="FancyZones_Zone_Appearance.Header" xml:space="preserve">
<value>Zone appearance</value>
</data>
<data name="LearnMore.Content" xml:space="preserve">
<value>Learn more</value>
</data>
<data name="FileExplorerPreview_ToggleSwitch_Thumbnail_GCODE.Header" xml:space="preserve">
<value>Geometric Code</value>
<comment>File type, do not translate</comment>
@@ -2903,13 +2906,13 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Reparent shortcut</value>
</data>
<data name="CropAndLock_ReparentActivation_Shortcut.Description" xml:space="preserve">
<value>Creates a cropped version of an applications window by embedding the original app into a new window. This is experimental and may cause issues due to the reparenting behavior.</value>
<value>Shortcut to crop an application's window into a cropped window. This is experimental and can cause issues with some applications, since the cropped window will contain the original application window.</value>
</data>
<data name="CropAndLock_ThumbnailActivation_Shortcut.Header" xml:space="preserve">
<value>Thumbnail shortcut</value>
</data>
<data name="CropAndLock_ThumbnailActivation_Shortcut.Description" xml:space="preserve">
<value>Creates a cropped, non-interactive thumbnail of another window. Improves app compatibility.</value>
<value>Shortcut to crop and create a thumbnail of another window. The application isn't controllable through the thumbnail but it'll have less compatibility issues. </value>
</data>
<data name="CropAndLock.SecondaryLinksHeader" xml:space="preserve">
<value>Attribution</value>
@@ -2977,6 +2980,12 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Do not activate when Game Mode is on</value>
<comment>Game Mode is a Windows feature</comment>
</data>
<data name="AlwaysOnTop_SoundTitle.Header" xml:space="preserve">
<value>Sound</value>
</data>
<data name="AlwaysOnTop_Sound.Content" xml:space="preserve">
<value>Play a sound when pinning a window</value>
</data>
<data name="AlwaysOnTop_Behavior.Header" xml:space="preserve">
<value>Behavior</value>
</data>
@@ -3052,10 +3061,10 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Behavior</value>
</data>
<data name="Peek_AlwaysRunNotElevated.Header" xml:space="preserve">
<value>Always run without elevation (even if PowerToys is elevated)</value>
<value>Always run not elevated, even when PowerToys is elevated</value>
</data>
<data name="Peek_AlwaysRunNotElevated.Description" xml:space="preserve">
<value>Runs Peek without admin permissions to improve access to network shares. To apply this change, you must disable and re-enable Peek.</value>
<value>Tries to run Peek without elevated permissions, to fix access to network shares. You need to disable and re-enable Peek for changes to this value to take effect.</value>
<comment>Peek is a product name, do not loc</comment>
</data>
<data name="Peek_CloseAfterLosingFocus.Header" xml:space="preserve">
@@ -3063,13 +3072,13 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<comment>Peek is a product name, do not loc</comment>
</data>
<data name="Peek_ConfirmFileDelete.Header" xml:space="preserve">
<value>Confirm before deleting files</value>
<value>Ask for confirmation before deleting files</value>
</data>
<data name="Peek_ConfirmFileDelete.Description" xml:space="preserve">
<value>You'll be asked to confirm before files are moved to the Recycle Bin</value>
<value>When enabled, you will be prompted to confirm before moving files to the Recycle Bin.</value>
</data>
<data name="FancyZones_DisableRoundCornersOnWindowSnap.Content" xml:space="preserve">
<value>Disable rounded corners when a window is snapped</value>
<value>Disable round corners when window is snapped</value>
</data>
<data name="PowerLauncher_SearchQueryTuningEnabled.Description" xml:space="preserve">
<value>Fine tune results ordering</value>
@@ -3102,7 +3111,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Global sort order score modifier</value>
</data>
<data name="AlwaysOnTop_RoundCorners.Content" xml:space="preserve">
<value>Enable rounded corners</value>
<value>Enable round corners</value>
</data>
<data name="LearnMore_QuickAccent.Text" xml:space="preserve">
<value>Learn more about Quick Accent</value>
@@ -3155,6 +3164,9 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="AlwaysOnTop_ShortDescription" xml:space="preserve">
<value>Pin a window</value>
</data>
<data name="Awake_ShortDescription" xml:space="preserve">
<value>Keep your PC awake</value>
</data>
<data name="ColorPicker_ShortDescription" xml:space="preserve">
<value>Pick a color</value>
</data>
@@ -3173,6 +3185,9 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="FindMyMouse_ShortDescription" xml:space="preserve">
<value>Find the mouse</value>
</data>
<data name="ImageResizer_ShortDescription" xml:space="preserve">
<value>Resize images from right-click context menu</value>
</data>
<data name="MouseHighlighter_ShortDescription" xml:space="preserve">
<value>Highlight clicks</value>
</data>
@@ -3182,6 +3197,9 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="MouseCrosshairs_ShortDescription" xml:space="preserve">
<value>Draw crosshairs centered on the mouse pointer</value>
</data>
<data name="MouseWithoutBorders_ShortDescription" xml:space="preserve">
<value>Move your cursor across multiple devices</value>
</data>
<data name="AdvancedPaste_ShortDescription" xml:space="preserve">
<value>An AI powered tool to put your clipboard content into any format you need, focused towards developer workflows.</value>
</data>
@@ -3194,6 +3212,9 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="Peek_ShortDescription" xml:space="preserve">
<value>Quick and easy previewer</value>
</data>
<data name="PowerRename_ShortDescription" xml:space="preserve">
<value>Rename files and folders from right-click context menu</value>
</data>
<data name="Run_ShortDescription" xml:space="preserve">
<value>A quick launcher</value>
</data>
@@ -3294,7 +3315,7 @@ Activate by holding the key for the character you want to add an accent to, then
<comment>ms = milliseconds</comment>
</data>
<data name="QuickAccent_InputTimeMs.Description" xml:space="preserve">
<value>How long a key must be held before the accent menu appears</value>
<value>Hold the key down for this much time to make the accent menu appear (ms)</value>
<comment>ms = milliseconds</comment>
</data>
<data name="QuickAccent_ExcludedApps.Description" xml:space="preserve">
@@ -3625,7 +3646,7 @@ Activate by holding the key for the character you want to add an accent to, then
<comment>"Hosts File Editor" is a product name</comment>
</data>
<data name="Hosts_AdditionalLinesPosition.Header" xml:space="preserve">
<value>Placement of additional content</value>
<value>Position of additional content</value>
</data>
<data name="Hosts_AdditionalLinesPosition_Bottom.Content" xml:space="preserve">
<value>Bottom</value>
@@ -3653,7 +3674,7 @@ Activate by holding the key for the character you want to add an accent to, then
<comment>"Hosts File Editor" is the name of the utility</comment>
</data>
<data name="Hosts_Toggle_LaunchAdministrator.Description" xml:space="preserve">
<value>Required in order to make changes to the hosts file</value>
<value>Needs to be launched as administrator in order to make changes to the hosts file</value>
</data>
<data name="Hosts_Toggle_LaunchAdministrator.Header" xml:space="preserve">
<value>Launch as administrator</value>
@@ -3695,7 +3716,7 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Environment Variables</value>
</data>
<data name="EnvironmentVariables_Toggle_LaunchAdministrator.Description" xml:space="preserve">
<value>Required in order to make changes to the system environment variables</value>
<value>Needs to be launched as administrator in order to make changes to the system environment variables</value>
</data>
<data name="EnvironmentVariables_Toggle_LaunchAdministrator.Header" xml:space="preserve">
<value>Launch as administrator</value>
@@ -3736,7 +3757,7 @@ Activate by holding the key for the character you want to add an accent to, then
<value>This setting is managed by your organization.</value>
</data>
<data name="Hosts_AdditionalLinesPosition.Description" xml:space="preserve">
<value>Includes items like the file header and any lines that cant be parsed</value>
<value>Additional content includes the file header and lines that can't parse</value>
</data>
<data name="TextExtractor_Languages.Header" xml:space="preserve">
<value>Preferred language</value>
@@ -3773,7 +3794,7 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Experimentation</value>
</data>
<data name="GeneralPage_EnableExperimentation.Description" xml:space="preserve">
<value>Only Windows Insider builds may be selected for experimentation</value>
<value>Note: Only Windows Insider builds may be selected for experimentation</value>
<comment>{Locked="Windows Insider"}</comment>
</data>
<data name="GeneralPage_EnableExperimentation.Header" xml:space="preserve">
@@ -3783,13 +3804,28 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Diagnostics &amp; feedback</value>
</data>
<data name="GeneralPage_DiagnosticsAndFeedback_Link.Content" xml:space="preserve">
<value>Learn more about the logged information and how it's used</value>
<value>Learn more about the information we log &amp; how it gets used</value>
</data>
<data name="GeneralPage_EnableDataDiagnostics.Header" xml:space="preserve">
<value>Diagnostic data</value>
</data>
<data name="GeneralPage_EnableDataDiagnostics.Description" xml:space="preserve">
<value>Helps inform bug fixes, performance, and improvements</value>
</data>
<data name="GeneralPage_ViewDiagnosticData.Header" xml:space="preserve">
<value>View diagnostic data</value>
</data>
<data name="GeneralPage_EnableViewDiagnosticData.Header" xml:space="preserve">
<value>Save logs to this device</value>
<value>Enable viewing</value>
</data>
<data name="GeneralPage_EnableViewDiagnosticData.Description" xml:space="preserve">
<value>Uses up to 1GB (or more) of hard drive space on your PC</value>
</data>
<data name="GeneralPage_ViewDiagnosticDataViewer.Header" xml:space="preserve">
<value>Diagnostic data viewer</value>
</data>
<data name="GeneralPage_ViewDiagnosticDataViewer.Description" xml:space="preserve">
<value>Generate .xml files containing readable diagnostic data. Folder may include .xml and .etl files</value>
</data>
<data name="Shell_AdvancedPaste.Content" xml:space="preserve">
<value>Advanced Paste</value>
@@ -3952,6 +3988,9 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="QuickAccessTxt.Text" xml:space="preserve">
<value>Quick access</value>
</data>
<data name="UpdateAvailable.Title" xml:space="preserve">
<value>Update available</value>
</data>
<data name="FileExplorerPreview_Toggle_Monaco_Max_File_Size.Header" xml:space="preserve">
<value>Maximum file size to preview</value>
<comment>Size refers to the disk space used by a file</comment>
@@ -4016,7 +4055,7 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Activation shortcut</value>
</data>
<data name="MouseUtils_MouseJump_ThumbnailSize.Header" xml:space="preserve">
<value>Thumbnail size</value>
<value>Thumbnail Size</value>
</data>
<data name="MouseUtils_MouseJump_ThumbnailSize_Description_Prefix.Text" xml:space="preserve">
<value>Constrain thumbnail image size to a maximum of</value>
@@ -4151,7 +4190,11 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Launch Registry Preview</value>
</data>
<data name="RegistryPreview_DefaultRegApp.Header" xml:space="preserve">
<value>Make Registry Preview the default app for .reg files</value>
<value>Default app</value>
</data>
<data name="RegistryPreview_DefaultRegApp.Description" xml:space="preserve">
<value>Make Registry Preview default app for opening .reg files</value>
<comment>Registry Preview is app name. Do not localize.</comment>
</data>
<data name="AdvancedPaste_ShortcutWarning.Title" xml:space="preserve">
<value>Using this shortcut may prevent non-text paste actions (e.g. images, files) or built-in paste plain text actions in other applications from functioning.</value>
@@ -4229,6 +4272,12 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="Shell_Dashboard.Content" xml:space="preserve">
<value>Dashboard</value>
</data>
<data name="DisabledModules.Text" xml:space="preserve">
<value>Disabled modules</value>
</data>
<data name="EnabledModules.Text" xml:space="preserve">
<value>Enabled modules</value>
</data>
<data name="Peek_Preview_GroupSettings.Header" xml:space="preserve">
<value>Preview</value>
</data>
@@ -4272,7 +4321,7 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Show the release notes after an update</value>
</data>
<data name="ShowSystemTrayIcon.Description" xml:space="preserve">
<value>To access settings, run the PowerToys executable again</value>
<value>This settings page is accessible by running the PowerToys executable again</value>
</data>
<data name="ShowSystemTrayIcon.Header" xml:space="preserve">
<value>Show system tray icon</value>
@@ -4372,6 +4421,10 @@ Activate by holding the key for the character you want to add an accent to, then
<value>New+</value>
<comment>New+ is the name of the utility. Localize product name in accordance with Windows New</comment>
</data>
<data name="NewPlus_Product_Description.Description" xml:space="preserve">
<value>Create files and folders from a personalized set of templates</value>
<comment>New+ product description</comment>
</data>
<data name="NewPlus_Learn_More.Text" xml:space="preserve">
<value>Learn more about New+</value>
<comment>New+ learn more link. Localize product name in accordance with Windows New</comment>
@@ -4408,15 +4461,15 @@ Activate by holding the key for the character you want to add an accent to, then
<comment>Display options label</comment>
</data>
<data name="NewPlus_Hide_File_Extension_Toggle.Header" xml:space="preserve">
<value>Hide the file extension in template names</value>
<value>Hide template filename extension</value>
<comment>Template file name extension settings toggle</comment>
</data>
<data name="NewPlus_Hide_Starting_Digits_Toggle.Header" xml:space="preserve">
<value>Hide leading digits, spaces, and dots in template filenames</value>
<value>Hide template filename starting digits, spaces, and dots</value>
<comment>Template filename starting digits settings toggle</comment>
</data>
<data name="NewPlus_Hide_Starting_Digits_Description.Text" xml:space="preserve">
<value>Ignores digits, spaces, and dots at the start of filenames—useful for sorting templates without showing those characters</value>
<value>This option is useful when using digits, spaces and dots at the beginning of filenames to control the display order of templates</value>
<comment>Template filename starting digits settings toggle</comment>
</data>
<data name="NewPlus_behavior.Header" xml:space="preserve">
@@ -4803,13 +4856,13 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<value>Open settings</value>
</data>
<data name="LanguageHeader.Header" xml:space="preserve">
<value>Language</value>
<value>Application language</value>
</data>
<data name="LanguageHeader.Description" xml:space="preserve">
<value>PowerToys matches your Windows language by default</value>
<value>PowerToys will use OS language by default.</value>
</data>
<data name="LanguageRestartInfo.Title" xml:space="preserve">
<value>Restart PowerToys to apply language changes</value>
<value>Restart PowerToys for language change to take effect.</value>
</data>
<data name="LanguageRestartInfoButton.Content" xml:space="preserve">
<value>Restart</value>
@@ -5004,7 +5057,7 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<value>Generate bug report package</value>
</data>
<data name="GeneralPage_ReportBugPackage.Description" xml:space="preserve">
<value>Log files will be zipped to your desktop</value>
<value>Create zip folder with logs on the Desktop</value>
</data>
<data name="GeneralPageReportBugPackage.Content" xml:space="preserve">
<value>Generate package</value>
@@ -5027,41 +5080,4 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<data name="HighlightMode_Spotlight_Mode.Content" xml:space="preserve">
<value>Spotlight</value>
</data>
<data name="LearnMore.Content" xml:space="preserve">
<value>Learn more</value>
</data>
<data name="Awake_ShortDescription" xml:space="preserve">
<value>Keep your PC awake</value>
</data>
<data name="ImageResizer_ShortDescription" xml:space="preserve">
<value>Resize images from right-click context menu</value>
</data>
<data name="MouseWithoutBorders_ShortDescription" xml:space="preserve">
<value>Move your cursor across multiple devices</value>
</data>
<data name="PowerRename_ShortDescription" xml:space="preserve">
<value>Rename files and folders from right-click context menu</value>
</data>
<data name="GeneralPage_EnableDataDiagnosticsText.Text" xml:space="preserve">
<value>Helps us make PowerToys faster, more stable, and better over time</value>
</data>
<data name="UpdateAvailable.Title" xml:space="preserve">
<value>Update available</value>
</data>
<data name="DisabledModules.Text" xml:space="preserve">
<value>Disabled modules</value>
</data>
<data name="EnabledModules.Text" xml:space="preserve">
<value>Enabled modules</value>
</data>
<data name="NewPlus_Product_Description.Description" xml:space="preserve">
<value>Create files and folders from a personalized set of templates</value>
<comment>New+ product description</comment>
</data>
<data name="AlwaysOnTop_Sound.Header" xml:space="preserve">
<value>Play a sound when pinning a window</value>
</data>
<data name="GeneralPage_EnableViewDiagnosticDataText.Text" xml:space="preserve">
<value>Stores diagnostic data locally in .xml format; folder may include .etl files as well. May use up 1GB or more of disk space.</value>
</data>
</root>