Compare commits

..

4 Commits

54 changed files with 296 additions and 788 deletions

View File

@@ -10,7 +10,7 @@ parameters:
default: {}
steps:
- task: EsrpCodeSigning@6
- task: EsrpCodeSigning@5
displayName: 🔏 ${{ parameters.displayName }}
inputs:
ConnectedServiceName: ${{ parameters.signingIdentity.serviceName }}

View File

@@ -13,8 +13,6 @@
<PackageVersion Include="Microsoft.Bot.AdaptiveExpressions.Core" Version="4.23.0" />
<PackageVersion Include="Appium.WebDriver" Version="4.4.5" />
<PackageVersion Include="CoenM.ImageSharp.ImageHash" Version="1.3.6" />
<!-- Pin the SixLabors.ImageSharp version (a transitive dependency of CoenM.ImageSharp.ImageHash) to restore functionality and apply patches. -->
<PackageVersion Include="SixLabors.ImageSharp" Version="2.1.12" />
<PackageVersion Include="CommunityToolkit.Common" Version="8.4.0" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageVersion Include="CommunityToolkit.WinUI.Animations" Version="8.2.250402" />

View File

@@ -1549,7 +1549,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
}
processes.resize(bytes / sizeof(processes[0]));
std::array<std::wstring_view, 44> processesToTerminate = {
std::array<std::wstring_view, 42> processesToTerminate = {
L"PowerToys.PowerLauncher.exe",
L"PowerToys.Settings.exe",
L"PowerToys.AdvancedPaste.exe",
@@ -1584,14 +1584,12 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
L"PowerToys.MouseWithoutBordersService.exe",
L"PowerToys.CropAndLock.exe",
L"PowerToys.EnvironmentVariables.exe",
L"PowerToys.QuickAccess.exe",
L"PowerToys.WorkspacesSnapshotTool.exe",
L"PowerToys.WorkspacesLauncher.exe",
L"PowerToys.WorkspacesLauncherUI.exe",
L"PowerToys.WorkspacesEditor.exe",
L"PowerToys.WorkspacesWindowArranger.exe",
L"Microsoft.CmdPal.UI.exe",
L"Microsoft.CmdPal.Ext.PowerToys.exe",
L"PowerToys.ZoomIt.exe",
L"PowerToys.exe",
};

View File

@@ -66,10 +66,5 @@ namespace PowerToys.GPOWrapperProjection
{
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredWorkspacesEnabledValue();
}
public static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue()
{
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredLightSwitchEnabledValue();
}
}
}

View File

@@ -466,27 +466,39 @@
TextChanged="EditVariableDialogValueTxtBox_TextChanged"
TextWrapping="Wrap" />
<MenuFlyoutSeparator Visibility="{Binding ShowAsList, Converter={StaticResource BoolToVisibilityConverter}}" />
<ItemsControl
<ListView
x:Name="EditVariableValuesList"
Margin="0,-8,0,12"
HorizontalAlignment="Stretch"
AllowDrop="True"
CanDragItems="True"
CanReorderItems="True"
DragItemsCompleted="EditVariableValuesList_DragItemsCompleted"
ItemsSource="{Binding ValuesList, Mode=TwoWay}"
Visibility="{Binding ShowAsList, Converter={StaticResource BoolToVisibilityConverter}}">
<ItemsControl.ItemTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="40" />
</Grid.ColumnDefinitions>
<FontIcon
Grid.Column="0"
Margin="0,0,8,0"
FontSize="16"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Glyph="&#xE759;" />
<TextBox
Grid.Column="1"
Background="Transparent"
BorderBrush="Transparent"
LostFocus="EditVariableValuesListTextBox_LostFocus"
Text="{Binding Text}" />
<Button
x:Uid="More_Options_Button"
Grid.Column="1"
Grid.Column="2"
VerticalAlignment="Center"
Content="&#xE712;"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
@@ -523,8 +535,8 @@
</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</ScrollViewer>
</ContentDialog>

View File

@@ -16,6 +16,8 @@ namespace EnvironmentVariablesUILib
{
public sealed partial class EnvironmentVariablesMainPage : Page
{
private const string ValueListSeparator = ";";
private sealed class RelayCommandParameter
{
public RelayCommandParameter(Variable variable, VariablesSet set)
@@ -440,7 +442,7 @@ namespace EnvironmentVariablesUILib
variable.ValuesList.Move(index, index - 1);
}
var newValues = string.Join(";", variable.ValuesList?.Select(x => x.Text).ToArray());
var newValues = string.Join(ValueListSeparator, variable.ValuesList?.Select(x => x.Text).ToArray());
EditVariableDialogValueTxtBox.Text = newValues;
}
@@ -461,7 +463,7 @@ namespace EnvironmentVariablesUILib
variable.ValuesList.Move(index, index + 1);
}
var newValues = string.Join(";", variable.ValuesList?.Select(x => x.Text).ToArray());
var newValues = string.Join(ValueListSeparator, variable.ValuesList?.Select(x => x.Text).ToArray());
EditVariableDialogValueTxtBox.Text = newValues;
}
@@ -476,7 +478,7 @@ namespace EnvironmentVariablesUILib
var variable = EditVariableDialog.DataContext as Variable;
variable.ValuesList.Remove(listItem);
var newValues = string.Join(";", variable.ValuesList?.Select(x => x.Text).ToArray());
var newValues = string.Join(ValueListSeparator, variable.ValuesList?.Select(x => x.Text).ToArray());
EditVariableDialogValueTxtBox.Text = newValues;
}
@@ -492,7 +494,7 @@ namespace EnvironmentVariablesUILib
var index = variable.ValuesList.IndexOf(listItem);
variable.ValuesList.Insert(index, new Variable.ValuesListItem { Text = string.Empty });
var newValues = string.Join(";", variable.ValuesList?.Select(x => x.Text).ToArray());
var newValues = string.Join(ValueListSeparator, variable.ValuesList?.Select(x => x.Text).ToArray());
EditVariableDialogValueTxtBox.TextChanged -= EditVariableDialogValueTxtBox_TextChanged;
EditVariableDialogValueTxtBox.Text = newValues;
EditVariableDialogValueTxtBox.TextChanged += EditVariableDialogValueTxtBox_TextChanged;
@@ -510,7 +512,7 @@ namespace EnvironmentVariablesUILib
var index = variable.ValuesList.IndexOf(listItem);
variable.ValuesList.Insert(index + 1, new Variable.ValuesListItem { Text = string.Empty });
var newValues = string.Join(";", variable.ValuesList?.Select(x => x.Text).ToArray());
var newValues = string.Join(ValueListSeparator, variable.ValuesList?.Select(x => x.Text).ToArray());
EditVariableDialogValueTxtBox.TextChanged -= EditVariableDialogValueTxtBox_TextChanged;
EditVariableDialogValueTxtBox.Text = newValues;
EditVariableDialogValueTxtBox.TextChanged += EditVariableDialogValueTxtBox_TextChanged;
@@ -532,7 +534,7 @@ namespace EnvironmentVariablesUILib
listItem.Text = (sender as TextBox)?.Text;
var variable = EditVariableDialog.DataContext as Variable;
var newValues = string.Join(";", variable.ValuesList?.Select(x => x.Text).ToArray());
var newValues = string.Join(ValueListSeparator, variable.ValuesList?.Select(x => x.Text).ToArray());
EditVariableDialogValueTxtBox.TextChanged -= EditVariableDialogValueTxtBox_TextChanged;
EditVariableDialogValueTxtBox.Text = newValues;
EditVariableDialogValueTxtBox.TextChanged += EditVariableDialogValueTxtBox_TextChanged;
@@ -548,5 +550,16 @@ namespace EnvironmentVariablesUILib
CancelAddVariable();
ConfirmAddVariableBtn.IsEnabled = false;
}
private void EditVariableValuesList_DragItemsCompleted(ListViewBase sender, DragItemsCompletedEventArgs args)
{
if (EditVariableDialog.DataContext is Variable variable && variable.ValuesList != null)
{
var newValues = string.Join(ValueListSeparator, variable.ValuesList.Select(x => x.Text));
EditVariableDialogValueTxtBox.TextChanged -= EditVariableDialogValueTxtBox_TextChanged;
EditVariableDialogValueTxtBox.Text = newValues;
EditVariableDialogValueTxtBox.TextChanged += EditVariableDialogValueTxtBox_TextChanged;
}
}
}
}

View File

