mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-13 15:56:41 +01:00
Compare commits
4 Commits
leilzh/dev
...
shawn/impr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca4b52e8c2 | ||
|
|
be06f081d1 | ||
|
|
7cb41d8fe3 | ||
|
|
73dda5a08d |
50
.github/prompts/create-commit-title.prompt.md
vendored
50
.github/prompts/create-commit-title.prompt.md
vendored
@@ -6,45 +6,13 @@ description: 'Generate an 80-character git commit title for the local diff'
|
||||
|
||||
# Generate Commit Title
|
||||
|
||||
## Purpose
|
||||
Provide a single-line, ready-to-paste git commit title (<= 80 characters) that reflects the most important local changes since `HEAD`.
|
||||
**Goal:** Provide a ready-to-paste git commit title (<= 80 characters) that captures the most important local changes since `HEAD`.
|
||||
|
||||
## Input to collect
|
||||
- Run exactly one command to view the local diff:
|
||||
```@terminal
|
||||
git diff HEAD
|
||||
```
|
||||
|
||||
## How to decide the title
|
||||
1. From the diff, find the dominant area (e.g., `src/modules/*`, `doc/devdocs/**`) and the change type (bug fix, docs update, config tweak).
|
||||
2. Draft an imperative, plain-ASCII title that:
|
||||
- Mentions the primary component when obvious (e.g., `FancyZones:` or `Docs:`)
|
||||
- Stays within 80 characters and has no trailing punctuation
|
||||
|
||||
## Final output
|
||||
- Reply with only the commit title on a single line—no extra text.
|
||||
|
||||
## PR title convention (when asked)
|
||||
Use Conventional Commits style:
|
||||
|
||||
`<type>(<scope>): <summary>`
|
||||
|
||||
**Allowed types**
|
||||
- feat, fix, docs, refactor, perf, test, build, ci, chore
|
||||
|
||||
**Scope rules**
|
||||
- Use a short, PowerToys-focused scope (one word preferred). Common scopes:
|
||||
- Core: `runner`, `settings-ui`, `common`, `docs`, `build`, `ci`, `installer`, `gpo`, `dsc`
|
||||
- Modules: `fancyzones`, `powerrename`, `awake`, `colorpicker`, `imageresizer`, `keyboardmanager`, `mouseutils`, `peek`, `hosts`, `file-locksmith`, `screen-ruler`, `text-extractor`, `cropandlock`, `paste`, `powerlauncher`
|
||||
- If unclear, pick the closest module or subsystem; omit only if unavoidable
|
||||
|
||||
**Summary rules**
|
||||
- Imperative, present tense (“add”, “update”, “remove”, “fix”)
|
||||
- Keep it <= 72 characters when possible; be specific, avoid “misc changes”
|
||||
|
||||
**Examples**
|
||||
- `feat(fancyzones): add canvas template duplication`
|
||||
- `fix(mouseutils): guard crosshair toggle when dpi info missing`
|
||||
- `docs(runner): document tray icon states`
|
||||
- `build(installer): align wix v5 suffix flag`
|
||||
- `ci(ci): cache pipeline artifacts for x64`
|
||||
**Workflow:**
|
||||
1. Run a single command to view the local diff since the last commit:
|
||||
```@terminal
|
||||
git diff HEAD
|
||||
```
|
||||
2. From that diff, identify the dominant area (reference key paths like `src/modules/*`, `doc/devdocs/**`, etc.), the type of change (bug fix, docs update, config tweak), and any notable impact.
|
||||
3. Draft a concise, imperative commit title summarizing the dominant change. Keep it plain ASCII, <= 80 characters, and avoid trailing punctuation. Mention the primary component when obvious (for example `FancyZones:` or `Docs:`).
|
||||
4. Respond with only the final commit title on a single line so it can be pasted directly into `git commit`.
|
||||
|
||||
1
.github/prompts/create-pr-summary.prompt.md
vendored
1
.github/prompts/create-pr-summary.prompt.md
vendored
@@ -22,4 +22,3 @@ description: 'Generate a PowerToys-ready pull request description from the local
|
||||
5. Confirm validation: list tests executed with results or state why tests were skipped in line with repo guidance.
|
||||
6. Load `.github/pull_request_template.md`, mirror its section order, and populate it with the gathered facts. Include only relevant checklist entries, marking them `[x]/[ ]` and noting any intentional omissions as "N/A".
|
||||
7. Present the filled template inside a fenced ```markdown code block with no extra commentary so it is ready to paste into a PR, clearly flagging any placeholders that still need user input.
|
||||
8. Prepend the PR title above the filled template, applying the Conventional Commit type/scope rules from `.github/prompts/create-commit-title.prompt.md`; pick the dominant component from the diff and keep the title concise and imperative.
|
||||
|
||||
9
.github/prompts/fix-spelling.prompt.md
vendored
9
.github/prompts/fix-spelling.prompt.md
vendored
@@ -10,8 +10,8 @@ description: 'Resolve Code scanning / check-spelling comments on the active PR'
|
||||
|
||||
**Guardrails:**
|
||||
- Update only discussion threads authored by `github-actions` or `github-actions[bot]` that mention `Code scanning results / check-spelling`.
|
||||
- Prefer improving the wording in the originally flagged file when it clarifies intent without changing meaning; if the wording is already clear/standard for the context, handle it via `.github/actions/spell-check/expect.txt` and reuse existing entries.
|
||||
- Limit edits to the flagged text and `.github/actions/spell-check/expect.txt`; leave all other files and topics untouched.
|
||||
- Resolve findings solely by editing `.github/actions/spell-check/expect.txt`; reuse existing entries.
|
||||
- Leave all other files and topics untouched.
|
||||
|
||||
**Prerequisites:**
|
||||
- Install GitHub CLI if it is not present: `winget install GitHub.cli`.
|
||||
@@ -20,6 +20,5 @@ description: 'Resolve Code scanning / check-spelling comments on the active PR'
|
||||
**Workflow:**
|
||||
1. Determine the active pull request with a single `gh pr view --json number` call (default to the current branch).
|
||||
2. Fetch all PR discussion data once via `gh pr view --json comments,reviews` and filter to check-spelling comments authored by `github-actions` or `github-actions[bot]` that are not minimized; when several remain, process only the most recent comment body.
|
||||
3. For each flagged token, first consider tightening or rephrasing the original text to avoid the false positive while keeping the meaning intact; if the existing wording is already normal and professional for the context, proceed to allowlisting instead of changing it.
|
||||
4. When allowlisting, review `.github/actions/spell-check/expect.txt` for an equivalent term (for example an existing lowercase variant); when found, reuse that normalized term rather than adding a new entry, even if the flagged token differs only by casing. Only add a new entry after confirming no equivalent already exists.
|
||||
5. Add any remaining missing token to `.github/actions/spell-check/expect.txt`, keeping surrounding formatting intact.
|
||||
3. For each flagged token, review `.github/actions/spell-check/expect.txt` for an equivalent term (for example an existing lowercase variant); when found, reuse that normalized term rather than adding a new entry, even if the flagged token differs only by casing. Only add a new entry after confirming no equivalent already exists.
|
||||
4. Add any remaining missing token to `.github/actions/spell-check/expect.txt`, keeping surrounding formatting intact.
|
||||
@@ -10,7 +10,7 @@ parameters:
|
||||
default: {}
|
||||
|
||||
steps:
|
||||
- task: EsrpCodeSigning@6
|
||||
- task: EsrpCodeSigning@5
|
||||
displayName: 🔏 ${{ parameters.displayName }}
|
||||
inputs:
|
||||
ConnectedServiceName: ${{ parameters.signingIdentity.serviceName }}
|
||||
|
||||
17
.vscode/settings.json
vendored
17
.vscode/settings.json
vendored
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"github.copilot.chat.reviewSelection.instructions": [
|
||||
{
|
||||
"file": ".github/prompts/review-pr.prompt.md"
|
||||
}
|
||||
],
|
||||
"github.copilot.chat.commitMessageGeneration.instructions": [
|
||||
{
|
||||
"file": ".github/prompts/create-commit-title.prompt.md"
|
||||
}
|
||||
],
|
||||
"github.copilot.chat.pullRequestDescriptionGeneration.instructions": [
|
||||
{
|
||||
"file": ".github/prompts/create-pr-summary.prompt.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -66,10 +66,5 @@ namespace PowerToys.GPOWrapperProjection
|
||||
{
|
||||
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredWorkspacesEnabledValue();
|
||||
}
|
||||
|
||||
public static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue()
|
||||
{
|
||||
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredLightSwitchEnabledValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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="" />
|
||||
<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=""
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
@@ -523,8 +535,8 @@
|
||||
</Button>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</ContentDialog>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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() =>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)],
|
||||
};
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)),
|
||||
};
|
||||
|
||||
|
||||
@@ -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}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
],
|
||||
|
||||
@@ -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)],
|
||||
};
|
||||
|
||||
|
||||
@@ -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),
|
||||
],
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -154,9 +154,6 @@ public sealed class AllAppsViewModel : Observable
|
||||
if (_coordinator.UpdateModuleEnabled(flyoutItem.Tag, flyoutItem.IsEnabled))
|
||||
{
|
||||
_coordinator.NotifyUserSettingsInteraction();
|
||||
|
||||
// Trigger re-sort immediately when status changes on UI
|
||||
RefreshFlyoutMenuItems();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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))]
|
||||
|
||||
@@ -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))]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user