@@ -10,10 +10,10 @@ public struct ApplicationWrapper
{
public struct WindowPositionWrapper
{
[JsonPropertyName("X")]
[JsonPropertyName("x")]
public int X { get; set; }
[JsonPropertyName("Y")]
[JsonPropertyName("y")]
public int Y { get; set; }
[JsonPropertyName("width")]

View File

@@ -21,7 +21,7 @@ public sealed partial class BuiltInsCommandProvider : CommandProvider
public override ICommandItem[] TopLevelCommands() =>
[
new CommandItem(openSettings) { },
new CommandItem(_newExtension) { Title = _newExtension.Title },
new CommandItem(_newExtension) { Title = _newExtension.Title, Subtitle = Properties.Resources.builtin_new_extension_subtitle },
];
public override IFallbackCommandItem[] FallbackCommands() =>

View File

@@ -291,26 +291,24 @@
</ItemGroup>
<!-- </AdaptiveCardsWorkaround> -->
<!-- Metadata for build information - Use Target to ensure properties are evaluated at build time, not project load time -->
<Target Name="AddBuildMetadataAssemblyAttributes" BeforeTargets="GetAssemblyAttributes">
<ItemGroup>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>PublishTrimmed</_Parameter1>
<_Parameter2>$(PublishTrimmed)</_Parameter2>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>PublishAot</_Parameter1>
<_Parameter2>$(PublishAot)</_Parameter2>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>CIBuild</_Parameter1>
<_Parameter2>$(CIBuild)</_Parameter2>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>CommandPaletteBranding</_Parameter1>
<_Parameter2>$(CommandPaletteBranding)</_Parameter2>
</AssemblyAttribute>
</ItemGroup>
</Target>
<!-- Metadata for build information -->
<ItemGroup>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>PublishTrimmed</_Parameter1>
<_Parameter2>$(PublishTrimmed)</_Parameter2>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>PublishAot</_Parameter1>
<_Parameter2>$(PublishAot)</_Parameter2>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>CIBuild</_Parameter1>
<_Parameter2>$(CIBuild)</_Parameter2>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>CommandPaletteBranding</_Parameter1>
<_Parameter2>$(CommandPaletteBranding)</_Parameter2>
</AssemblyAttribute>
</ItemGroup>
</Project>

View File

@@ -90,5 +90,20 @@ namespace Microsoft.CmdPal.Ext.TimeDate.UnitTests
// Assert
Assert.IsFalse(string.IsNullOrEmpty(displayName));
}
[TestMethod]
public void GetTranslatedPluginDescriptionTest()
{
// Setup
var provider = new TimeDateCommandsProvider();
// Act
var commands = provider.TopLevelCommands();
var subtitle = commands[0].Subtitle;
// Assert
Assert.IsFalse(string.IsNullOrEmpty(subtitle));
Assert.IsTrue(subtitle.Contains("Show time and date values in different formats"));
}
}
}

View File

@@ -5,7 +5,7 @@
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
<XesBaseYearForStoreVersion>2025</XesBaseYearForStoreVersion>
<VersionMajor>0</VersionMajor>
<VersionMinor>8</VersionMinor>
<VersionMinor>7</VersionMinor>
<VersionInfoProductName>Microsoft Command Palette</VersionInfoProductName>
</PropertyGroup>
</Project>

View File

@@ -15,6 +15,7 @@ public partial class CalculatorCommandProvider : CommandProvider
private static ISettingsInterface settings = new SettingsManager();
private readonly ListItem _listItem = new(new CalculatorListPage(settings))
{
Subtitle = Resources.calculator_top_level_subtitle,
MoreCommands = [new CommandContextItem(((SettingsManager)settings).Settings.SettingsPage)],
};

View File

@@ -19,6 +19,7 @@ public partial class ClipboardHistoryCommandsProvider : CommandProvider
_clipboardHistoryListItem = new ListItem(new ClipboardHistoryListPage(_settingsManager))
{
Title = Properties.Resources.list_item_title,
Subtitle = Properties.Resources.list_item_subtitle,
Icon = Icons.ClipboardListIcon,
MoreCommands = [
new CommandContextItem(_settingsManager.Settings.SettingsPage),

View File

@@ -2,7 +2,6 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.CommandPalette.Extensions;
@@ -42,19 +41,15 @@ internal sealed partial class FancyZonesMonitorListItem : ListItem
public static Details BuildMonitorDetails(FancyZonesMonitorDescriptor monitor)
{
var currentVirtualDesktop = FancyZonesVirtualDesktop.GetCurrentVirtualDesktopIdString();
// Calculate physical resolution from logical pixels and DPI
var scaleFactor = monitor.Data.Dpi > 0 ? monitor.Data.Dpi / 96.0 : 1.0;
var physicalWidth = (int)Math.Round(monitor.Data.MonitorWidth * scaleFactor);
var physicalHeight = (int)Math.Round(monitor.Data.MonitorHeight * scaleFactor);
var resolution = $"{physicalWidth}\u00D7{physicalHeight}";
var tags = new List<IDetailsElement>
{
DetailTag(Resources.FancyZones_Monitor, monitor.Data.Monitor),
DetailTag(Resources.FancyZones_Instance, monitor.Data.MonitorInstanceId),
DetailTag(Resources.FancyZones_Serial, monitor.Data.MonitorSerialNumber),
DetailTag(Resources.FancyZones_Number, monitor.Data.MonitorNumber.ToString(CultureInfo.InvariantCulture)),
DetailTag(Resources.FancyZones_VirtualDesktop, currentVirtualDesktop),
DetailTag(Resources.FancyZones_Resolution, resolution),
DetailTag(Resources.FancyZones_WorkArea, $"{monitor.Data.LeftCoordinate},{monitor.Data.TopCoordinate} {monitor.Data.WorkAreaWidth}\u00D7{monitor.Data.WorkAreaHeight}"),
DetailTag(Resources.FancyZones_Resolution, $"{monitor.Data.MonitorWidth}\u00D7{monitor.Data.MonitorHeight}"),
DetailTag(Resources.FancyZones_DPI, monitor.Data.Dpi.ToString(CultureInfo.InvariantCulture)),
};

View File

@@ -19,12 +19,8 @@ internal readonly record struct FancyZonesMonitorDescriptor(
{
get
{
// MonitorWidth/Height are logical (DPI-scaled) pixels, calculate physical resolution
var scaleFactor = Data.Dpi > 0 ? Data.Dpi / 96.0 : 1.0;
var physicalWidth = (int)Math.Round(Data.MonitorWidth * scaleFactor);
var physicalHeight = (int)Math.Round(Data.MonitorHeight * scaleFactor);
var size = $"{physicalWidth}×{physicalHeight}";
var scaling = Data.Dpi > 0 ? string.Format(CultureInfo.InvariantCulture, "{0}%", (int)Math.Round(scaleFactor * 100)) : "n/a";
var size = $"{Data.MonitorWidth}×{Data.MonitorHeight}";
var scaling = Data.Dpi > 0 ? string.Format(CultureInfo.InvariantCulture, "{0}%", (int)Math.Round(Data.Dpi * 100 / 96.0)) : "n/a";
return $"{size} \u2022 {scaling}";
}
}

View File

@@ -485,21 +485,12 @@ internal static class FancyZonesThumbnailRenderer
private static List<NormalizedRect> GetFocusRects(int zoneCount)
{
// Focus layout parameters from FancyZonesEditor CanvasLayoutModel:
// - DefaultOffset = 100px from top-left (normalized: ~0.05 for typical screen)
// - OffsetShift = 50px per zone (normalized: ~0.025)
// - ZoneSizeMultiplier = 0.4 (zones are 40% of screen)
zoneCount = Math.Clamp(zoneCount, 1, 8);
var rects = new List<NormalizedRect>(zoneCount);
const float defaultOffset = 0.05f; // ~100px on 1920px screen
const float offsetShift = 0.025f; // ~50px on 1920px screen
const float zoneSize = 0.4f; // 40% of screen
for (var i = 0; i < zoneCount; i++)
{
var offset = i * offsetShift;
rects.Add(new NormalizedRect(defaultOffset + offset, defaultOffset + offset, zoneSize, zoneSize));
var offset = i * 0.06f;
rects.Add(new NormalizedRect(0.1f + offset, 0.1f + offset, 0.8f, 0.8f));
}
return rects;

View File

@@ -7,7 +7,6 @@
<OutputType>WinExe</OutputType>
<RootNamespace>PowerToysExtension</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>..\..\..\..\runner\svgs\icon.ico</ApplicationIcon>
<PublishProfile>win-$(Platform).pubxml</PublishProfile>
<EnableMsixTooling>false</EnableMsixTooling>
<WindowsPackageType>None</WindowsPackageType>

View File

@@ -31,6 +31,7 @@ public partial class RemoteDesktopCommandProvider : CommandProvider
listPageCommand = new CommandItem(listPage)
{
Subtitle = Resources.remotedesktop_subtitle,
Icon = Icons.RDPIcon,
MoreCommands = [
new CommandContextItem(settingsManager.Settings.SettingsPage),

View File

@@ -39,6 +39,7 @@ public partial class ShellCommandsProvider : CommandProvider
{
Icon = Icons.RunV2Icon,
Title = Resources.shell_command_name,
Subtitle = Resources.cmd_plugin_description,
MoreCommands = [
new CommandContextItem(Settings.SettingsPage),
],

View File

@@ -28,6 +28,7 @@ public sealed partial class TimeDateCommandsProvider : CommandProvider
{
Icon = _timeDateExtensionPage.Icon,
Title = Resources.Microsoft_plugin_timedate_plugin_name,
Subtitle = GetTranslatedPluginDescription(),
MoreCommands = [new CommandContextItem(_settingsManager.Settings.SettingsPage)],
};

View File

@@ -26,6 +26,7 @@ public partial class WindowWalkerCommandsProvider : CommandProvider
_windowWalkerPageItem = new CommandItem(new WindowWalkerListPage())
{
Title = Resources.window_walker_top_level_command_title,
Subtitle = Resources.windowwalker_name,
MoreCommands = [
new CommandContextItem(Settings.SettingsPage),
],

View File

@@ -30,6 +30,7 @@ public sealed partial class WindowsSettingsCommandsProvider : CommandProvider
_searchSettingsListItem = new CommandItem(new WindowsSettingsListPage(_windowsSettings))
{
Title = Resources.settings_title,
Subtitle = Resources.settings_subtitle,
};
_fallback = new(_windowsSettings);

View File

@@ -9,7 +9,6 @@ using System.CommandLine.Invocation;
using System.Globalization;
using System.Linq;
using FancyZonesCLI.Utils;
using FancyZonesEditorCommon.Data;
using FancyZonesEditorCommon.Utils;
@@ -36,19 +35,13 @@ internal sealed partial class SetHotkeyCommand : FancyZonesBaseCommand
{
// FancyZones running guard is handled by FancyZonesBaseCommand.
int key = context.ParseResult.GetValueForArgument(_key);
string layoutInput = context.ParseResult.GetValueForArgument(_layout);
string layout = context.ParseResult.GetValueForArgument(_layout);
if (key < 0 || key > 9)
{
throw new InvalidOperationException(Properties.Resources.set_hotkey_error_invalid_key);
}
// Normalize GUID to Windows format with braces (supports input with or without braces)
if (!GuidHelper.TryNormalizeGuid(layoutInput, out string layout))
{
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_hotkey_error_not_custom, layoutInput));
}
// Editor only allows assigning hotkeys to existing custom layouts.
var customLayouts = FancyZonesDataIO.ReadCustomLayouts();
@@ -67,7 +60,7 @@ internal sealed partial class SetHotkeyCommand : FancyZonesBaseCommand
if (!matchedLayout.HasValue)
{
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_hotkey_error_not_custom, layoutInput));
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_hotkey_error_not_custom, layout));
}
string layoutName = matchedLayout.Value.Name;

View File

@@ -140,12 +140,9 @@ internal sealed partial class SetLayoutCommand : FancyZonesBaseCommand
return null;
}
// Normalize GUID to Windows format with braces (supports input with or without braces)
string normalizedLayout = GuidHelper.NormalizeGuid(layout) ?? layout;
foreach (var customLayout in customLayouts.CustomLayouts)
{
if (customLayout.Uuid.Equals(normalizedLayout, StringComparison.OrdinalIgnoreCase))
if (customLayout.Uuid.Equals(layout, StringComparison.OrdinalIgnoreCase))
{
return customLayout;
}

View File

@@ -4,7 +4,6 @@
using System;
using System.CommandLine;
using System.Globalization;
using System.Linq;
namespace FancyZonesCLI.CommandLine;
@@ -14,15 +13,15 @@ internal static class FancyZonesCliUsage
public static void PrintUsage()
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
Console.WriteLine(Properties.Resources.usage_title);
Console.WriteLine("FancyZones CLI - Command line interface for FancyZones");
Console.WriteLine();
var cmd = FancyZonesCliCommandFactory.CreateRootCommand();
Console.WriteLine(Properties.Resources.usage_syntax);
Console.WriteLine("Usage: FancyZonesCLI [command] [options]");
Console.WriteLine();
Console.WriteLine(Properties.Resources.usage_options);
Console.WriteLine("Options:");
foreach (var option in cmd.Options)
{
var aliases = string.Join(", ", option.Aliases);
@@ -31,7 +30,7 @@ internal static class FancyZonesCliUsage
}
Console.WriteLine();
Console.WriteLine(Properties.Resources.usage_commands);
Console.WriteLine("Commands:");
foreach (var command in cmd.Subcommands)
{
if (command.IsHidden)
@@ -52,7 +51,7 @@ internal static class FancyZonesCliUsage
}
Console.WriteLine();
Console.WriteLine(Properties.Resources.usage_examples);
Console.WriteLine("Examples:");
Console.WriteLine(" FancyZonesCLI --help");
Console.WriteLine(" FancyZonesCLI --version");
Console.WriteLine(" FancyZonesCLI get-monitors");
@@ -60,135 +59,4 @@ internal static class FancyZonesCliUsage
Console.WriteLine(" FancyZonesCLI set-layout <uuid> --monitor 1");
Console.WriteLine(" FancyZonesCLI get-hotkeys");
}
public static void PrintCommandUsage(string commandName)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
var rootCmd = FancyZonesCliCommandFactory.CreateRootCommand();
// Find matching subcommand by name or alias
var subcommand = rootCmd.Subcommands.FirstOrDefault(c =>
c.Aliases.Any(a => string.Equals(a, commandName, StringComparison.OrdinalIgnoreCase)));
if (subcommand == null)
{
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.usage_unknown_command, commandName));
Console.WriteLine();
Console.WriteLine(Properties.Resources.usage_run_help);
return;
}
// Command name and description
Console.WriteLine($"{Properties.Resources.usage_command} {subcommand.Name}");
if (!string.IsNullOrEmpty(subcommand.Description))
{
Console.WriteLine($" {subcommand.Description}");
}
Console.WriteLine();
// Usage line
string argsLabel = string.Join(" ", subcommand.Arguments.Select(a => $"<{a.Name}>"));
string optionsLabel = subcommand.Options.Any() ? " [options]" : string.Empty;
Console.WriteLine($"Usage: FancyZonesCLI {subcommand.Name} {argsLabel}{optionsLabel}".TrimEnd());
Console.WriteLine();
// Aliases
var aliases = subcommand.Aliases.Where(a => !string.Equals(a, subcommand.Name, StringComparison.OrdinalIgnoreCase)).ToList();
if (aliases.Count > 0)
{
Console.WriteLine($"{Properties.Resources.usage_aliases} {string.Join(", ", aliases)}");
Console.WriteLine();
}
// Arguments
if (subcommand.Arguments.Any())
{
Console.WriteLine(Properties.Resources.usage_arguments);
foreach (var arg in subcommand.Arguments)
{
var argDescription = arg.Description ?? string.Empty;
Console.WriteLine($" <{arg.Name}>{(arg.Arity.MinimumNumberOfValues == 0 ? $" {Properties.Resources.usage_optional}" : string.Empty),-20} {argDescription}");
}
Console.WriteLine();
}
// Options
if (subcommand.Options.Any())
{
Console.WriteLine(Properties.Resources.usage_options);
foreach (var option in subcommand.Options)
{
var optAliases = string.Join(", ", option.Aliases);
var optDescription = option.Description ?? string.Empty;
Console.WriteLine($" {optAliases,-25} {optDescription}");
}
Console.WriteLine();
}
// Command-specific examples
PrintCommandExamples(subcommand.Name);
}
private static void PrintCommandExamples(string commandName)
{
Console.WriteLine(Properties.Resources.usage_examples);
switch (commandName.ToLowerInvariant())
{
case "get-monitors":
Console.WriteLine(" FancyZonesCLI get-monitors");
Console.WriteLine(" FancyZonesCLI m");
break;
case "get-layouts":
Console.WriteLine(" FancyZonesCLI get-layouts");
Console.WriteLine(" FancyZonesCLI ls");
break;
case "get-active-layout":
Console.WriteLine(" FancyZonesCLI get-active-layout");
Console.WriteLine(" FancyZonesCLI active");
break;
case "set-layout":
Console.WriteLine(" FancyZonesCLI set-layout focus");
Console.WriteLine(" FancyZonesCLI set-layout columns --monitor 1");
Console.WriteLine(" FancyZonesCLI set-layout {uuid} --all");
Console.WriteLine(" FancyZonesCLI s rows -m 2");
break;
case "open-editor":
Console.WriteLine(" FancyZonesCLI open-editor");
Console.WriteLine(" FancyZonesCLI e");
break;
case "open-settings":
Console.WriteLine(" FancyZonesCLI open-settings");
Console.WriteLine(" FancyZonesCLI settings");
break;
case "get-hotkeys":
Console.WriteLine(" FancyZonesCLI get-hotkeys");
Console.WriteLine(" FancyZonesCLI hk");
break;
case "set-hotkey":
Console.WriteLine(" FancyZonesCLI set-hotkey 1 {layout-uuid}");
Console.WriteLine(" FancyZonesCLI shk 2 0CEBCBA9-9C32-4395-B93E-DC77485AD6D0");
break;
case "remove-hotkey":
Console.WriteLine(" FancyZonesCLI remove-hotkey 1");
Console.WriteLine(" FancyZonesCLI rhk 2");
break;
default:
Console.WriteLine($" FancyZonesCLI {commandName}");
break;
}
}
}

View File

@@ -12,8 +12,6 @@ namespace FancyZonesCLI;
internal sealed class Program
{
private static readonly string[] HelpFlags = ["--help", "-h", "-?"];
private static async Task<int> Main(string[] args)
{
Logger.InitializeLogger();
@@ -23,17 +21,14 @@ internal sealed class Program
NativeMethods.InitializeWindowMessages();
// Intercept help requests early and print custom usage.
if (TryHandleHelpRequest(args))
if (args.Any(a => string.Equals(a, "--help", StringComparison.OrdinalIgnoreCase) ||
string.Equals(a, "-h", StringComparison.OrdinalIgnoreCase) ||
string.Equals(a, "-?", StringComparison.OrdinalIgnoreCase)))
{
FancyZonesCliUsage.PrintUsage();
return 0;
}
// Detect PowerShell script block expansion (when {} is interpreted as script block)
if (DetectPowerShellScriptBlockArgs(args))
{
return 1;
}
RootCommand rootCommand = FancyZonesCliCommandFactory.CreateRootCommand();
int exitCode = await rootCommand.InvokeAsync(args);
@@ -48,69 +43,4 @@ internal sealed class Program
return exitCode;
}
/// <summary>
/// Handles help requests for root command and subcommands.
/// </summary>
/// <returns>True if help was printed, false otherwise.</returns>
private static bool TryHandleHelpRequest(string[] args)
{
bool hasHelpFlag = args.Any(a => HelpFlags.Any(h => string.Equals(a, h, StringComparison.OrdinalIgnoreCase)));
if (!hasHelpFlag)
{
return false;
}
// Get non-help arguments to identify subcommand
var nonHelpArgs = args.Where(a => !HelpFlags.Any(h => string.Equals(a, h, StringComparison.OrdinalIgnoreCase))).ToArray();
if (nonHelpArgs.Length == 0)
{
// Root help: fancyzones cli --help
FancyZonesCliUsage.PrintUsage();
}
else
{
// Subcommand help: fancyzones cli <command> --help
string subcommandName = nonHelpArgs[0];
FancyZonesCliUsage.PrintCommandUsage(subcommandName);
}
return true;
}
/// <summary>
/// Detects when PowerShell interprets {GUID} as a script block and converts it to encoded command args.
/// This happens when users forget to quote GUIDs with braces in PowerShell.
/// </summary>
/// <returns>True if PowerShell script block args were detected, false otherwise.</returns>
private static bool DetectPowerShellScriptBlockArgs(string[] args)
{
// PowerShell converts {scriptblock} to: -encodedCommand <base64> -inputFormat xml -outputFormat text
bool hasEncodedCommand = args.Any(a => string.Equals(a, "-encodedCommand", StringComparison.OrdinalIgnoreCase));
bool hasInputFormat = args.Any(a => string.Equals(a, "-inputFormat", StringComparison.OrdinalIgnoreCase));
bool hasOutputFormat = args.Any(a => string.Equals(a, "-outputFormat", StringComparison.OrdinalIgnoreCase));
if (hasEncodedCommand || (hasInputFormat && hasOutputFormat))
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(Properties.Resources.error_powershell_scriptblock_title);
Console.ResetColor();
Console.WriteLine();
Console.WriteLine(Properties.Resources.error_powershell_scriptblock_explanation);
Console.WriteLine(Properties.Resources.error_powershell_scriptblock_hint);
Console.WriteLine();
Console.WriteLine($" {Properties.Resources.error_powershell_scriptblock_option1}");
Console.WriteLine($" {Properties.Resources.error_powershell_scriptblock_option1_example}");
Console.WriteLine();
Console.WriteLine($" {Properties.Resources.error_powershell_scriptblock_option2}");
Console.WriteLine($" {Properties.Resources.error_powershell_scriptblock_option2_example}");
Console.WriteLine();
Logger.LogWarning("PowerShell script block expansion detected - user needs to quote GUID or omit braces");
return true;
}
return false;
}
}

View File

@@ -349,113 +349,5 @@ namespace FancyZonesCLI.Properties {
return ResourceManager.GetString("editor_params_timeout", resourceCulture);
}
}
internal static string error_powershell_scriptblock_title {
get {
return ResourceManager.GetString("error_powershell_scriptblock_title", resourceCulture);
}
}
internal static string error_powershell_scriptblock_explanation {
get {
return ResourceManager.GetString("error_powershell_scriptblock_explanation", resourceCulture);
}
}
internal static string error_powershell_scriptblock_hint {
get {
return ResourceManager.GetString("error_powershell_scriptblock_hint", resourceCulture);
}
}
internal static string error_powershell_scriptblock_option1 {
get {
return ResourceManager.GetString("error_powershell_scriptblock_option1", resourceCulture);
}
}
internal static string error_powershell_scriptblock_option1_example {
get {
return ResourceManager.GetString("error_powershell_scriptblock_option1_example", resourceCulture);
}
}
internal static string error_powershell_scriptblock_option2 {
get {
return ResourceManager.GetString("error_powershell_scriptblock_option2", resourceCulture);
}
}
internal static string error_powershell_scriptblock_option2_example {
get {
return ResourceManager.GetString("error_powershell_scriptblock_option2_example", resourceCulture);
}
}
internal static string usage_title {
get {
return ResourceManager.GetString("usage_title", resourceCulture);
}
}
internal static string usage_syntax {
get {
return ResourceManager.GetString("usage_syntax", resourceCulture);
}
}
internal static string usage_options {
get {
return ResourceManager.GetString("usage_options", resourceCulture);
}
}
internal static string usage_commands {
get {
return ResourceManager.GetString("usage_commands", resourceCulture);
}
}
internal static string usage_examples {
get {
return ResourceManager.GetString("usage_examples", resourceCulture);
}
}
internal static string usage_arguments {
get {
return ResourceManager.GetString("usage_arguments", resourceCulture);
}
}
internal static string usage_aliases {
get {
return ResourceManager.GetString("usage_aliases", resourceCulture);
}
}
internal static string usage_command {
get {
return ResourceManager.GetString("usage_command", resourceCulture);
}
}
internal static string usage_optional {
get {
return ResourceManager.GetString("usage_optional", resourceCulture);
}
}
internal static string usage_unknown_command {
get {
return ResourceManager.GetString("usage_unknown_command", resourceCulture);
}
}
internal static string usage_run_help {
get {
return ResourceManager.GetString("usage_run_help", resourceCulture);
}
}
}
}

View File

@@ -230,62 +230,4 @@ Tip: For templates, use the type name (e.g., 'focus', 'columns', 'rows', 'grid',
<data name="editor_params_timeout" xml:space="preserve">
<value>Could not get current monitor information (timed out after {0}ms waiting for '{1}').</value>
</data>
<!-- PowerShell Script Block Detection -->
<data name="error_powershell_scriptblock_title" xml:space="preserve">
<value>Error: Invalid GUID format detected.</value>
</data>
<data name="error_powershell_scriptblock_explanation" xml:space="preserve">
<value>PowerShell interprets curly braces {} as script blocks.</value>
</data>
<data name="error_powershell_scriptblock_hint" xml:space="preserve">
<value>Please quote your GUID or use it without braces:</value>
</data>
<data name="error_powershell_scriptblock_option1" xml:space="preserve">
<value>Option 1 - Quote the GUID:</value>
</data>
<data name="error_powershell_scriptblock_option1_example" xml:space="preserve">
<value>FancyZonesCLI shk 1 '{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}'</value>
</data>
<data name="error_powershell_scriptblock_option2" xml:space="preserve">
<value>Option 2 - Omit the braces (recommended):</value>
</data>
<data name="error_powershell_scriptblock_option2_example" xml:space="preserve">
<value>FancyZonesCLI shk 1 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</value>
</data>
<!-- CLI Usage -->
<data name="usage_title" xml:space="preserve">
<value>FancyZones CLI - Command line interface for FancyZones</value>
</data>
<data name="usage_syntax" xml:space="preserve">
<value>Usage: FancyZonesCLI [command] [options]</value>
</data>
<data name="usage_options" xml:space="preserve">
<value>Options:</value>
</data>
<data name="usage_commands" xml:space="preserve">
<value>Commands:</value>
</data>
<data name="usage_examples" xml:space="preserve">
<value>Examples:</value>
</data>
<data name="usage_arguments" xml:space="preserve">
<value>Arguments:</value>
</data>
<data name="usage_aliases" xml:space="preserve">
<value>Aliases:</value>
</data>
<data name="usage_command" xml:space="preserve">
<value>Command:</value>
</data>
<data name="usage_optional" xml:space="preserve">
<value>(optional)</value>
</data>
<data name="usage_unknown_command" xml:space="preserve">
<value>Unknown command: {0}</value>
</data>
<data name="usage_run_help" xml:space="preserve">
<value>Run 'FancyZonesCLI --help' to see available commands.</value>
</data>
</root>

View File

@@ -1,52 +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;
using System.Diagnostics.CodeAnalysis;
#nullable enable
namespace FancyZonesCLI.Utils;
/// <summary>
/// Helper class for normalizing GUID strings to Windows format with braces.
/// Supports input with or without braces: both "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
/// and "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" are accepted.
/// </summary>
internal static class GuidHelper
{
/// <summary>
/// Normalizes a GUID string to Windows format with braces.
/// Returns null if the input is not a valid GUID.
/// </summary>
/// <param name="input">GUID string with or without braces.</param>
/// <returns>GUID in "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" format, or null if invalid.</returns>
public static string? NormalizeGuid(string? input)
{
if (string.IsNullOrWhiteSpace(input))
{
return null;
}
if (Guid.TryParse(input, out Guid guid))
{
// "B" format includes braces: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
return guid.ToString("B").ToUpperInvariant();
}
return null;
}
/// <summary>
/// Tries to normalize a GUID string to Windows format with braces.
/// </summary>
/// <param name="input">GUID string with or without braces.</param>
/// <param name="normalizedGuid">The normalized GUID string, or the original input if normalization fails.</param>
/// <returns>True if the input was successfully normalized; otherwise, false.</returns>
public static bool TryNormalizeGuid(string? input, [NotNullWhen(true)] out string? normalizedGuid)
{
normalizedGuid = NormalizeGuid(input);
return normalizedGuid != null;
}
}

View File

@@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using static FancyZonesEditorCommon.Data.CustomLayouts;
@@ -24,10 +23,8 @@ namespace FancyZonesEditorCommon.Data
{
public struct CanvasZoneWrapper
{
[JsonPropertyName("X")]
public int X { get; set; }
[JsonPropertyName("Y")]
public int Y { get; set; }
public int Width { get; set; }

View File

@@ -191,22 +191,27 @@ bool EditorParameters::Save(const WorkAreaConfiguration& configuration, OnThread
monitorJson.dpi = dpi;
// Get DPI-unaware values for dimensions (virtual coordinates for WPF sizing)
MONITORINFOEX monitorInfoUnaware{};
MONITORINFOEX monitorInfo{};
dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] {
monitorInfoUnaware.cbSize = sizeof(monitorInfoUnaware);
GetMonitorInfo(monitor, &monitorInfoUnaware);
monitorInfo.cbSize = sizeof(monitorInfo);
if (!GetMonitorInfo(monitor, &monitorInfo))
{
return;
}
} }).wait();
// Dimensions in virtual coordinates (from DPI-unaware thread)
monitorJson.monitorWidth = monitorInfoUnaware.rcMonitor.right - monitorInfoUnaware.rcMonitor.left;
monitorJson.monitorHeight = monitorInfoUnaware.rcMonitor.bottom - monitorInfoUnaware.rcMonitor.top;
monitorJson.workAreaWidth = monitorInfoUnaware.rcWork.right - monitorInfoUnaware.rcWork.left;
monitorJson.workAreaHeight = monitorInfoUnaware.rcWork.bottom - monitorInfoUnaware.rcWork.top;
float width = static_cast<float>(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left);
float height = static_cast<float>(monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top);
DPIAware::Convert(monitor, width, height);
// Position in virtual coordinates (matched by DPI-unaware context in WPF editor)
monitorJson.left = monitorInfoUnaware.rcWork.left;
monitorJson.top = monitorInfoUnaware.rcWork.top;
monitorJson.monitorWidth = static_cast<int>(std::roundf(width));
monitorJson.monitorHeight = static_cast<int>(std::roundf(height));
// use dpi-unaware values
monitorJson.top = monitorInfo.rcWork.top;
monitorJson.left = monitorInfo.rcWork.left;
monitorJson.workAreaWidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
monitorJson.workAreaHeight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
argsJson.monitors.emplace_back(std::move(monitorJson));
}

View File

@@ -67,18 +67,10 @@ namespace FancyZonesEditor.Models
Window.KeyUp += ((App)Application.Current).App_KeyUp;
Window.KeyDown += ((App)Application.Current).App_KeyDown;
// Store for DPI-unaware positioning
_virtualWorkArea = workArea;
// Set initial WPF properties
Window.Left = workArea.X;
Window.Top = workArea.Y;
Window.Width = workArea.Width;
Window.Height = workArea.Height;
// After HWND is created, reposition using DPI-unaware context
// This matches the C++ backend which uses a DPI-unaware thread
Window.SourceInitialized += OnWindowSourceInitialized;
}
public Monitor(string monitorName, string monitorInstanceId, string monitorSerialNumber, string virtualDesktop, int dpi, Rect workArea, Size monitorSize)
@@ -88,33 +80,16 @@ namespace FancyZonesEditor.Models
}
private LayoutSettings _settings;
private Rect _virtualWorkArea;
private void OnWindowSourceInitialized(object sender, EventArgs e)
{
// Reposition window using DPI-unaware context to match the virtual coordinates
// from the FancyZones C++ backend (which uses a DPI-unaware thread)
Utils.NativeMethods.SetWindowPositionDpiUnaware(
Window,
(int)_virtualWorkArea.X,
(int)_virtualWorkArea.Y,
(int)_virtualWorkArea.Width,
(int)_virtualWorkArea.Height);
}
public void Scale(double scaleFactor)
{
Device.Scale(scaleFactor);
_virtualWorkArea = Device.WorkAreaRect;
// Use DPI-unaware positioning
Utils.NativeMethods.SetWindowPositionDpiUnaware(
Window,
(int)_virtualWorkArea.X,
(int)_virtualWorkArea.Y,
(int)_virtualWorkArea.Width,
(int)_virtualWorkArea.Height);
var workArea = Device.WorkAreaRect;
Window.Left = workArea.X;
Window.Top = workArea.Y;
Window.Width = workArea.Width;
Window.Height = workArea.Height;
}
public void SetLayoutSettings(LayoutModel model)

View File

@@ -69,11 +69,7 @@ namespace FancyZonesEditor.Utils
}
else
{
// Convert virtual coordinates to physical resolution by applying DPI scale
double scale = DPI / 96.0;
int physicalWidth = (int)Math.Round(ScreenBoundsWidth * scale);
int physicalHeight = (int)Math.Round(ScreenBoundsHeight * scale);
return physicalWidth + " × " + physicalHeight;
return ScreenBoundsWidth + " × " + ScreenBoundsHeight;
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// 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.
@@ -17,48 +17,14 @@ namespace FancyZonesEditor.Utils
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
private static extern IntPtr SetThreadDpiAwarenessContext(IntPtr dpiContext);
private const int GWL_EX_STYLE = -20;
private const int WS_EX_APPWINDOW = 0x00040000;
private const int WS_EX_TOOLWINDOW = 0x00000080;
private const uint SWP_NOZORDER = 0x0004;
private const uint SWP_NOACTIVATE = 0x0010;
private static readonly IntPtr DPI_AWARENESS_CONTEXT_UNAWARE = new IntPtr(-1);
public static void SetWindowStyleToolWindow(Window hwnd)
{
var helper = new WindowInteropHelper(hwnd).Handle;
_ = SetWindowLong(helper, GWL_EX_STYLE, (GetWindowLong(helper, GWL_EX_STYLE) | WS_EX_TOOLWINDOW) & ~WS_EX_APPWINDOW);
}
/// <summary>
/// Positions a WPF window using DPI-unaware context to match the virtual coordinates
/// from the FancyZones C++ backend (which uses a DPI-unaware thread).
/// This fixes overlay positioning on mixed-DPI multi-monitor setups.
/// </summary>
public static void SetWindowPositionDpiUnaware(Window window, int x, int y, int width, int height)
{
var helper = new WindowInteropHelper(window).Handle;
if (helper != IntPtr.Zero)
{
// Temporarily switch to DPI-unaware context to position window.
// This matches how the C++ backend gets coordinates via dpiUnawareThread.
IntPtr oldContext = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
try
{
SetWindowPos(helper, IntPtr.Zero, x, y, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
}
finally
{
SetThreadDpiAwarenessContext(oldContext);
}
}
}
}
}

View File

@@ -31,7 +31,7 @@ internal static class Program
Console.InputEncoding = Encoding.Unicode;
// Initialize logger to file (same as other modules)
CliLogger.Initialize("\\Image Resizer\\CLI");
CliLogger.Initialize("\\ImageResizer\\Logs");
CliLogger.Info($"ImageResizerCLI started with {args.Length} argument(s)");
try

View File

@@ -126,10 +126,13 @@ namespace ImageResizer.Properties
h => ncc.CollectionChanged -= h,
() => settings.CustomSize = new CustomSize());
// Reset is used instead of Replace to avoid ArgumentOutOfRangeException
// when notifying changes for virtual items (CustomSize/AiSize) that exist
// outside the bounds of the underlying _sizes collection.
Assert.AreEqual(NotifyCollectionChangedAction.Reset, result.Arguments.Action);
Assert.AreEqual(NotifyCollectionChangedAction.Replace, result.Arguments.Action);
Assert.AreEqual(1, result.Arguments.NewItems.Count);
Assert.AreEqual(settings.CustomSize, result.Arguments.NewItems[0]);
Assert.AreEqual(0, result.Arguments.NewStartingIndex);
Assert.AreEqual(1, result.Arguments.OldItems.Count);
Assert.AreEqual(originalCustomSize, result.Arguments.OldItems[0]);
Assert.AreEqual(0, result.Arguments.OldStartingIndex);
}
[TestMethod]

View File

@@ -216,15 +216,27 @@ namespace ImageResizer.Properties
{
if (e.PropertyName == nameof(Models.CustomSize))
{
var oldCustomSize = _customSize;
_customSize = settings.CustomSize;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
OnCollectionChanged(
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Replace,
_customSize,
oldCustomSize,
_sizes.Count));
}
else if (e.PropertyName == nameof(Models.AiSize))
{
var oldAiSize = _aiSize;
_aiSize = settings.AiSize;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
OnCollectionChanged(
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Replace,
_aiSize,
oldAiSize,
_sizes.Count + 1));
}
else if (e.PropertyName == nameof(Sizes))
{

View File

@@ -388,13 +388,6 @@ namespace Peek.UI
IsErrorVisible = true;
}
public void ShowError(string message)
{
IsErrorVisible = false;
ErrorMessage = message;
IsErrorVisible = true;
}
private void NavigationThrottleTimer_Tick(object? sender, object e)
{
if (sender == null)

View File

@@ -50,8 +50,7 @@
Item="{x:Bind ViewModel.CurrentItem, Mode=OneWay}"
NumberOfFiles="{x:Bind ViewModel.DisplayItemCount, Mode=OneWay}"
PreviewSizeChanged="FilePreviewer_PreviewSizeChanged"
ScalingFactor="{x:Bind ViewModel.ScalingFactor, Mode=OneWay}"
Visibility="{x:Bind ContentVisibility(ViewModel.IsErrorVisible), Mode=OneWay}" />
ScalingFactor="{x:Bind ViewModel.ScalingFactor, Mode=OneWay}" />
<InfoBar
x:Name="ErrorInfoBar"
@@ -60,7 +59,6 @@
Grid.RowSpan="2"
Margin="4,0,4,6"
VerticalAlignment="Bottom"
Closed="ErrorInfoBar_Closed"
IsOpen="{x:Bind ViewModel.IsErrorVisible, Mode=TwoWay}"
Message="{x:Bind ViewModel.ErrorMessage, Mode=OneWay}"
Severity="Error" />

View File

@@ -14,7 +14,6 @@ using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Peek.Common.Constants;
using Peek.Common.Extensions;
using Peek.Common.Helpers;
using Peek.FilePreviewer.Models;
using Peek.FilePreviewer.Previewers;
using Peek.UI.Extensions;
@@ -196,20 +195,6 @@ namespace Peek.UI
bootTime.Start();
ViewModel.Initialize(selectedItem);
// If no files were found (e.g., in virtual folders like Home/Recent), show an error
if (ViewModel.CurrentItem == null)
{
Logger.LogInfo("Peek: No files found to preview, showing error.");
var errorMessage = ResourceLoaderInstance.ResourceLoader.GetString("NoFilesSelected");
ViewModel.ShowError(errorMessage);
// Still show the window so user can see the warning
this.Show();
WindowHelpers.BringToForeground(this.GetWindowHandle());
return;
}
ViewModel.ScalingFactor = this.GetMonitorScale();
this.Content.KeyUp += Content_KeyUp;
@@ -317,24 +302,5 @@ namespace Peek.UI
{
themeListener?.Dispose();
}
/// <summary>
/// Returns Visibility.Collapsed when error is showing, Visibility.Visible when not.
/// </summary>
public Visibility ContentVisibility(bool isErrorVisible)
{
return isErrorVisible ? Visibility.Collapsed : Visibility.Visible;
}
/// <summary>
/// Handle InfoBar closed - if there's no current item, close the window.
/// </summary>
private void ErrorInfoBar_Closed(InfoBar sender, InfoBarClosedEventArgs args)
{
if (ViewModel.CurrentItem == null)
{
Uninitialize();
}
}
}
}

View File

@@ -341,10 +341,6 @@
<value>No more files to preview.</value>
<comment>The message to show when there are no files remaining to preview.</comment>
</data>
<data name="NoFilesSelected" xml:space="preserve">
<value>No files selected or this folder is not supported for preview.</value>
<comment>Displayed when Peek is activated in a virtual folder (like Home or Recent) where file selection cannot be retrieved.</comment>
</data>
<data name="DeleteFileError_NotFound" xml:space="preserve">
<value>The file cannot be found. Please check if the file has been moved, renamed, or deleted.</value>
<comment>Displayed if the file or path was not found</comment>

View File

@@ -7,7 +7,6 @@
<TargetFramework>net9.0-windows10.0.26100.0</TargetFramework>
<RootNamespace>Microsoft.PowerToys.QuickAccess</RootNamespace>
<AssemblyName>PowerToys.QuickAccess</AssemblyName>
<ApplicationIcon>..\..\runner\svgs\icon.ico</ApplicationIcon>
<UseWinUI>true</UseWinUI>
<WindowsPackageType>None</WindowsPackageType>
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
@@ -51,8 +50,8 @@
</ItemGroup>
<ItemGroup>
<PRIResource Include="..\Settings.UI\Strings\**\Resources.resw">
<Link>Strings\%(RecursiveDir)Resources.resw</Link>
<PRIResource Include="..\Settings.UI\Strings\en-us\Resources.resw">
<Link>Strings\en-us\Resources.resw</Link>
</PRIResource>
</ItemGroup>

View File

@@ -10,7 +10,6 @@ using global::PowerToys.GPOWrapper;
using ManagedCommon;
using Microsoft.PowerToys.QuickAccess.Helpers;
using Microsoft.PowerToys.QuickAccess.Services;
using Microsoft.PowerToys.Settings.UI.Controls;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
@@ -27,7 +26,6 @@ public sealed class AllAppsViewModel : Observable
private readonly SettingsUtils _settingsUtils;
private readonly ResourceLoader _resourceLoader;
private readonly DispatcherQueue _dispatcherQueue;
private readonly List<FlyoutMenuItem> _allFlyoutMenuItems = new();
private GeneralSettings _generalSettings;
public ObservableCollection<FlyoutMenuItem> FlyoutMenuItems { get; }
@@ -60,28 +58,9 @@ public sealed class AllAppsViewModel : Observable
_resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
FlyoutMenuItems = new ObservableCollection<FlyoutMenuItem>();
BuildFlyoutMenuItems();
RefreshFlyoutMenuItems();
}
private void BuildFlyoutMenuItems()
{
_allFlyoutMenuItems.Clear();
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
{
if (moduleType == ModuleType.GeneralSettings)
{
continue;
}
_allFlyoutMenuItems.Add(new FlyoutMenuItem
{
Tag = moduleType,
EnabledChangedCallback = EnabledChangedOnUI,
});
}
}
private void OnSettingsChanged(GeneralSettings newSettings)
{
_dispatcherQueue.TryEnqueue(() =>
@@ -103,37 +82,63 @@ public sealed class AllAppsViewModel : Observable
private void RefreshFlyoutMenuItems()
{
foreach (var item in _allFlyoutMenuItems)
var desiredItems = new List<FlyoutMenuItem>();
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
{
var moduleType = item.Tag;
if (moduleType == ModuleType.GeneralSettings)
{
continue;
}
var gpo = Helpers.ModuleGpoHelper.GetModuleGpoConfiguration(moduleType);
var isLocked = gpo is GpoRuleConfigured.Enabled or GpoRuleConfigured.Disabled;
var isEnabled = gpo == GpoRuleConfigured.Enabled || (!isLocked && Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetIsModuleEnabled(_generalSettings, moduleType));
item.Label = _resourceLoader.GetString(Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleLabelResourceName(moduleType));
item.IsLocked = isLocked;
item.Icon = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleTypeFluentIconName(moduleType);
var existingItem = FlyoutMenuItems.FirstOrDefault(x => x.Tag == moduleType);
if (item.IsEnabled != isEnabled)
if (existingItem != null)
{
item.UpdateStatus(isEnabled);
existingItem.Label = _resourceLoader.GetString(Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleLabelResourceName(moduleType));
existingItem.IsLocked = isLocked;
existingItem.Icon = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleTypeFluentIconName(moduleType);
if (existingItem.IsEnabled != isEnabled)
{
var callback = existingItem.EnabledChangedCallback;
existingItem.EnabledChangedCallback = null;
existingItem.IsEnabled = isEnabled;
existingItem.EnabledChangedCallback = callback;
}
desiredItems.Add(existingItem);
}
else
{
desiredItems.Add(new FlyoutMenuItem
{
Label = _resourceLoader.GetString(Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleLabelResourceName(moduleType)),
IsEnabled = isEnabled,
IsLocked = isLocked,
Tag = moduleType,
Icon = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleTypeFluentIconName(moduleType),
EnabledChangedCallback = EnabledChangedOnUI,
});
}
}
var sortedItems = DashboardSortOrder switch
{
DashboardSortOrder.ByStatus => _allFlyoutMenuItems.OrderByDescending(x => x.IsEnabled).ThenBy(x => x.Label).ToList(),
_ => _allFlyoutMenuItems.OrderBy(x => x.Label).ToList(),
DashboardSortOrder.ByStatus => desiredItems.OrderByDescending(x => x.IsEnabled).ThenBy(x => x.Label).ToList(),
_ => desiredItems.OrderBy(x => x.Label).ToList(),
};
if (FlyoutMenuItems.Count == 0)
for (int i = FlyoutMenuItems.Count - 1; i >= 0; i--)
{
foreach (var item in sortedItems)
if (!sortedItems.Contains(FlyoutMenuItems[i]))
{
FlyoutMenuItems.Add(item);
FlyoutMenuItems.RemoveAt(i);
}
return;
}
for (int i = 0; i < sortedItems.Count; i++)
@@ -141,22 +146,22 @@ public sealed class AllAppsViewModel : Observable
var item = sortedItems[i];
var oldIndex = FlyoutMenuItems.IndexOf(item);
if (oldIndex != -1 && oldIndex != i)
if (oldIndex < 0)
{
FlyoutMenuItems.Insert(i, item);
}
else if (oldIndex != i)
{
FlyoutMenuItems.Move(oldIndex, i);
}
}
}
private void EnabledChangedOnUI(ModuleListItem item)
private void EnabledChangedOnUI(FlyoutMenuItem item)
{
var flyoutItem = (FlyoutMenuItem)item;
if (_coordinator.UpdateModuleEnabled(flyoutItem.Tag, flyoutItem.IsEnabled))
if (_coordinator.UpdateModuleEnabled(item.Tag, item.IsEnabled))
{
_coordinator.NotifyUserSettingsInteraction();
// Trigger re-sort immediately when status changes on UI
RefreshFlyoutMenuItems();
}
}

View File

@@ -22,6 +22,21 @@ public sealed class FlyoutMenuItem : ModuleListItem
set => base.Tag = value;
}
public override bool IsEnabled
{
get => base.IsEnabled;
set
{
if (base.IsEnabled != value)
{
base.IsEnabled = value;
EnabledChangedCallback?.Invoke(this);
}
}
}
public Action<FlyoutMenuItem>? EnabledChangedCallback { get; set; }
public bool Visible
{
get => _visible;

View File

@@ -2,7 +2,6 @@
// 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; // For Action
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
@@ -18,22 +17,6 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
private bool _isLocked;
private object? _tag;
private ICommand? _clickCommand;
private bool _isUpdating;
public Action<ModuleListItem>? EnabledChangedCallback { get; set; }
public void UpdateStatus(bool isEnabled)
{
_isUpdating = true;
try
{
IsEnabled = isEnabled;
}
finally
{
_isUpdating = false;
}
}
public virtual string Label
{
@@ -96,11 +79,6 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
{
_isEnabled = value;
OnPropertyChanged();
if (!_isUpdating)
{
EnabledChangedCallback?.Invoke(this);
}
}
}
}

View File

@@ -112,13 +112,6 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
eventHandle.Set();
}
return true;
case ModuleType.LightSwitch:
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.LightSwitchToggleEvent()))
{
eventHandle.Set();
}
return true;
default:
return false;

View File

@@ -64,7 +64,6 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
AddFlyoutMenuItem(ModuleType.EnvironmentVariables);
AddFlyoutMenuItem(ModuleType.FancyZones);
AddFlyoutMenuItem(ModuleType.Hosts);
AddFlyoutMenuItem(ModuleType.LightSwitch);
AddFlyoutMenuItem(ModuleType.PowerLauncher);
AddFlyoutMenuItem(ModuleType.PowerOCR);
AddFlyoutMenuItem(ModuleType.RegistryPreview);
@@ -121,7 +120,6 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
{
ModuleType.ColorPicker => SettingsRepository<ColorPickerSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ActivationShortcut.ToString(),
ModuleType.FancyZones => SettingsRepository<FancyZonesSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.FancyzonesEditorHotkey.Value.ToString(),
ModuleType.LightSwitch => SettingsRepository<LightSwitchSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ToggleThemeHotkey.Value.ToString(),
ModuleType.PowerLauncher => SettingsRepository<PowerLauncherSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.OpenPowerLauncher.ToString(),
ModuleType.PowerOCR => SettingsRepository<PowerOcrSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ActivationShortcut.ToString(),
ModuleType.Workspaces => SettingsRepository<WorkspacesSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.Hotkey.Value.ToString(),

View File

@@ -11,7 +11,7 @@ using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Library
namespace Settings.UI.Library
{
public class LightSwitchSettings : BasePTModuleSettings, ISettingsConfig, ICloneable, IHotkeyConfig
{

View File

@@ -56,7 +56,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonSerializable(typeof(HostsSettings))]
[JsonSerializable(typeof(ImageResizerSettings))]
[JsonSerializable(typeof(KeyboardManagerSettings))]
[JsonSerializable(typeof(LightSwitchSettings))]
[JsonSerializable(typeof(SettingsUILibrary.LightSwitchSettings))]
[JsonSerializable(typeof(MeasureToolSettings))]
[JsonSerializable(typeof(MouseHighlighterSettings))]
[JsonSerializable(typeof(MouseJumpSettings))]

View File

@@ -23,7 +23,7 @@ namespace Microsoft.PowerToys.Settings.UI.SerializationContext;
[JsonSerializable(typeof(FileLocksmithSettings))]
[JsonSerializable(typeof(FindMyMouseSettings))]
[JsonSerializable(typeof(IList<PowerToysReleaseInfo>))]
[JsonSerializable(typeof(LightSwitchSettings))]
[JsonSerializable(typeof(SettingsUILibrary.LightSwitchSettings))]
[JsonSerializable(typeof(MeasureToolSettings))]
[JsonSerializable(typeof(MouseHighlighterSettings))]
[JsonSerializable(typeof(MouseJumpSettings))]

View File

@@ -50,7 +50,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
var darkSettings = this.moduleSettingsRepository.SettingsConfig;
// Pass them into the ViewModel
this.ViewModel = new LightSwitchViewModel(this.generalSettingsRepository, darkSettings, ShellPage.SendDefaultIPCMessage);
this.ViewModel = new LightSwitchViewModel(darkSettings, this.sendConfigMsg);
this.ViewModel.PropertyChanged += ViewModel_PropertyChanged;
this.LoadSettings(this.generalSettingsRepository, this.moduleSettingsRepository);
@@ -185,7 +185,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
// need to save the values
this.ViewModel.Latitude = latitude.ToString(CultureInfo.InvariantCulture);
this.ViewModel.Longitude = longitude.ToString(CultureInfo.InvariantCulture);
this.ViewModel.SyncButtonInformation = $"{this.ViewModel.Latitude}°, {this.ViewModel.Longitude}°";
this.ViewModel.SyncButtonInformation = $"{this.ViewModel.Latitude}<EFBFBD>, {this.ViewModel.Longitude}<EFBFBD>";
var result = SunCalc.CalculateSunriseSunset(latitude, longitude, DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
@@ -293,7 +293,18 @@ namespace Microsoft.PowerToys.Settings.UI.Views
private void UpdateEnabledState(bool recommendedState)
{
ViewModel.RefreshEnabledState();
var enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredLightSwitchEnabledValue();
if (enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
{
// Get the enabled state from GPO.
this.ViewModel.IsEnabledGpoConfigured = true;
this.ViewModel.EnabledGPOConfiguration = enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
}
else
{
this.ViewModel.IsEnabled = recommendedState;
}
}
private async void SyncLocationButton_Click(object sender, RoutedEventArgs e)

View File

@@ -26,6 +26,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
set => base.Tag = value;
}
public Action<DashboardListItem> EnabledChangedCallback { get; set; }
public override bool IsEnabled
{
get => base.IsEnabled;
set
{
if (base.IsEnabled != value)
{
base.IsEnabled = value;
EnabledChangedCallback?.Invoke(this);
}
}
}
public bool Visible
{
get => _visible;

View File

@@ -50,6 +50,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
// Flag to prevent circular updates when a UI toggle triggers settings changes.
private bool _isUpdatingFromUI;
private bool _isUpdatingFromSettings;
private AllHotkeyConflictsData _allHotkeyConflictsData = new AllHotkeyConflictsData();
@@ -257,7 +258,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
// Only update if there's an actual change to minimize UI notifications.
if (item.IsEnabled != newEnabledState)
{
item.UpdateStatus(newEnabledState);
item.IsEnabled = newEnabledState;
}
if (item.IsLocked != newLockedState)
@@ -274,17 +275,19 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
/// Sets the _isUpdatingFromUI flag to prevent circular updates, then updates
/// settings, re-sorts if needed, and refreshes dependent collections.
/// </summary>
private void EnabledChangedOnUI(ModuleListItem item)
private void EnabledChangedOnUI(DashboardListItem dashboardListItem)
{
var dashboardListItem = (DashboardListItem)item;
var isEnabled = dashboardListItem.IsEnabled;
if (_isUpdatingFromSettings)
{
return;
}
_isUpdatingFromUI = true;
try
{
Views.ShellPage.UpdateGeneralSettingsCallback(dashboardListItem.Tag, isEnabled);
Views.ShellPage.UpdateGeneralSettingsCallback(dashboardListItem.Tag, dashboardListItem.IsEnabled);
if (dashboardListItem.Tag == ModuleType.NewPlus && isEnabled == true)
if (dashboardListItem.Tag == ModuleType.NewPlus && dashboardListItem.IsEnabled == true)
{
var settingsUtils = SettingsUtils.Default;
var settings = NewPlusViewModel.LoadSettings(settingsUtils);
@@ -322,6 +325,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
return;
}
_isUpdatingFromSettings = true;
try
{
RefreshModuleList();
@@ -336,6 +340,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
Logger.LogError($"Updating active/disabled modules list failed: {ex.Message}");
}
finally
{
_isUpdatingFromSettings = false;
}
}
/// <summary>

View File

@@ -14,10 +14,8 @@ using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.PowerToys.Settings.UI.SerializationContext;
using Newtonsoft.Json.Linq;
using PowerToys.GPOWrapper;
using Settings.UI.Library;
using Settings.UI.Library.Helpers;
@@ -29,16 +27,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private Func<string, int> SendConfigMSG { get; }
private GeneralSettings GeneralSettingsConfig { get; set; }
public ObservableCollection<SearchLocation> SearchLocations { get; } = new();
public LightSwitchViewModel(ISettingsRepository<GeneralSettings> settingsRepository, LightSwitchSettings initialSettings = null, Func<string, int> ipcMSGCallBackFunc = null)
public LightSwitchViewModel(LightSwitchSettings initialSettings = null, Func<string, int> ipcMSGCallBackFunc = null)
{
ArgumentNullException.ThrowIfNull(settingsRepository);
GeneralSettingsConfig = settingsRepository.SettingsConfig;
InitializeEnabledValue();
_moduleSettings = initialSettings ?? new LightSwitchSettings();
SendConfigMSG = ipcMSGCallBackFunc;
@@ -66,21 +58,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
return hotkeysDict;
}
private void InitializeEnabledValue()
{
_enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredLightSwitchEnabledValue();
if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
{
// Get the enabled state from GPO.
_enabledStateIsGPOConfigured = true;
_isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
}
else
{
_isEnabled = GeneralSettingsConfig.Enabled.LightSwitch;
}
}
private void ForceLightNow()
{
Logger.LogInfo("Sending custom action: forceLight");
@@ -116,26 +93,33 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
public bool IsEnabled
{
get => _isEnabled;
set
get
{
if (_enabledStateIsGPOConfigured)
{
// If it's GPO configured, shouldn't be able to change this state.
return;
return _enabledGPOConfiguration;
}
if (value != _isEnabled)
else
{
return _isEnabled;
}
}
set
{
if (_isEnabled != value)
{
if (_enabledStateIsGPOConfigured)
{
// If it's GPO configured, shouldn't be able to change this state.
return;
}
_isEnabled = value;
// Set the status in the general settings configuration
GeneralSettingsConfig.Enabled.LightSwitch = value;
OutGoingGeneralSettings snd = new OutGoingGeneralSettings(GeneralSettingsConfig);
RefreshEnabledState();
SendConfigMSG(snd.ToString());
OnPropertyChanged(nameof(IsEnabled));
NotifyPropertyChanged();
}
}
}
@@ -143,16 +127,24 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
public bool IsEnabledGpoConfigured
{
get => _enabledStateIsGPOConfigured;
}
public GpoRuleConfigured EnabledGPOConfiguration
{
get => _enabledGpoRuleConfiguration;
set
{
if (_enabledGpoRuleConfiguration != value)
if (_enabledStateIsGPOConfigured != value)
{
_enabledGpoRuleConfiguration = value;
_enabledStateIsGPOConfigured = value;
NotifyPropertyChanged();
}
}
}
public bool EnabledGPOConfiguration
{
get => _enabledGPOConfiguration;
set
{
if (_enabledGPOConfiguration != value)
{
_enabledGPOConfiguration = value;
NotifyPropertyChanged();
}
}
@@ -583,7 +575,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
private bool _enabledStateIsGPOConfigured;
private GpoRuleConfigured _enabledGpoRuleConfiguration;
private bool _enabledGPOConfiguration;
private LightSwitchSettings _moduleSettings;
private bool _isEnabled;
private HotkeySettings _toggleThemeHotkey;